From arnd at arndb.de  Fri Jan  1 04:55:42 2016
From: arnd at arndb.de (Arnd Bergmann)
Date: Fri, 01 Jan 2016 13:55:42 +0100
Subject: [RESEND PATCH v1 3/4] net: ethernet: arc: Add support emac for
 RK3036
In-Reply-To: <FFCE9129-357E-4643-A350-9927A5AD5720@gmail.com>
References: <1451287341-16453-1-git-send-email-zhengxing@rock-chips.com>
 <1451287341-16453-4-git-send-email-zhengxing@rock-chips.com>
 <FFCE9129-357E-4643-A350-9927A5AD5720@gmail.com>
Message-ID: <3169681.aSJgN2TRic@wuerfel>

On Tuesday 29 December 2015 14:59:59 Florian Fainelli wrote:
> On December 27, 2015 11:22:20 PM PST, Xing Zheng <zhengxing at rock-chips.com> wrote:
> >The RK3036's GRFs offset are different with RK3066/RK3188, and need to
> >set
> >mac TX/RX clock before probe emac.
> >
> >Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
> >---
> <snip>
> > };
> > 
> > static const struct of_device_id emac_rockchip_dt_ids[] = {
> >-      { .compatible = "rockchip,rk3066-emac", .data =
> >&emac_rockchip_dt_data[0] },
> >-      { .compatible = "rockchip,rk3188-emac", .data =
> >&emac_rockchip_dt_data[1] },
> >+      { .compatible = "rockchip,rk3036-emac", .data =
> >&emac_rockchip_dt_data[0] },
> >+      { .compatible = "rockchip,rk3066-emac", .data =
> >&emac_rockchip_dt_data[1] },
> >+      { .compatible = "rockchip,rk3188-emac", .data =
> >&emac_rockchip_dt_data[2] },
> >       { /* Sentinel */ }
> 
> Food for thought, you might want to use an enum here to index emac_rockchip_dt_data which would be less error prone if you add/remove entries in this structure.
> 

I would use named structures instead:

static const struct emac_rockchip_soc_data emac_rk3066_emac_data = {
	.grf_offset = 0x154,
};

static const struct of_device_id emac_rockchip_dt_ids[] = {
	{ .compatible = "rockchip,rk3066-emac", .data = &emac_rk3066_emac_data,
	...
};

	Armd


From arnd at arndb.de  Fri Jan  1 05:42:52 2016
From: arnd at arndb.de (Arnd Bergmann)
Date: Fri, 01 Jan 2016 14:42:52 +0100
Subject: [PATCH] [BUG] clk: rockchip: don't mark clock names as initconst
Message-ID: <2791943.MBZ53mI7Gp@wuerfel>

The latest changes to the rockchip clk implementation cause
tons of warnings and/or errors (depending on the configuration):

WARNING: vmlinux.o(.data+0x125394): Section mismatch in reference from the variable __compound_literal.125 to the (unknown reference) .init.rodata:(unknown)

The reason is a compount literal in a macro that refers to an
object in the .init.rodata section:

 #define COMPOSITE_FRACMUX(_id, cname, pname, f, mo, df, go, gs, gf, ch) \
        {                                                       \
		...
                .child          = &(struct rockchip_clk_branch)ch, \
        }

'ch' here is placed in .data, and I could find no way to override that
using an attribute or pragma because there is no identifier associated
with it.

This patch avoids the warning by placing the names in .rodata rather than
.init.rodata. However, it is not an ideal solution because we really
want all those data structures to be discarded along with the initdata.

Signed-off-by: Arnd Bergmann <arnd at arndb.de>
Fixes: 8ca1ca8f6039 ("clk: rockchip: handle mux dependency of fractional dividers")

diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
index 3f71ee5d1bc9..5cadf5e3fb16 100644
--- a/drivers/clk/rockchip/clk.h
+++ b/drivers/clk/rockchip/clk.h
@@ -244,7 +244,7 @@ struct clk *rockchip_clk_register_inverter(const char *name,
 				void __iomem *reg, int shift, int flags,
 				spinlock_t *lock);
 
-#define PNAME(x) static const char *const x[] __initconst
+#define PNAME(x) static const char *const x[]
 
 enum rockchip_clk_branch_type {
 	branch_composite,



From heiko at sntech.de  Fri Jan  1 09:06:30 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Fri, 01 Jan 2016 18:06:30 +0100
Subject: [PATCH] [BUG] clk: rockchip: don't mark clock names as initconst
In-Reply-To: <2791943.MBZ53mI7Gp@wuerfel>
References: <2791943.MBZ53mI7Gp@wuerfel>
Message-ID: <1799610.nrn5HMCSxL@diego>

Hi Arnd,

Am Freitag, 1. Januar 2016, 14:42:52 schrieb Arnd Bergmann:
> The latest changes to the rockchip clk implementation cause
> tons of warnings and/or errors (depending on the configuration):
> 
> WARNING: vmlinux.o(.data+0x125394): Section mismatch in reference from the
> variable __compound_literal.125 to the (unknown reference)
> .init.rodata:(unknown)
> 
> The reason is a compount literal in a macro that refers to an
> object in the .init.rodata section:
> 
>  #define COMPOSITE_FRACMUX(_id, cname, pname, f, mo, df, go, gs, gf, ch) \
>         {                                                       \
> 		...
>                 .child          = &(struct rockchip_clk_branch)ch, \
>         }
> 
> 'ch' here is placed in .data, and I could find no way to override that
> using an attribute or pragma because there is no identifier associated
> with it.

"[PATCH] clk: rockchip: fix section mismatches with new child-clocks" [0]

should be in Mike's + Stephen's inbox since last week as well, which moves the 
offending new elements into separate entities, which can have __initdata 
attributes again.


Heiko

[0] http://www.spinics.net/lists/arm-kernel/msg471295.html




From arnd at arndb.de  Fri Jan  1 13:50:43 2016
From: arnd at arndb.de (Arnd Bergmann)
Date: Fri, 01 Jan 2016 22:50:43 +0100
Subject: [PATCH] [BUG] clk: rockchip: don't mark clock names as initconst
In-Reply-To: <1799610.nrn5HMCSxL@diego>
References: <2791943.MBZ53mI7Gp@wuerfel> <1799610.nrn5HMCSxL@diego>
Message-ID: <6692523.e5K11FUKe0@wuerfel>

On Friday 01 January 2016 18:06:30 Heiko St?bner wrote:
> 
> "[PATCH] clk: rockchip: fix section mismatches with new child-clocks" [0]
> 
> should be in Mike's + Stephen's inbox since last week as well, which moves the 
> offending new elements into separate entities, which can have __initdata 
> attributes again.
> 
> 
> Heiko
> 
> [0] http://www.spinics.net/lists/arm-kernel/msg471295.html

The patch looks good, but for some reason, the next-20151223 kernel had no
problem and next-20151231 was broken, the top commits in drivers/clk/rockchips
are:

commit a915e30dd26ea5f3cc2e2c044aba38ee5973d3fa
Merge: ce6dd266d535 b0158bb27c7b
Author: Michael Turquette <mturquette at baylibre.com>
Date:   Wed Dec 23 13:08:56 2015 -0800

    Merge branch 'clk-rockchip' into clk-next

commit b0158bb27c7b6e9843f541c17b24dbd964b76db6
Author: Xing Zheng <zhengxing at rock-chips.com>
Date:   Tue Dec 22 22:28:01 2015 +0100

    clk: rockchip: rk3036: include downstream muxes into fractional dividers
    
    Use the newly introduced possibility to combine the fractional dividers


	Arnd


From heiko at sntech.de  Fri Jan  1 14:05:10 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Fri, 01 Jan 2016 23:05:10 +0100
Subject: [PATCH] [BUG] clk: rockchip: don't mark clock names as initconst
In-Reply-To: <6692523.e5K11FUKe0@wuerfel>
References: <2791943.MBZ53mI7Gp@wuerfel> <1799610.nrn5HMCSxL@diego>
 <6692523.e5K11FUKe0@wuerfel>
Message-ID: <3926467.jaQRQH5bEO@diego>

Am Freitag, 1. Januar 2016, 22:50:43 schrieb Arnd Bergmann:
> On Friday 01 January 2016 18:06:30 Heiko St?bner wrote:
> > "[PATCH] clk: rockchip: fix section mismatches with new child-clocks" [0]
> > 
> > should be in Mike's + Stephen's inbox since last week as well, which moves
> > the offending new elements into separate entities, which can have
> > __initdata attributes again.
> > 
> > 
> > Heiko
> > 
> > [0] http://www.spinics.net/lists/arm-kernel/msg471295.html
> 
> The patch looks good, but for some reason, the next-20151223 kernel had no
> problem and next-20151231 was broken, the top commits in
> drivers/clk/rockchips are:

That is correct. next-20151223 did not contain the offending patches yet. 
After the patches got merged into the clock-tree the kbuild-robot alerted us 
to the __initdata issue, so I created the linked patch as fixup.


Heiko

> 
> commit a915e30dd26ea5f3cc2e2c044aba38ee5973d3fa
> Merge: ce6dd266d535 b0158bb27c7b
> Author: Michael Turquette <mturquette at baylibre.com>
> Date:   Wed Dec 23 13:08:56 2015 -0800
> 
>     Merge branch 'clk-rockchip' into clk-next
> 
> commit b0158bb27c7b6e9843f541c17b24dbd964b76db6
> Author: Xing Zheng <zhengxing at rock-chips.com>
> Date:   Tue Dec 22 22:28:01 2015 +0100
> 
>     clk: rockchip: rk3036: include downstream muxes into fractional dividers
> 
>     Use the newly introduced possibility to combine the fractional dividers
> 
> 
> 	Arnd



From heiko at sntech.de  Fri Jan  1 14:10:54 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Fri, 01 Jan 2016 23:10:54 +0100
Subject: [RESEND PATCH v1 4/4] clk: rockchip: rk3036: fix and add node id
 for emac clock
In-Reply-To: <5681F121.3070307@rock-chips.com>
References: <1451293433-32392-1-git-send-email-zhengxing@rock-chips.com>
 <5681E8E4.2010303@rock-chips.com> <5681F121.3070307@rock-chips.com>
Message-ID: <1562268.fb58QJ4jV7@diego>

Hi Xing,

Am Dienstag, 29. Dezember 2015, 10:34:09 schrieb Xing Zheng:
> On 2015?12?29? 09:59, Yakir Yang wrote:
> > On 12/28/2015 08:41 PM, Heiko St?bner wrote:
> >> Am Montag, 28. Dezember 2015, 17:03:53 schrieb Xing Zheng:
> >>> Due to referred old version TRM, there is incorrect emac clock node,
> >>> we should fix it. The SEL_21_9 is the parent of SEL_21_4.
> >>> 
> >>> In the emac driver, we need to refer HCLK_MAC, and because There are
> >>> only 3PLLs (APLL/GPLL/DPLL) on the rk3036, most clock are under the
> >>> GPLL, and it is unable to provide the accurate rate for mac_ref which
> >>> need to 50MHz probability, we should let it under the APLL and are
> >>> able to set the freq which integer multiples of 50MHz, so we add these
> >>> emac node for reference.
> >> 
> >> I don't really follow here. While I do understand that the emac needs
> >> 50MHz, I
> >> don't think using the APLL as source is helpful.
> >> 
> >> The APLL is the main clocksource for the cpu-cores, including frequency
> >> scaling, and while it currently only lists 816MHz as sole frequency,
> >> you're
> >> pretty much guaranteed to not get your correct multiple of 50MHz from
> >> there
> >> either. And limiting the cpu to just do 600MHz to get the mac working
> >> sounds
> >> pretty bad ;-) .
> >> 
> >> 
> >> In the rk3036 cru-node the gpll gets set to 594MHz. Is there a
> >> special reason
> >> why it needs to be 594MHz and cannot be a round 600MHz? Because that
> >> would
> >> also provide your 50MHz-multiple nicely.
> > 
> > Yes, this magic 594MHz would help to support the standard HDMI
> > resolutions, here are the math:
> > 
> > 1920x1080-60Hz DCLK = 148.5MHz = 594MHz / 4
> > 1280x720-60Hz DCLK = 74.25MHz = 594MHz / 8
> > 720x480-60Hz DCLK = 27MHz = 594MHz / 22
> > 
> > Thanks,
> > - Yakir
> 
> Thanks Yakir.
> 
> Hi Heiko,
>  From the above, do you have better idea for the RK3036's emac without
> ext-oscillator?

During the last days I did play a bit with the clock framework. As I don't 
have a Kylin (or any rk3036) board, I did build a test-case with pclk_cpu on 
the rk3188 (which can be affected by the armclk if not reparented to the 
gpll), which got sucessfully adapted to get back to (or near) the originally 
requested frequency.

So ideally you could roll back your mux/div split here and try the attached 
diff. In theory it should help :-) .
As can be seen by the FIXMEs, not fully finished, but I'd like to determine if 
it fixes the issue at least.


Heiko
-------------- next part --------------
A non-text attachment was scrubbed...
Name: clk-keep-req-rate.diff
Type: text/x-patch
Size: 2997 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160101/1db1687e/attachment.bin>

From zhengxing at rock-chips.com  Fri Jan  1 18:34:46 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Sat, 2 Jan 2016 10:34:46 +0800
Subject: [RESEND PATCH v1 4/4] clk: rockchip: rk3036: fix and add node id
 for emac clock
In-Reply-To: <1562268.fb58QJ4jV7@diego>
References: <1451293433-32392-1-git-send-email-zhengxing@rock-chips.com>
 <5681E8E4.2010303@rock-chips.com> <5681F121.3070307@rock-chips.com>
 <1562268.fb58QJ4jV7@diego>
Message-ID: <01110793-A349-4006-82A2-4489CE832B19@rock-chips.com>

Hi Heiko,
    Thank you for your patch, I will apply and test it later.

Thanks.

> ? 2016?1?2??06:10?Heiko St?bner <heiko at sntech.de> ???
> 
> Hi Xing,
> 
> Am Dienstag, 29. Dezember 2015, 10:34:09 schrieb Xing Zheng:
>> On 2015?12?29? 09:59, Yakir Yang wrote:
>>> On 12/28/2015 08:41 PM, Heiko St?bner wrote:
>>>> Am Montag, 28. Dezember 2015, 17:03:53 schrieb Xing Zheng:
>>>>> Due to referred old version TRM, there is incorrect emac clock node,
>>>>> we should fix it. The SEL_21_9 is the parent of SEL_21_4.
>>>>> 
>>>>> In the emac driver, we need to refer HCLK_MAC, and because There are
>>>>> only 3PLLs (APLL/GPLL/DPLL) on the rk3036, most clock are under the
>>>>> GPLL, and it is unable to provide the accurate rate for mac_ref which
>>>>> need to 50MHz probability, we should let it under the APLL and are
>>>>> able to set the freq which integer multiples of 50MHz, so we add these
>>>>> emac node for reference.
>>>> 
>>>> I don't really follow here. While I do understand that the emac needs
>>>> 50MHz, I
>>>> don't think using the APLL as source is helpful.
>>>> 
>>>> The APLL is the main clocksource for the cpu-cores, including frequency
>>>> scaling, and while it currently only lists 816MHz as sole frequency,
>>>> you're
>>>> pretty much guaranteed to not get your correct multiple of 50MHz from
>>>> there
>>>> either. And limiting the cpu to just do 600MHz to get the mac working
>>>> sounds
>>>> pretty bad ;-) .
>>>> 
>>>> 
>>>> In the rk3036 cru-node the gpll gets set to 594MHz. Is there a
>>>> special reason
>>>> why it needs to be 594MHz and cannot be a round 600MHz? Because that
>>>> would
>>>> also provide your 50MHz-multiple nicely.
>>> 
>>> Yes, this magic 594MHz would help to support the standard HDMI
>>> resolutions, here are the math:
>>> 
>>> 1920x1080-60Hz DCLK = 148.5MHz = 594MHz / 4
>>> 1280x720-60Hz DCLK = 74.25MHz = 594MHz / 8
>>> 720x480-60Hz DCLK = 27MHz = 594MHz / 22
>>> 
>>> Thanks,
>>> - Yakir
>> 
>> Thanks Yakir.
>> 
>> Hi Heiko,
>> From the above, do you have better idea for the RK3036's emac without
>> ext-oscillator?
> 
> During the last days I did play a bit with the clock framework. As I don't 
> have a Kylin (or any rk3036) board, I did build a test-case with pclk_cpu on 
> the rk3188 (which can be affected by the armclk if not reparented to the 
> gpll), which got sucessfully adapted to get back to (or near) the originally 
> requested frequency.
> 
> So ideally you could roll back your mux/div split here and try the attached 
> diff. In theory it should help :-) .
> As can be seen by the FIXMEs, not fully finished, but I'd like to determine if 
> it fixes the issue at least.
> 
> 
> Heiko
> <clk-keep-req-rate.diff>



From zhengxing at rock-chips.com  Fri Jan  1 18:38:28 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Sat, 2 Jan 2016 10:38:28 +0800
Subject: [RESEND PATCH v1 3/4] net: ethernet: arc: Add support emac for
 RK3036
In-Reply-To: <3169681.aSJgN2TRic@wuerfel>
References: <1451287341-16453-1-git-send-email-zhengxing@rock-chips.com>
 <1451287341-16453-4-git-send-email-zhengxing@rock-chips.com>
 <FFCE9129-357E-4643-A350-9927A5AD5720@gmail.com> <3169681.aSJgN2TRic@wuerfel>
Message-ID: <6ADB598D-07E0-4E51-A9FE-25B47FD87F7E@rock-chips.com>



OK, I think I will use named structures.

Thanks.

> ? 2016?1?1??20:55?Arnd Bergmann <arnd at arndb.de> ???
> 
>> On Tuesday 29 December 2015 14:59:59 Florian Fainelli wrote:
>>> On December 27, 2015 11:22:20 PM PST, Xing Zheng <zhengxing at rock-chips.com> wrote:
>>> The RK3036's GRFs offset are different with RK3066/RK3188, and need to
>>> set
>>> mac TX/RX clock before probe emac.
>>> 
>>> Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
>>> ---
>> <snip>
>>> };
>>> 
>>> static const struct of_device_id emac_rockchip_dt_ids[] = {
>>> -      { .compatible = "rockchip,rk3066-emac", .data =
>>> &emac_rockchip_dt_data[0] },
>>> -      { .compatible = "rockchip,rk3188-emac", .data =
>>> &emac_rockchip_dt_data[1] },
>>> +      { .compatible = "rockchip,rk3036-emac", .data =
>>> &emac_rockchip_dt_data[0] },
>>> +      { .compatible = "rockchip,rk3066-emac", .data =
>>> &emac_rockchip_dt_data[1] },
>>> +      { .compatible = "rockchip,rk3188-emac", .data =
>>> &emac_rockchip_dt_data[2] },
>>>      { /* Sentinel */ }
>> 
>> Food for thought, you might want to use an enum here to index emac_rockchip_dt_data which would be less error prone if you add/remove entries in this structure.
> 
> I would use named structures instead:
> 
> static const struct emac_rockchip_soc_data emac_rk3066_emac_data = {
>    .grf_offset = 0x154,
> };
> 
> static const struct of_device_id emac_rockchip_dt_ids[] = {
>    { .compatible = "rockchip,rk3066-emac", .data = &emac_rk3066_emac_data,
>    ...
> };
> 
>    Armd
> 
> 



From f.fainelli at gmail.com  Fri Jan  1 21:51:17 2016
From: f.fainelli at gmail.com (Florian Fainelli)
Date: Fri, 01 Jan 2016 21:51:17 -0800
Subject: [PATCH] stmmac: Don't exit mdio registration when mdio subnode is
 not found in the DTS
In-Reply-To: <1451397935-23643-1-git-send-email-romain.perier@gmail.com>
References: <1451397935-23643-1-git-send-email-romain.perier@gmail.com>
Message-ID: <0E6F98A1-B9F1-48B0-B5CA-A215803E827F@gmail.com>

On December 29, 2015 6:05:35 AM PST, Romain Perier <romain.perier at gmail.com> wrote:
>Originally, most of the platforms using this driver did not define an
>mdio subnode
>in the devicetree. Commit e34d65 ("stmmac: create of compatible mdio
>bus for stmmac driver")
>introduced a backward compatibily issue by using of_mdiobus_register
>explicitly
>with an mdio subnode. This patch fixes the issue by calling the
>function
>mdiobus_register, when mdio subnode is not found. The driver is now
>compatible
>with both modes.

Looks reasonable to me, though you will want to make sure the different DTSes get updated to include the proper compatible node for the MDIO bus.

>
>Signed-off-by: Romain Perier <romain.perier at gmail.com>

Please include a Fixes tag to help keep track of changes.

>---
> drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c | 8 +++++---
> 1 file changed, 5 insertions(+), 3 deletions(-)
>
>diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
>b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
>index 16c85cc..0034de44 100644
>--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
>+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
>@@ -218,8 +218,7 @@ int stmmac_mdio_register(struct net_device *ndev)
> 		if (mdio_node) {
> 			netdev_dbg(ndev, "FOUND MDIO subnode\n");
> 		} else {
>-			netdev_err(ndev, "NO MDIO subnode\n");
>-			return 0;
>+			netdev_warn(ndev, "No MDIO subnode found\n");
> 		}
> 	}
> 
>@@ -251,7 +250,10 @@ int stmmac_mdio_register(struct net_device *ndev)
> 	new_bus->phy_mask = mdio_bus_data->phy_mask;
> 	new_bus->parent = priv->device;
> 
>-	err = of_mdiobus_register(new_bus, mdio_node);
>+	if (IS_ENABLED(CONFIG_OF) && mdio_node)

You should be able to drop the IS_ENABLED part since there is an inline provided in the non-OF case which does a fallback to mdiobus_register().

>+		err = of_mdiobus_register(new_bus, mdio_node);
>+	else
>+		err = mdiobus_register(new_bus);
> 	if (err != 0) {

This looks reasonable, does it work if we just assign mdio_node to the pdev->dev.of_node to be backwards compatible with DTSes which have not yet been updated?

> 		pr_err("%s: Cannot register as MDIO bus\n", new_bus->name);
> 		goto bus_register_fail;


-- 
Florian


From romain.perier at gmail.com  Sat Jan  2 07:11:18 2016
From: romain.perier at gmail.com (Romain Perier)
Date: Sat, 2 Jan 2016 16:11:18 +0100
Subject: [PATCH] stmmac: Don't exit mdio registration when mdio subnode is
 not found in the DTS
In-Reply-To: <0E6F98A1-B9F1-48B0-B5CA-A215803E827F@gmail.com>
References: <1451397935-23643-1-git-send-email-romain.perier@gmail.com>
 <0E6F98A1-B9F1-48B0-B5CA-A215803E827F@gmail.com>
Message-ID: <CABgxDoLNWEtYtRTrYhp7T=tk3n-aPMCgOP2SL7+DoxFqTk7GuQ@mail.gmail.com>

Hi all,

2016-01-02 6:51 GMT+01:00 Florian Fainelli <f.fainelli at gmail.com>:
> On December 29, 2015 6:05:35 AM PST, Romain Perier <romain.perier at gmail.com> wrote:
>>Originally, most of the platforms using this driver did not define an
>>mdio subnode
>>in the devicetree. Commit e34d65 ("stmmac: create of compatible mdio
>>bus for stmmac driver")
>>introduced a backward compatibily issue by using of_mdiobus_register
>>explicitly
>>with an mdio subnode. This patch fixes the issue by calling the
>>function
>>mdiobus_register, when mdio subnode is not found. The driver is now
>>compatible
>>with both modes.
>
> Looks reasonable to me, though you will want to make sure the different DTSes get updated to include the proper compatible node for the MDIO bus.
>
>>
>>Signed-off-by: Romain Perier <romain.perier at gmail.com>
>
> Please include a Fixes tag to help keep track of changes.

Sure, sorry for my ignorance, but I did not know that we're supposed
to add a Fixes tag in this situation.

>
>>---
>> drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c | 8 +++++---
>> 1 file changed, 5 insertions(+), 3 deletions(-)
>>
>>diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
>>b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
>>index 16c85cc..0034de44 100644
>>--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
>>+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
>>@@ -218,8 +218,7 @@ int stmmac_mdio_register(struct net_device *ndev)
>>               if (mdio_node) {
>>                       netdev_dbg(ndev, "FOUND MDIO subnode\n");
>>               } else {
>>-                      netdev_err(ndev, "NO MDIO subnode\n");
>>-                      return 0;
>>+                      netdev_warn(ndev, "No MDIO subnode found\n");
>>               }
>>       }
>>
>>@@ -251,7 +250,10 @@ int stmmac_mdio_register(struct net_device *ndev)
>>       new_bus->phy_mask = mdio_bus_data->phy_mask;
>>       new_bus->parent = priv->device;
>>
>>-      err = of_mdiobus_register(new_bus, mdio_node);
>>+      if (IS_ENABLED(CONFIG_OF) && mdio_node)
>

> You should be able to drop the IS_ENABLED part since there is an inline provided in the non-OF case which does a fallback to mdiobus_register().

Good point

>
>>+              err = of_mdiobus_register(new_bus, mdio_node);
>>+      else
>>+              err = mdiobus_register(new_bus);
>>       if (err != 0) {
>
> This looks reasonable, does it work if we just assign mdio_node to the pdev->dev.of_node to be backwards compatible with DTSes which have not yet been updated?
>

I don't understand what you mean. Instead of defining mdio_node with
NULL, you propose to assign it to pdev->dev.of_node as its default
value... then it would be reassigned with "child_nod" if a subnode
with the compatible string "snps,dwmac-mdio" is found, or kept with
its default value otherwise... and in this case we could call
of_mdiobus_register(new_bus, mdio_node) all the time ? (it removes the
conditonnal branch)

Regards,
Romain


From f.fainelli at gmail.com  Sat Jan  2 09:52:06 2016
From: f.fainelli at gmail.com (Florian Fainelli)
Date: Sat, 2 Jan 2016 09:52:06 -0800
Subject: [PATCH] stmmac: Don't exit mdio registration when mdio subnode is
 not found in the DTS
In-Reply-To: <CABgxDoLNWEtYtRTrYhp7T=tk3n-aPMCgOP2SL7+DoxFqTk7GuQ@mail.gmail.com>
References: <1451397935-23643-1-git-send-email-romain.perier@gmail.com>
 <0E6F98A1-B9F1-48B0-B5CA-A215803E827F@gmail.com>
 <CABgxDoLNWEtYtRTrYhp7T=tk3n-aPMCgOP2SL7+DoxFqTk7GuQ@mail.gmail.com>
Message-ID: <56880E46.4050206@gmail.com>

Le 02/01/2016 07:11, Romain Perier a ?crit :
>>> Signed-off-by: Romain Perier <romain.perier at gmail.com>
>>
>> Please include a Fixes tag to help keep track of changes.
> 
> Sure, sorry for my ignorance, but I did not know that we're supposed
> to add a Fixes tag in this situation.

It is a good practice and definitively helps making sure that if
regression entered a merge window, and a fix came in another merge
window, people can easily backport the fix, moving on..

[snip]

>> This looks reasonable, does it work if we just assign mdio_node to the pdev->dev.of_node to be backwards compatible with DTSes which have not yet been updated?
>>
> 
> I don't understand what you mean. Instead of defining mdio_node with
> NULL, you propose to assign it to pdev->dev.of_node as its default
> value... then it would be reassigned with "child_nod" if a subnode
> with the compatible string "snps,dwmac-mdio" is found, or kept with
> its default value otherwise... and in this case we could call
> of_mdiobus_register(new_bus, mdio_node) all the time ? (it removes the
> conditonnal branch)

Your understanding is correct, this is what I was suggesting, thanks
-- 
Florian


From mturquette at baylibre.com  Sat Jan  2 13:41:38 2016
From: mturquette at baylibre.com (Michael Turquette)
Date: Sat, 02 Jan 2016 13:41:38 -0800
Subject: [PATCH] clk: rockchip: fix section mismatches with new
 child-clocks
In-Reply-To: <2433759.V4b2k7Wq9v@diego>
References: <2433759.V4b2k7Wq9v@diego>
Message-ID: <20160102214138.20626.42548@quark.deferred.io>

Quoting Heiko St?bner (2015-12-26 05:07:15)
> To model the muxes downstream of fractional dividers we introduced the
> child property, allowing to describe a direct child clock.
> The first implementation seems to cause section warnings, as the core
> clock-tree is marked as initdata while the data pointed to from the
> child element is not.
> 
> While there may be some way to also set that missing property in the
> inline notation I didn't find it, so to actually fix the issue for now
> move the sub-definitions into separate declarations that can have
> their own __initdata properties.
> 
> Signed-off-by: Heiko Stuebner <heiko at sntech.de>

Applied.

Thanks,
Mike

> ---
>  drivers/clk/rockchip/clk-rk3036.c | 35 ++++++++++++++------
>  drivers/clk/rockchip/clk-rk3188.c | 70 ++++++++++++++++++++++++++++-----------
>  drivers/clk/rockchip/clk-rk3288.c | 56 ++++++++++++++++++++++---------
>  drivers/clk/rockchip/clk.h        |  2 +-
>  4 files changed, 116 insertions(+), 47 deletions(-)
> 
> diff --git a/drivers/clk/rockchip/clk-rk3036.c b/drivers/clk/rockchip/clk-rk3036.c
> index fb35263..ebce980 100644
> --- a/drivers/clk/rockchip/clk-rk3036.c
> +++ b/drivers/clk/rockchip/clk-rk3036.c
> @@ -149,6 +149,26 @@ static struct rockchip_pll_clock rk3036_pll_clks[] __initdata = {
>  #define DFLAGS CLK_DIVIDER_HIWORD_MASK
>  #define GFLAGS (CLK_GATE_HIWORD_MASK | CLK_GATE_SET_TO_DISABLE)
>  
> +static struct rockchip_clk_branch rk3036_uart0_fracmux __initdata =
> +       MUX(SCLK_UART0, "sclk_uart0", mux_uart0_p, CLK_SET_RATE_PARENT,
> +                       RK2928_CLKSEL_CON(13), 8, 2, MFLAGS);
> +
> +static struct rockchip_clk_branch rk3036_uart1_fracmux __initdata =
> +       MUX(SCLK_UART1, "sclk_uart1", mux_uart1_p, CLK_SET_RATE_PARENT,
> +                       RK2928_CLKSEL_CON(14), 8, 2, MFLAGS);
> +
> +static struct rockchip_clk_branch rk3036_uart2_fracmux __initdata =
> +       MUX(SCLK_UART2, "sclk_uart2", mux_uart2_p, CLK_SET_RATE_PARENT,
> +                       RK2928_CLKSEL_CON(15), 8, 2, MFLAGS);
> +
> +static struct rockchip_clk_branch rk3036_i2s_fracmux __initdata =
> +       MUX(0, "i2s_pre", mux_i2s_pre_p, CLK_SET_RATE_PARENT,
> +                       RK2928_CLKSEL_CON(3), 8, 2, MFLAGS);
> +
> +static struct rockchip_clk_branch rk3036_spdif_fracmux __initdata =
> +       MUX(SCLK_SPDIF, "sclk_spdif", mux_spdif_p, 0,
> +                       RK2928_CLKSEL_CON(5), 8, 2, MFLAGS);
> +
>  static struct rockchip_clk_branch rk3036_clk_branches[] __initdata = {
>         /*
>          * Clock-Architecture Diagram 1
> @@ -230,18 +250,15 @@ static struct rockchip_clk_branch rk3036_clk_branches[] __initdata = {
>         COMPOSITE_FRACMUX(0, "uart0_frac", "uart0_src", CLK_SET_RATE_PARENT,
>                         RK2928_CLKSEL_CON(17), 0,
>                         RK2928_CLKGATE_CON(1), 9, GFLAGS,
> -       MUX(SCLK_UART0, "sclk_uart0", mux_uart0_p, CLK_SET_RATE_PARENT,
> -                       RK2928_CLKSEL_CON(13), 8, 2, MFLAGS)),
> +                       &rk3036_uart0_fracmux),
>         COMPOSITE_FRACMUX(0, "uart1_frac", "uart1_src", CLK_SET_RATE_PARENT,
>                         RK2928_CLKSEL_CON(18), 0,
>                         RK2928_CLKGATE_CON(1), 11, GFLAGS,
> -       MUX(SCLK_UART1, "sclk_uart1", mux_uart1_p, CLK_SET_RATE_PARENT,
> -                       RK2928_CLKSEL_CON(14), 8, 2, MFLAGS)),
> +                       &rk3036_uart1_fracmux),
>         COMPOSITE_FRACMUX(0, "uart2_frac", "uart2_src", CLK_SET_RATE_PARENT,
>                         RK2928_CLKSEL_CON(19), 0,
>                         RK2928_CLKGATE_CON(1), 13, GFLAGS,
> -       MUX(SCLK_UART2, "sclk_uart2", mux_uart2_p, CLK_SET_RATE_PARENT,
> -                       RK2928_CLKSEL_CON(15), 8, 2, MFLAGS)),
> +                       &rk3036_uart2_fracmux),
>  
>         COMPOSITE(0, "aclk_vcodec", mux_pll_src_3plls_p, 0,
>                         RK2928_CLKSEL_CON(32), 14, 2, MFLAGS, 8, 5, DFLAGS,
> @@ -292,8 +309,7 @@ static struct rockchip_clk_branch rk3036_clk_branches[] __initdata = {
>         COMPOSITE_FRACMUX(0, "i2s_frac", "i2s_src", CLK_SET_RATE_PARENT,
>                         RK2928_CLKSEL_CON(7), 0,
>                         RK2928_CLKGATE_CON(0), 10, GFLAGS,
> -       MUX(0, "i2s_pre", mux_i2s_pre_p, CLK_SET_RATE_PARENT,
> -                       RK2928_CLKSEL_CON(3), 8, 2, MFLAGS)),
> +                       &rk3036_i2s_fracmux),
>         COMPOSITE_NODIV(SCLK_I2S_OUT, "i2s_clkout", mux_i2s_clkout_p, 0,
>                         RK2928_CLKSEL_CON(3), 12, 1, MFLAGS,
>                         RK2928_CLKGATE_CON(0), 13, GFLAGS),
> @@ -306,8 +322,7 @@ static struct rockchip_clk_branch rk3036_clk_branches[] __initdata = {
>         COMPOSITE_FRACMUX(0, "spdif_frac", "spdif_src", 0,
>                         RK2928_CLKSEL_CON(9), 0,
>                         RK2928_CLKGATE_CON(2), 12, GFLAGS,
> -       MUX(SCLK_SPDIF, "sclk_spdif", mux_spdif_p, 0,
> -                       RK2928_CLKSEL_CON(5), 8, 2, MFLAGS)),
> +                       &rk3036_spdif_fracmux),
>  
>         GATE(SCLK_OTGPHY0, "sclk_otgphy0", "xin12m", CLK_IGNORE_UNUSED,
>                         RK2928_CLKGATE_CON(1), 5, GFLAGS),
> diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c
> index 6398a91..7f7444c 100644
> --- a/drivers/clk/rockchip/clk-rk3188.c
> +++ b/drivers/clk/rockchip/clk-rk3188.c
> @@ -247,6 +247,30 @@ static struct clk_div_table div_core_peri_t[] = {
>         { /* sentinel */ },
>  };
>  
> +static struct rockchip_clk_branch common_hsadc_out_fracmux __initdata =
> +       MUX(0, "sclk_hsadc_out", mux_sclk_hsadc_p, 0,
> +                       RK2928_CLKSEL_CON(22), 4, 2, MFLAGS);
> +
> +static struct rockchip_clk_branch common_spdif_fracmux __initdata =
> +       MUX(SCLK_SPDIF, "sclk_spdif", mux_sclk_spdif_p, CLK_SET_RATE_PARENT,
> +                       RK2928_CLKSEL_CON(5), 8, 2, MFLAGS);
> +
> +static struct rockchip_clk_branch common_uart0_fracmux __initdata =
> +       MUX(SCLK_UART0, "sclk_uart0", mux_sclk_uart0_p, 0,
> +                       RK2928_CLKSEL_CON(13), 8, 2, MFLAGS);
> +
> +static struct rockchip_clk_branch common_uart1_fracmux __initdata =
> +       MUX(SCLK_UART1, "sclk_uart1", mux_sclk_uart1_p, 0,
> +                       RK2928_CLKSEL_CON(14), 8, 2, MFLAGS);
> +
> +static struct rockchip_clk_branch common_uart2_fracmux __initdata =
> +       MUX(SCLK_UART2, "sclk_uart2", mux_sclk_uart2_p, 0,
> +                       RK2928_CLKSEL_CON(15), 8, 2, MFLAGS);
> +
> +static struct rockchip_clk_branch common_uart3_fracmux __initdata =
> +       MUX(SCLK_UART3, "sclk_uart3", mux_sclk_uart3_p, 0,
> +                       RK2928_CLKSEL_CON(16), 8, 2, MFLAGS);
> +
>  static struct rockchip_clk_branch common_clk_branches[] __initdata = {
>         /*
>          * Clock-Architecture Diagram 2
> @@ -338,8 +362,7 @@ static struct rockchip_clk_branch common_clk_branches[] __initdata = {
>         COMPOSITE_FRACMUX(0, "hsadc_frac", "hsadc_src", 0,
>                         RK2928_CLKSEL_CON(23), 0,
>                         RK2928_CLKGATE_CON(2), 7, GFLAGS,
> -               MUX(0, "sclk_hsadc_out", mux_sclk_hsadc_p, 0,
> -                               RK2928_CLKSEL_CON(22), 4, 2, MFLAGS)),
> +                       &common_hsadc_out_fracmux),
>         INVERTER(SCLK_HSADC, "sclk_hsadc", "sclk_hsadc_out",
>                         RK2928_CLKSEL_CON(22), 7, IFLAGS),
>  
> @@ -353,8 +376,7 @@ static struct rockchip_clk_branch common_clk_branches[] __initdata = {
>         COMPOSITE_FRACMUX(0, "spdif_frac", "spdif_pll", CLK_SET_RATE_PARENT,
>                         RK2928_CLKSEL_CON(9), 0,
>                         RK2928_CLKGATE_CON(0), 14, GFLAGS,
> -               MUX(SCLK_SPDIF, "sclk_spdif", mux_sclk_spdif_p, CLK_SET_RATE_PARENT,
> -                               RK2928_CLKSEL_CON(5), 8, 2, MFLAGS)),
> +                       &common_spdif_fracmux),
>  
>         /*
>          * Clock-Architecture Diagram 4
> @@ -388,32 +410,28 @@ static struct rockchip_clk_branch common_clk_branches[] __initdata = {
>         COMPOSITE_FRACMUX(0, "uart0_frac", "uart0_pre", 0,
>                         RK2928_CLKSEL_CON(17), 0,
>                         RK2928_CLKGATE_CON(1), 9, GFLAGS,
> -               MUX(SCLK_UART0, "sclk_uart0", mux_sclk_uart0_p, 0,
> -                               RK2928_CLKSEL_CON(13), 8, 2, MFLAGS)),
> +                       &common_uart0_fracmux),
>         COMPOSITE_NOMUX(0, "uart1_pre", "uart_src", 0,
>                         RK2928_CLKSEL_CON(14), 0, 7, DFLAGS,
>                         RK2928_CLKGATE_CON(1), 10, GFLAGS),
>         COMPOSITE_FRACMUX(0, "uart1_frac", "uart1_pre", 0,
>                         RK2928_CLKSEL_CON(18), 0,
>                         RK2928_CLKGATE_CON(1), 11, GFLAGS,
> -               MUX(SCLK_UART1, "sclk_uart1", mux_sclk_uart1_p, 0,
> -                               RK2928_CLKSEL_CON(14), 8, 2, MFLAGS)),
> +                       &common_uart1_fracmux),
>         COMPOSITE_NOMUX(0, "uart2_pre", "uart_src", 0,
>                         RK2928_CLKSEL_CON(15), 0, 7, DFLAGS,
>                         RK2928_CLKGATE_CON(1), 12, GFLAGS),
>         COMPOSITE_FRACMUX(0, "uart2_frac", "uart2_pre", 0,
>                         RK2928_CLKSEL_CON(19), 0,
>                         RK2928_CLKGATE_CON(1), 13, GFLAGS,
> -               MUX(SCLK_UART2, "sclk_uart2", mux_sclk_uart2_p, 0,
> -                               RK2928_CLKSEL_CON(15), 8, 2, MFLAGS)),
> +                       &common_uart2_fracmux),
>         COMPOSITE_NOMUX(0, "uart3_pre", "uart_src", 0,
>                         RK2928_CLKSEL_CON(16), 0, 7, DFLAGS,
>                         RK2928_CLKGATE_CON(1), 14, GFLAGS),
>         COMPOSITE_FRACMUX(0, "uart3_frac", "uart3_pre", 0,
>                         RK2928_CLKSEL_CON(20), 0,
>                         RK2928_CLKGATE_CON(1), 15, GFLAGS,
> -               MUX(SCLK_UART3, "sclk_uart3", mux_sclk_uart3_p, 0,
> -                               RK2928_CLKSEL_CON(16), 8, 2, MFLAGS)),
> +                       &common_uart3_fracmux),
>  
>         GATE(SCLK_JTAG, "jtag", "ext_jtag", 0, RK2928_CLKGATE_CON(1), 3, GFLAGS),
>  
> @@ -523,6 +541,18 @@ static struct clk_div_table div_aclk_cpu_t[] = {
>         { /* sentinel */ },
>  };
>  
> +static struct rockchip_clk_branch rk3066a_i2s0_fracmux __initdata =
> +       MUX(SCLK_I2S0, "sclk_i2s0", mux_sclk_i2s0_p, 0,
> +                       RK2928_CLKSEL_CON(2), 8, 2, MFLAGS);
> +
> +static struct rockchip_clk_branch rk3066a_i2s1_fracmux __initdata =
> +       MUX(SCLK_I2S1, "sclk_i2s1", mux_sclk_i2s1_p, 0,
> +                       RK2928_CLKSEL_CON(3), 8, 2, MFLAGS);
> +
> +static struct rockchip_clk_branch rk3066a_i2s2_fracmux __initdata =
> +       MUX(SCLK_I2S2, "sclk_i2s2", mux_sclk_i2s2_p, 0,
> +                       RK2928_CLKSEL_CON(4), 8, 2, MFLAGS);
> +
>  static struct rockchip_clk_branch rk3066a_clk_branches[] __initdata = {
>         DIVTBL(0, "aclk_cpu_pre", "armclk", 0,
>                         RK2928_CLKSEL_CON(1), 0, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, div_aclk_cpu_t),
> @@ -587,24 +617,21 @@ static struct rockchip_clk_branch rk3066a_clk_branches[] __initdata = {
>         COMPOSITE_FRACMUX(0, "i2s0_frac", "i2s0_pre", 0,
>                         RK2928_CLKSEL_CON(6), 0,
>                         RK2928_CLKGATE_CON(0), 8, GFLAGS,
> -               MUX(SCLK_I2S0, "sclk_i2s0", mux_sclk_i2s0_p, 0,
> -                               RK2928_CLKSEL_CON(2), 8, 2, MFLAGS)),
> +                       &rk3066a_i2s0_fracmux),
>         COMPOSITE_NOMUX(0, "i2s1_pre", "i2s_src", 0,
>                         RK2928_CLKSEL_CON(3), 0, 7, DFLAGS,
>                         RK2928_CLKGATE_CON(0), 9, GFLAGS),
>         COMPOSITE_FRACMUX(0, "i2s1_frac", "i2s1_pre", 0,
>                         RK2928_CLKSEL_CON(7), 0,
>                         RK2928_CLKGATE_CON(0), 10, GFLAGS,
> -               MUX(SCLK_I2S1, "sclk_i2s1", mux_sclk_i2s1_p, 0,
> -                               RK2928_CLKSEL_CON(3), 8, 2, MFLAGS)),
> +                       &rk3066a_i2s1_fracmux),
>         COMPOSITE_NOMUX(0, "i2s2_pre", "i2s_src", 0,
>                         RK2928_CLKSEL_CON(4), 0, 7, DFLAGS,
>                         RK2928_CLKGATE_CON(0), 11, GFLAGS),
>         COMPOSITE_FRACMUX(0, "i2s2_frac", "i2s2_pre", 0,
>                         RK2928_CLKSEL_CON(8), 0,
>                         RK2928_CLKGATE_CON(0), 12, GFLAGS,
> -               MUX(SCLK_I2S2, "sclk_i2s2", mux_sclk_i2s2_p, 0,
> -                               RK2928_CLKSEL_CON(4), 8, 2, MFLAGS)),
> +                       &rk3066a_i2s2_fracmux),
>  
>         GATE(HCLK_I2S1, "hclk_i2s1", "hclk_cpu", 0, RK2928_CLKGATE_CON(7), 3, GFLAGS),
>         GATE(HCLK_I2S2, "hclk_i2s2", "hclk_cpu", 0, RK2928_CLKGATE_CON(7), 4, GFLAGS),
> @@ -638,6 +665,10 @@ static struct clk_div_table div_rk3188_aclk_core_t[] = {
>  PNAME(mux_hsicphy_p)           = { "sclk_otgphy0", "sclk_otgphy1",
>                                     "gpll", "cpll" };
>  
> +static struct rockchip_clk_branch rk3188_i2s0_fracmux __initdata =
> +       MUX(SCLK_I2S0, "sclk_i2s0", mux_sclk_i2s0_p, 0,
> +                       RK2928_CLKSEL_CON(3), 8, 2, MFLAGS);
> +
>  static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = {
>         COMPOSITE_NOMUX_DIVTBL(0, "aclk_core", "armclk", CLK_IGNORE_UNUSED,
>                         RK2928_CLKSEL_CON(1), 3, 3, DFLAGS | CLK_DIVIDER_READ_ONLY,
> @@ -694,8 +725,7 @@ static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = {
>         COMPOSITE_FRACMUX(0, "i2s0_frac", "i2s0_pre", 0,
>                         RK2928_CLKSEL_CON(7), 0,
>                         RK2928_CLKGATE_CON(0), 10, GFLAGS,
> -               MUX(SCLK_I2S0, "sclk_i2s0", mux_sclk_i2s0_p, 0,
> -                               RK2928_CLKSEL_CON(3), 8, 2, MFLAGS)),
> +                       &rk3188_i2s0_fracmux),
>  
>         GATE(0, "hclk_imem0", "hclk_cpu", 0, RK2928_CLKGATE_CON(4), 14, GFLAGS),
>         GATE(0, "hclk_imem1", "hclk_cpu", 0, RK2928_CLKGATE_CON(4), 15, GFLAGS),
> diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c
> index 80065e6..984fc18 100644
> --- a/drivers/clk/rockchip/clk-rk3288.c
> +++ b/drivers/clk/rockchip/clk-rk3288.c
> @@ -225,6 +225,38 @@ static struct clk_div_table div_hclk_cpu_t[] = {
>  #define GFLAGS (CLK_GATE_HIWORD_MASK | CLK_GATE_SET_TO_DISABLE)
>  #define IFLAGS ROCKCHIP_INVERTER_HIWORD_MASK
>  
> +static struct rockchip_clk_branch rk3288_i2s_fracmux __initdata =
> +       MUX(0, "i2s_pre", mux_i2s_pre_p, CLK_SET_RATE_PARENT,
> +                       RK3288_CLKSEL_CON(4), 8, 2, MFLAGS);
> +
> +static struct rockchip_clk_branch rk3288_spdif_fracmux __initdata =
> +       MUX(0, "spdif_mux", mux_spdif_p, CLK_SET_RATE_PARENT,
> +                       RK3288_CLKSEL_CON(5), 8, 2, MFLAGS);
> +
> +static struct rockchip_clk_branch rk3288_spdif_8ch_fracmux __initdata =
> +       MUX(0, "spdif_8ch_mux", mux_spdif_8ch_p, CLK_SET_RATE_PARENT,
> +                       RK3288_CLKSEL_CON(40), 8, 2, MFLAGS);
> +
> +static struct rockchip_clk_branch rk3288_uart0_fracmux __initdata =
> +       MUX(SCLK_UART0, "sclk_uart0", mux_uart0_p, CLK_SET_RATE_PARENT,
> +                       RK3288_CLKSEL_CON(13), 8, 2, MFLAGS);
> +
> +static struct rockchip_clk_branch rk3288_uart1_fracmux __initdata =
> +       MUX(SCLK_UART1, "sclk_uart1", mux_uart1_p, CLK_SET_RATE_PARENT,
> +                       RK3288_CLKSEL_CON(14), 8, 2, MFLAGS);
> +
> +static struct rockchip_clk_branch rk3288_uart2_fracmux __initdata =
> +       MUX(SCLK_UART2, "sclk_uart2", mux_uart2_p, CLK_SET_RATE_PARENT,
> +                       RK3288_CLKSEL_CON(15), 8, 2, MFLAGS);
> +
> +static struct rockchip_clk_branch rk3288_uart3_fracmux __initdata =
> +       MUX(SCLK_UART3, "sclk_uart3", mux_uart3_p, CLK_SET_RATE_PARENT,
> +                       RK3288_CLKSEL_CON(16), 8, 2, MFLAGS);
> +
> +static struct rockchip_clk_branch rk3288_uart4_fracmux __initdata =
> +       MUX(SCLK_UART4, "sclk_uart4", mux_uart4_p, CLK_SET_RATE_PARENT,
> +                       RK3288_CLKSEL_CON(3), 8, 2, MFLAGS);
> +
>  static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = {
>         /*
>          * Clock-Architecture Diagram 1
> @@ -307,8 +339,7 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = {
>         COMPOSITE_FRACMUX(0, "i2s_frac", "i2s_src", CLK_SET_RATE_PARENT,
>                         RK3288_CLKSEL_CON(8), 0,
>                         RK3288_CLKGATE_CON(4), 2, GFLAGS,
> -               MUX(0, "i2s_pre", mux_i2s_pre_p, CLK_SET_RATE_PARENT,
> -                               RK3288_CLKSEL_CON(4), 8, 2, MFLAGS)),
> +                       &rk3288_i2s_fracmux),
>         COMPOSITE_NODIV(SCLK_I2S0_OUT, "i2s0_clkout", mux_i2s_clkout_p, 0,
>                         RK3288_CLKSEL_CON(4), 12, 1, MFLAGS,
>                         RK3288_CLKGATE_CON(4), 0, GFLAGS),
> @@ -323,8 +354,7 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = {
>         COMPOSITE_FRACMUX(0, "spdif_frac", "spdif_src", CLK_SET_RATE_PARENT,
>                         RK3288_CLKSEL_CON(9), 0,
>                         RK3288_CLKGATE_CON(4), 5, GFLAGS,
> -               MUX(0, "spdif_mux", mux_spdif_p, CLK_SET_RATE_PARENT,
> -                               RK3288_CLKSEL_CON(5), 8, 2, MFLAGS)),
> +                       &rk3288_spdif_fracmux),
>         GATE(SCLK_SPDIF, "sclk_spdif", "spdif_mux", CLK_SET_RATE_PARENT,
>                         RK3288_CLKGATE_CON(4), 6, GFLAGS),
>         COMPOSITE_NOMUX(0, "spdif_8ch_pre", "spdif_src", CLK_SET_RATE_PARENT,
> @@ -333,8 +363,7 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = {
>         COMPOSITE_FRACMUX(0, "spdif_8ch_frac", "spdif_8ch_pre", CLK_SET_RATE_PARENT,
>                         RK3288_CLKSEL_CON(41), 0,
>                         RK3288_CLKGATE_CON(4), 8, GFLAGS,
> -               MUX(0, "spdif_8ch_mux", mux_spdif_8ch_p, CLK_SET_RATE_PARENT,
> -                               RK3288_CLKSEL_CON(40), 8, 2, MFLAGS)),
> +                       &rk3288_spdif_8ch_fracmux),
>         GATE(SCLK_SPDIF8CH, "sclk_spdif_8ch", "spdif_8ch_mux", CLK_SET_RATE_PARENT,
>                         RK3288_CLKGATE_CON(4), 9, GFLAGS),
>  
> @@ -541,8 +570,7 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = {
>         COMPOSITE_FRACMUX(0, "uart0_frac", "uart0_src", CLK_SET_RATE_PARENT,
>                         RK3288_CLKSEL_CON(17), 0,
>                         RK3288_CLKGATE_CON(1), 9, GFLAGS,
> -               MUX(SCLK_UART0, "sclk_uart0", mux_uart0_p, CLK_SET_RATE_PARENT,
> -                               RK3288_CLKSEL_CON(13), 8, 2, MFLAGS)),
> +                       &rk3288_uart0_fracmux),
>         MUX(0, "uart_src", mux_pll_src_cpll_gpll_p, 0,
>                         RK3288_CLKSEL_CON(13), 15, 1, MFLAGS),
>         COMPOSITE_NOMUX(0, "uart1_src", "uart_src", 0,
> @@ -551,32 +579,28 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = {
>         COMPOSITE_FRACMUX(0, "uart1_frac", "uart1_src", CLK_SET_RATE_PARENT,
>                         RK3288_CLKSEL_CON(18), 0,
>                         RK3288_CLKGATE_CON(1), 11, GFLAGS,
> -               MUX(SCLK_UART1, "sclk_uart1", mux_uart1_p, CLK_SET_RATE_PARENT,
> -                               RK3288_CLKSEL_CON(14), 8, 2, MFLAGS)),
> +                       &rk3288_uart1_fracmux),
>         COMPOSITE_NOMUX(0, "uart2_src", "uart_src", 0,
>                         RK3288_CLKSEL_CON(15), 0, 7, DFLAGS,
>                         RK3288_CLKGATE_CON(1), 12, GFLAGS),
>         COMPOSITE_FRACMUX(0, "uart2_frac", "uart2_src", CLK_SET_RATE_PARENT,
>                         RK3288_CLKSEL_CON(19), 0,
>                         RK3288_CLKGATE_CON(1), 13, GFLAGS,
> -               MUX(SCLK_UART2, "sclk_uart2", mux_uart2_p, CLK_SET_RATE_PARENT,
> -                               RK3288_CLKSEL_CON(15), 8, 2, MFLAGS)),
> +                       &rk3288_uart2_fracmux),
>         COMPOSITE_NOMUX(0, "uart3_src", "uart_src", 0,
>                         RK3288_CLKSEL_CON(16), 0, 7, DFLAGS,
>                         RK3288_CLKGATE_CON(1), 14, GFLAGS),
>         COMPOSITE_FRACMUX(0, "uart3_frac", "uart3_src", CLK_SET_RATE_PARENT,
>                         RK3288_CLKSEL_CON(20), 0,
>                         RK3288_CLKGATE_CON(1), 15, GFLAGS,
> -               MUX(SCLK_UART3, "sclk_uart3", mux_uart3_p, CLK_SET_RATE_PARENT,
> -                               RK3288_CLKSEL_CON(16), 8, 2, MFLAGS)),
> +                       &rk3288_uart3_fracmux),
>         COMPOSITE_NOMUX(0, "uart4_src", "uart_src", 0,
>                         RK3288_CLKSEL_CON(3), 0, 7, DFLAGS,
>                         RK3288_CLKGATE_CON(2), 12, GFLAGS),
>         COMPOSITE_FRACMUX(0, "uart4_frac", "uart4_src", CLK_SET_RATE_PARENT,
>                         RK3288_CLKSEL_CON(7), 0,
>                         RK3288_CLKGATE_CON(2), 13, GFLAGS,
> -               MUX(SCLK_UART4, "sclk_uart4", mux_uart4_p, CLK_SET_RATE_PARENT,
> -                               RK3288_CLKSEL_CON(3), 8, 2, MFLAGS)),
> +                       &rk3288_uart4_fracmux),
>  
>         COMPOSITE(0, "mac_pll_src", mux_pll_src_npll_cpll_gpll_p, 0,
>                         RK3288_CLKSEL_CON(21), 0, 2, MFLAGS, 8, 5, DFLAGS,
> diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
> index 3f71ee5..ff8bd23 100644
> --- a/drivers/clk/rockchip/clk.h
> +++ b/drivers/clk/rockchip/clk.h
> @@ -424,7 +424,7 @@ struct rockchip_clk_branch {
>                 .gate_offset    = go,                           \
>                 .gate_shift     = gs,                           \
>                 .gate_flags     = gf,                           \
> -               .child          = &(struct rockchip_clk_branch)ch, \
> +               .child          = ch,                           \
>         }
>  
>  #define MUX(_id, cname, pnames, f, o, s, w, mf)                        \
> -- 
> 2.6.4
> 
> 


From mturquette at baylibre.com  Sat Jan  2 13:42:17 2016
From: mturquette at baylibre.com (Michael Turquette)
Date: Sat, 02 Jan 2016 13:42:17 -0800
Subject: [PATCH] [BUG] clk: rockchip: don't mark clock names as initconst
In-Reply-To: <3926467.jaQRQH5bEO@diego>
References: <2791943.MBZ53mI7Gp@wuerfel> <1799610.nrn5HMCSxL@diego>
 <6692523.e5K11FUKe0@wuerfel> <3926467.jaQRQH5bEO@diego>
Message-ID: <20160102214217.20626.98999@quark.deferred.io>

Quoting Heiko St?bner (2016-01-01 14:05:10)
> Am Freitag, 1. Januar 2016, 22:50:43 schrieb Arnd Bergmann:
> > On Friday 01 January 2016 18:06:30 Heiko St?bner wrote:
> > > "[PATCH] clk: rockchip: fix section mismatches with new child-clocks" [0]
> > > 
> > > should be in Mike's + Stephen's inbox since last week as well, which moves
> > > the offending new elements into separate entities, which can have
> > > __initdata attributes again.
> > > 
> > > 
> > > Heiko
> > > 
> > > [0] http://www.spinics.net/lists/arm-kernel/msg471295.html
> > 
> > The patch looks good, but for some reason, the next-20151223 kernel had no
> > problem and next-20151231 was broken, the top commits in
> > drivers/clk/rockchips are:
> 
> That is correct. next-20151223 did not contain the offending patches yet. 
> After the patches got merged into the clock-tree the kbuild-robot alerted us 
> to the __initdata issue, so I created the linked patch as fixup.

The fix has been pushed to the clk mirror. The next -next should no
longer have the section mismatch warnings.

Regards,
Mike

> 
> 
> Heiko
> 
> > 
> > commit a915e30dd26ea5f3cc2e2c044aba38ee5973d3fa
> > Merge: ce6dd266d535 b0158bb27c7b
> > Author: Michael Turquette <mturquette at baylibre.com>
> > Date:   Wed Dec 23 13:08:56 2015 -0800
> > 
> >     Merge branch 'clk-rockchip' into clk-next
> > 
> > commit b0158bb27c7b6e9843f541c17b24dbd964b76db6
> > Author: Xing Zheng <zhengxing at rock-chips.com>
> > Date:   Tue Dec 22 22:28:01 2015 +0100
> > 
> >     clk: rockchip: rk3036: include downstream muxes into fractional dividers
> > 
> >     Use the newly introduced possibility to combine the fractional dividers
> > 
> > 
> >       Arnd
> 


From romain.perier at gmail.com  Sun Jan  3 06:36:06 2016
From: romain.perier at gmail.com (Romain Perier)
Date: Sun, 3 Jan 2016 15:36:06 +0100
Subject: [PATCH] stmmac: Don't exit mdio registration when mdio subnode is
 not found in the DTS
In-Reply-To: <56880E46.4050206@gmail.com>
References: <1451397935-23643-1-git-send-email-romain.perier@gmail.com>
 <0E6F98A1-B9F1-48B0-B5CA-A215803E827F@gmail.com>
 <CABgxDoLNWEtYtRTrYhp7T=tk3n-aPMCgOP2SL7+DoxFqTk7GuQ@mail.gmail.com>
 <56880E46.4050206@gmail.com>
Message-ID: <CABgxDo+EOVW-RJ1WVY+oHt1tbW7qq9Q_+wK3ywiexh+auEebaA@mail.gmail.com>

Hi,

2016-01-02 18:52 GMT+01:00 Florian Fainelli <f.fainelli at gmail.com>:
>
>>> This looks reasonable, does it work if we just assign mdio_node to the pdev->dev.of_node to be backwards compatible with DTSes which have not yet been updated?
>>>
>>
>> I don't understand what you mean. Instead of defining mdio_node with
>> NULL, you propose to assign it to pdev->dev.of_node as its default
>> value... then it would be reassigned with "child_nod" if a subnode
>> with the compatible string "snps,dwmac-mdio" is found, or kept with
>> its default value otherwise... and in this case we could call
>> of_mdiobus_register(new_bus, mdio_node) all the time ? (it removes the
>> conditonnal branch)
>
> Your understanding is correct, this is what I was suggesting, thanks

So in this case, I already tested this yesterday, it does not work,
basically because the for loop just after of_mdiobus_register is never
executed, probably because new_bus->phy_map is not populated, which is
not the case by calling mdiobus_register for example.
of_mdiobus_register seems to populate this array of phys only for
subnodes found in the devicetree...

Regards,
Romain


From kishon at ti.com  Sun Jan  3 23:45:19 2016
From: kishon at ti.com (Kishon Vijay Abraham I)
Date: Mon, 4 Jan 2016 13:15:19 +0530
Subject: [PATCH 2/2] phy: add a driver for the Rockchip SoC internal eMMC
 PHY
In-Reply-To: <1451353928-23973-1-git-send-email-shawn.lin@rock-chips.com>
References: <1451353928-23973-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <568A230F.3000605@ti.com>

Hi,

On Tuesday 29 December 2015 07:22 AM, Shawn Lin wrote:
> This patch to add a generic PHY driver for ROCKCHIP eMMC PHY.
> Access the PHY via registers provided by GRF (general register
> files) module.
> 
> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
> ---
> 
>  drivers/phy/Kconfig             |   7 ++
>  drivers/phy/Makefile            |   1 +
>  drivers/phy/phy-rockchip-emmc.c | 256 ++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 264 insertions(+)
>  create mode 100644 drivers/phy/phy-rockchip-emmc.c
> 
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index 03cb3ea..47d16e3 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -320,6 +320,13 @@ config PHY_ROCKCHIP_USB
>  	help
>  	  Enable this to support the Rockchip USB 2.0 PHY.
>  
> +config PHY_ROCKCHIP_EMMC
> +	tristate "Rockchip EMMC PHY Driver"
> +	depends on ARCH_ROCKCHIP && OF
> +	select GENERIC_PHY
> +	help
> +	  Enable this to support the Rockchip EMMC PHY.
> +
>  config PHY_ST_SPEAR1310_MIPHY
>  	tristate "ST SPEAR1310-MIPHY driver"
>  	select GENERIC_PHY
> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> index 075db1a..ed8f175 100644
> --- a/drivers/phy/Makefile
> +++ b/drivers/phy/Makefile
> @@ -35,6 +35,7 @@ phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2)	+= phy-s5pv210-usb2.o
>  obj-$(CONFIG_PHY_EXYNOS5_USBDRD)	+= phy-exynos5-usbdrd.o
>  obj-$(CONFIG_PHY_QCOM_APQ8064_SATA)	+= phy-qcom-apq8064-sata.o
>  obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
> +obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o
>  obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA)	+= phy-qcom-ipq806x-sata.o
>  obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY)	+= phy-spear1310-miphy.o
>  obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)	+= phy-spear1340-miphy.o
> diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c
> new file mode 100644
> index 0000000..78f51a2
> --- /dev/null
> +++ b/drivers/phy/phy-rockchip-emmc.c
> @@ -0,0 +1,256 @@
> +/*
> + * Rockchip emmc PHY driver
> + *
> + * Copyright (C) 2015 Shawn Lin <shawn.lin at rock-chips.com>
> + * Copyright (C) 2015 ROCKCHIP, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +
> +/*
> + * The higher 16-bit of this register is used for write protection
> + * only if BIT(x + 16) set to 1 the BIT(x) can be written.
> + */
> +#define HIWORD_UPDATE(val, mask, shift) \
> +		((val) << (shift) | (mask) << ((shift) + 16))
> +
> +/* Register definition */
> +#define GRF_EMMCPHY_CON0	0x0
> +#define GRF_EMMCPHY_CON1	0x4
> +#define GRF_EMMCPHY_CON2	0x8
> +#define GRF_EMMCPHY_CON3	0xc
> +#define GRF_EMMCPHY_CON4	0x10
> +#define GRF_EMMCPHY_CON5	0x14
> +#define GRF_EMMCPHY_CON6	0x18
> +#define GRF_EMMCPHY_STATUS	0x20
> +
> +#define PHYCTRL_PDB_MASK	0x1
> +#define PHYCTRL_PDB_SHIFT	0x0
> +#define PHYCTRL_PDB_PWR_ON	0x1
> +#define PHYCTRL_PDB_PWR_OFF	0x0
> +#define PHYCTRL_ENDLL_MASK	0x1
> +#define PHYCTRL_ENDLL_SHIFT     0x1
> +#define PHYCTRL_ENDLL_ENABLE	0x1
> +#define PHYCTRL_ENDLL_DISABLE	0x0
> +#define PHYCTRL_CALDONE_MASK	0x1
> +#define PHYCTRL_CALDONE_SHIFT   0x6
> +#define PHYCTRL_CALDONE_DONE	0x1
> +#define PHYCTRL_CALDONE_GOING	0x0
> +#define PHYCTRL_DLLRDY_MASK	0x1
> +#define PHYCTRL_DLLRDY_SHIFT	0x5
> +#define PHYCTRL_DLLRDY_DONE	0x1
> +#define PHYCTRL_DLLRDY_GOING	0x0
> +
> +struct rockchip_emmc_phy {
> +	unsigned int	reg_offset;
> +	struct regmap	*reg_base;
> +	struct phy	*phy;

The phy looks unnecessary.
> +	bool		state;

hmm.. I want to have some sort of state machine in phy core so that individual
PHY drivers don't have to maintain the state. However I'm not sure if all the
PHY's will require such mechanism.

> +};
> +
> +static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy,
> +				   bool on_off)
> +{
> +	unsigned int caldone;
> +	unsigned int dllrdy;
> +
> +	/*
> +	 * Keep phyctrl_pdb and phyctrl_endll low to allow
> +	 * initialization of CALIO state M/C DFFs
> +	 */
> +	regmap_write(rk_phy->reg_base,
> +		     rk_phy->reg_offset + GRF_EMMCPHY_CON6,
> +		     HIWORD_UPDATE(PHYCTRL_PDB_PWR_OFF,
> +				   PHYCTRL_PDB_MASK,
> +				   PHYCTRL_PDB_SHIFT));
> +	regmap_write(rk_phy->reg_base,
> +		     rk_phy->reg_offset + GRF_EMMCPHY_CON6,
> +		     HIWORD_UPDATE(PHYCTRL_ENDLL_DISABLE,
> +				   PHYCTRL_ENDLL_MASK,
> +				   PHYCTRL_ENDLL_SHIFT));
> +
> +	/* Already finish power_off above */
> +	if (on_off == PHYCTRL_PDB_PWR_OFF)
> +		return 0;
> +
> +	/*
> +	 * According to the user manual, calpad calibration
> +	 * cycle takes more than 2us without the minimal recommended
> +	 * value, so we may need a little margin here
> +	 */
> +	udelay(3);
> +	regmap_write(rk_phy->reg_base,
> +		     rk_phy->reg_offset + GRF_EMMCPHY_CON6,
> +		     HIWORD_UPDATE(PHYCTRL_PDB_PWR_ON,
> +				   PHYCTRL_PDB_MASK,
> +				   PHYCTRL_PDB_SHIFT));
> +
> +	/*
> +	 * According to the user manual, it asks driver to
> +	 * wait 5us for calpad busy trimming
> +	 */
> +	udelay(5);
> +	regmap_read(rk_phy->reg_base,
> +		    rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
> +		    &caldone);
> +	caldone = (caldone >> PHYCTRL_CALDONE_SHIFT) & PHYCTRL_CALDONE_MASK;
> +	if (caldone != PHYCTRL_CALDONE_DONE) {
> +		pr_err("rockchip_emmc_phy_power: caldone timeout.\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	/*
> +	 * Enable analog DLL circuits, and we need extra 10.2us
> +	 * for dll to be ready for work.
> +	 */
> +	regmap_write(rk_phy->reg_base,
> +		     rk_phy->reg_offset + GRF_EMMCPHY_CON6,
> +		     HIWORD_UPDATE(PHYCTRL_ENDLL_ENABLE,
> +				   PHYCTRL_ENDLL_MASK,
> +				   PHYCTRL_ENDLL_SHIFT));
> +	udelay(11);
> +	regmap_read(rk_phy->reg_base,
> +		    rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
> +		    &dllrdy);
> +	dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK;
> +	if (dllrdy != PHYCTRL_DLLRDY_DONE) {
> +		pr_err("rockchip_emmc_phy_power: dllrdy timeout.\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	return 0;
> +}
> +
> +static int rockchip_emmc_phy_power_off(struct phy *phy)
> +{
> +	struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
> +	int ret = 0;
> +
> +	if (rk_phy->state == PHYCTRL_PDB_PWR_OFF)
> +		return 0;
> +
> +	/* Power down emmc phy analog blocks */
> +	ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_OFF);
> +	if (ret)
> +		return ret;
> +
> +	rk_phy->state = PHYCTRL_PDB_PWR_OFF;
> +
> +	return 0;
> +}
> +
> +static int rockchip_emmc_phy_power_on(struct phy *phy)
> +{
> +	struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
> +	int ret = 0;
> +
> +	if (rk_phy->state == PHYCTRL_PDB_PWR_ON)
> +		return 0;
> +
> +	/* Power up emmc phy analog blocks */
> +	ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_ON);
> +	if (ret)
> +		return ret;
> +
> +	rk_phy->state = PHYCTRL_PDB_PWR_ON;
> +
> +	return 0;
> +}
> +
> +static int rockchip_emmc_phy_init(struct phy *phy)
> +{
> +	rockchip_emmc_phy_power_on(phy);

do only phy initialization here, power on can be done later.
> +	return 0;
> +}
> +
> +static int rockchip_emmc_phy_exit(struct phy *phy)
> +{
> +	rockchip_emmc_phy_power_off(phy);

same here.

Thanks
Kishon


From jeffy.chen at rock-chips.com  Sun Jan  3 23:50:17 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Mon,  4 Jan 2016 15:50:17 +0800
Subject: [PATCH v1 0/6] rockchip: kylin: Boot with android boot image
Message-ID: <1451893823-20309-1-git-send-email-jeffy.chen@rock-chips.com>

We are porting android to kylin board now.
This series could let it boot up with android's boot image.


Jeffy Chen (6):
  common/image-fdt.c: Make boot_get_fdt() perform a check for Android
    images
  ARM: bootm: Try to use relocated ramdisk
  rockchip: rk3036: Bind GPIO banks
  rockchip: kylin: Add default gpt partition table
  rockchip: kylin: Enable boot with android boot image
  rockchip: kylin: Check fastboot request

 arch/arm/lib/bootm.c                      |  5 ++-
 board/kylin/kylin_rk3036/kylin_rk3036.c   | 32 ++++++++++++++++++
 common/image-fdt.c                        |  4 +++
 drivers/pinctrl/rockchip/pinctrl_rk3036.c |  8 +++++
 include/configs/kylin_rk3036.h            | 55 +++++++++++++++++++++++++++++++
 5 files changed, 103 insertions(+), 1 deletion(-)

-- 
2.1.4




From jeffy.chen at rock-chips.com  Sun Jan  3 23:50:19 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Mon,  4 Jan 2016 15:50:19 +0800
Subject: [PATCH v1 2/6] ARM: bootm: Try to use relocated ramdisk
In-Reply-To: <1451893823-20309-1-git-send-email-jeffy.chen@rock-chips.com>
References: <1451893823-20309-1-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <1451893823-20309-3-git-send-email-jeffy.chen@rock-chips.com>

After boot_ramdisk_high(), ramdisk would be relocated to
initrd_start & initrd_end, so use them instead of rd_start & rd_end.

Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
---

 arch/arm/lib/bootm.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c
index a477cae..ac94995 100644
--- a/arch/arm/lib/bootm.c
+++ b/arch/arm/lib/bootm.c
@@ -225,7 +225,10 @@ static void boot_prep_linux(bootm_headers_t *images)
 		if (BOOTM_ENABLE_MEMORY_TAGS)
 			setup_memory_tags(gd->bd);
 		if (BOOTM_ENABLE_INITRD_TAG) {
-			if (images->rd_start && images->rd_end) {
+			if (images->initrd_start && images->initrd_end) {
+				setup_initrd_tag(gd->bd, images->initrd_start,
+						 images->initrd_end);
+			} else if (images->rd_start && images->rd_end) {
 				setup_initrd_tag(gd->bd, images->rd_start,
 						 images->rd_end);
 			}
-- 
2.1.4




From jeffy.chen at rock-chips.com  Sun Jan  3 23:50:18 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Mon,  4 Jan 2016 15:50:18 +0800
Subject: [PATCH v1 1/6] common/image-fdt.c: Make boot_get_fdt() perform a
 check for Android images
In-Reply-To: <1451893823-20309-1-git-send-email-jeffy.chen@rock-chips.com>
References: <1451893823-20309-1-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <1451893823-20309-2-git-send-email-jeffy.chen@rock-chips.com>

Android images don't have a fdt.

Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
---

 common/image-fdt.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/common/image-fdt.c b/common/image-fdt.c
index 5e4e5bd..41aaa0d 100644
--- a/common/image-fdt.c
+++ b/common/image-fdt.c
@@ -379,6 +379,10 @@ int boot_get_fdt(int flag, int argc, char * const argv[], uint8_t arch,
 				       (long)fdt_addr);
 			}
 			break;
+#ifdef CONFIG_ANDROID_BOOT_IMAGE
+		case IMAGE_FORMAT_ANDROID:
+			goto no_fdt;
+#endif
 		default:
 			puts("ERROR: Did not find a cmdline Flattened Device Tree\n");
 			goto no_fdt;
-- 
2.1.4




From jeffy.chen at rock-chips.com  Sun Jan  3 23:50:20 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Mon,  4 Jan 2016 15:50:20 +0800
Subject: [PATCH v1 3/6] rockchip: rk3036: Bind GPIO banks
In-Reply-To: <1451893823-20309-1-git-send-email-jeffy.chen@rock-chips.com>
References: <1451893823-20309-1-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <1451893823-20309-4-git-send-email-jeffy.chen@rock-chips.com>

Call dm_scan_fdt_node() in rk3036 pinctrl uclass binding.

Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
---

 drivers/pinctrl/rockchip/pinctrl_rk3036.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/pinctrl/rockchip/pinctrl_rk3036.c b/drivers/pinctrl/rockchip/pinctrl_rk3036.c
index 581b096..1f78bf8 100644
--- a/drivers/pinctrl/rockchip/pinctrl_rk3036.c
+++ b/drivers/pinctrl/rockchip/pinctrl_rk3036.c
@@ -15,6 +15,7 @@
 #include <asm/arch/hardware.h>
 #include <asm/arch/periph.h>
 #include <dm/pinctrl.h>
+#include <dm/root.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -252,6 +253,12 @@ static struct pinctrl_ops rk3036_pinctrl_ops = {
 	.get_periph_id	= rk3036_pinctrl_get_periph_id,
 };
 
+static int rk3036_pinctrl_bind(struct udevice *dev)
+{
+	/* scan child GPIO banks */
+	return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false);
+}
+
 static int rk3036_pinctrl_probe(struct udevice *dev)
 {
 	struct rk3036_pinctrl_priv *priv = dev_get_priv(dev);
@@ -272,5 +279,6 @@ U_BOOT_DRIVER(pinctrl_rk3036) = {
 	.of_match	= rk3036_pinctrl_ids,
 	.priv_auto_alloc_size = sizeof(struct rk3036_pinctrl_priv),
 	.ops		= &rk3036_pinctrl_ops,
+	.bind		= rk3036_pinctrl_bind,
 	.probe		= rk3036_pinctrl_probe,
 };
-- 
2.1.4




From jeffy.chen at rock-chips.com  Sun Jan  3 23:50:22 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Mon,  4 Jan 2016 15:50:22 +0800
Subject: [PATCH v1 5/6] rockchip: kylin: Enable boot with android boot image
In-Reply-To: <1451893823-20309-1-git-send-email-jeffy.chen@rock-chips.com>
References: <1451893823-20309-1-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <1451893823-20309-6-git-send-email-jeffy.chen@rock-chips.com>

The android kernel is using appended dtb by default, and store
ramdisk right after kernel & dtb.
So we needs to relocate ramdisk, and use atags to pass params.

Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
---

 include/configs/kylin_rk3036.h | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/include/configs/kylin_rk3036.h b/include/configs/kylin_rk3036.h
index b750b26..49997ec 100644
--- a/include/configs/kylin_rk3036.h
+++ b/include/configs/kylin_rk3036.h
@@ -35,6 +35,29 @@
 #undef CONFIG_EXTRA_ENV_SETTINGS
 #define CONFIG_EXTRA_ENV_SETTINGS \
 	"partitions=" PARTS_DEFAULT \
+	"mmcdev=0\0" \
+	"mmcpart=5\0" \
+	"loadaddr=" __stringify(CONFIG_SYS_LOAD_ADDR) "\0" \
+
+#define CONFIG_ANDROID_BOOT_IMAGE
+#define CONFIG_SYS_BOOT_RAMDISK_HIGH
+#define CONFIG_SYS_HUSH_PARSER
+
+#undef CONFIG_BOOTCOMMAND
+#define CONFIG_BOOTCOMMAND \
+	"mmc dev ${mmcdev}; if mmc rescan; then " \
+		"part start mmc ${mmcdev} ${mmcpart} boot_start;" \
+		"part size mmc ${mmcdev} ${mmcpart} boot_size;" \
+		"mmc read ${loadaddr} ${boot_start} ${boot_size};" \
+		"bootm start ${loadaddr}; bootm ramdisk;" \
+		"bootm prep; bootm go;" \
+	"fi;" \
+
+/* Enable atags */
+#define CONFIG_SYS_BOOTPARAMS_LEN	(64*1024)
+#define CONFIG_INITRD_TAG
+#define CONFIG_SETUP_MEMORY_TAGS
+#define CONFIG_CMDLINE_TAG
 
 #endif
 
-- 
2.1.4




From jeffy.chen at rock-chips.com  Sun Jan  3 23:50:21 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Mon,  4 Jan 2016 15:50:21 +0800
Subject: [PATCH v1 4/6] rockchip: kylin: Add default gpt partition table
In-Reply-To: <1451893823-20309-1-git-send-email-jeffy.chen@rock-chips.com>
References: <1451893823-20309-1-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <1451893823-20309-5-git-send-email-jeffy.chen@rock-chips.com>

Add default android gpt partition table for kylin board.

Use "gpt write mmc 0 $partitions" to apply.

Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
---

 include/configs/kylin_rk3036.h | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/include/configs/kylin_rk3036.h b/include/configs/kylin_rk3036.h
index aa07889..b750b26 100644
--- a/include/configs/kylin_rk3036.h
+++ b/include/configs/kylin_rk3036.h
@@ -9,4 +9,33 @@
 
 #include <configs/rk3036_common.h>
 
+#ifndef CONFIG_SPL_BUILD
+
+/* Enable gpt partition table */
+#define CONFIG_CMD_GPT
+#define CONFIG_RANDOM_UUID
+#define CONFIG_EFI_PARTITION
+#define PARTS_DEFAULT \
+	"uuid_disk=${uuid_gpt_disk};" \
+	"name=loader,start=32K,size=4000K,uuid=${uuid_gpt_loader};" \
+	"name=reserved,size=64K,uuid=${uuid_gpt_reserved};" \
+	"name=misc,size=4M,uuid=${uuid_gpt_misc};" \
+	"name=recovery,size=32M,uuid=${uuid_gpt_recovery};" \
+	"name=boot_a,size=32M,uuid=${uuid_gpt_boot_a};" \
+	"name=boot_b,size=32M,uuid=${uuid_gpt_boot_b};" \
+	"name=system_a,size=818M,uuid=${uuid_gpt_system_a};" \
+	"name=system_b,size=818M,uuid=${uuid_gpt_system_b};" \
+	"name=vendor_a,size=50M,uuid=${uuid_gpt_vendor_a};" \
+	"name=vendor_b,size=50M,uuid=${uuid_gpt_vendor_b};" \
+	"name=cache,size=100M,uuid=${uuid_gpt_cache};" \
+	"name=metadata,size=16M,uuid=${uuid_gpt_metadata};" \
+	"name=persist,size=4M,uuid=${uuid_gpt_persist};" \
+	"name=userdata,size=-,uuid=${uuid_gpt_userdata};\0" \
+
+#undef CONFIG_EXTRA_ENV_SETTINGS
+#define CONFIG_EXTRA_ENV_SETTINGS \
+	"partitions=" PARTS_DEFAULT \
+
+#endif
+
 #endif
-- 
2.1.4




From jeffy.chen at rock-chips.com  Sun Jan  3 23:50:23 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Mon,  4 Jan 2016 15:50:23 +0800
Subject: [PATCH v1 6/6] rockchip: kylin: Check fastboot request
In-Reply-To: <1451893823-20309-1-git-send-email-jeffy.chen@rock-chips.com>
References: <1451893823-20309-1-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <1451893823-20309-7-git-send-email-jeffy.chen@rock-chips.com>

We will save boot mode flag in grf's os_reg[0], if fastboot
requested or fastboot key pressed, try to enter fastboot mode
at preboot stage.

Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
---

 board/kylin/kylin_rk3036/kylin_rk3036.c | 32 ++++++++++++++++++++++++++++++++
 include/configs/kylin_rk3036.h          |  3 +++
 2 files changed, 35 insertions(+)

diff --git a/board/kylin/kylin_rk3036/kylin_rk3036.c b/board/kylin/kylin_rk3036/kylin_rk3036.c
index 40d6b52..df2c123 100644
--- a/board/kylin/kylin_rk3036/kylin_rk3036.c
+++ b/board/kylin/kylin_rk3036/kylin_rk3036.c
@@ -8,10 +8,15 @@
 #include <dm.h>
 #include <asm/io.h>
 #include <asm/arch/uart.h>
+#include <asm/arch-rockchip/grf_rk3036.h>
 #include <asm/arch/sdram_rk3036.h>
+#include <asm/gpio.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
+#define GRF_BASE	0x20008000
+static struct rk3036_grf * const grf = (void *)GRF_BASE;
+
 void get_ddr_config(struct rk3036_ddr_config *config)
 {
 	/* K4B4G1646Q config */
@@ -28,6 +33,33 @@ void get_ddr_config(struct rk3036_ddr_config *config)
 	config->bw = 1;
 }
 
+#define FASTBOOT_KEY_GPIO 93
+#define ROCKCHIP_BOOT_MODE_NORMAL	0
+#define ROCKCHIP_BOOT_MODE_FASTBOOT	1
+
+int fastboot_key_pressed(void)
+{
+	gpio_request(FASTBOOT_KEY_GPIO, "fastboot_key");
+	gpio_direction_input(FASTBOOT_KEY_GPIO);
+	return !gpio_get_value(FASTBOOT_KEY_GPIO);
+}
+
+int board_late_init(void)
+{
+	int boot_mode = readl(&grf->os_reg[0]);
+
+	/* Clear boot mode */
+	writel(ROCKCHIP_BOOT_MODE_NORMAL, &grf->os_reg[0]);
+
+	if (boot_mode == ROCKCHIP_BOOT_MODE_FASTBOOT ||
+	    fastboot_key_pressed()) {
+		printf("enter fastboot!\n");
+		setenv("preboot", "setenv preboot; fastboot usb0");
+	}
+
+	return 0;
+}
+
 int board_init(void)
 {
 	return 0;
diff --git a/include/configs/kylin_rk3036.h b/include/configs/kylin_rk3036.h
index 49997ec..424e81b 100644
--- a/include/configs/kylin_rk3036.h
+++ b/include/configs/kylin_rk3036.h
@@ -39,6 +39,9 @@
 	"mmcpart=5\0" \
 	"loadaddr=" __stringify(CONFIG_SYS_LOAD_ADDR) "\0" \
 
+#define CONFIG_BOARD_LATE_INIT
+#define CONFIG_PREBOOT
+
 #define CONFIG_ANDROID_BOOT_IMAGE
 #define CONFIG_SYS_BOOT_RAMDISK_HIGH
 #define CONFIG_SYS_HUSH_PARSER
-- 
2.1.4




From jeffy.chen at rock-chips.com  Sun Jan  3 23:54:46 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Mon,  4 Jan 2016 15:54:46 +0800
Subject: [PATCH v2] serial: core: remove baud_rates when serial console setup
Message-ID: <1451894086-20435-1-git-send-email-jeffy.chen@rock-chips.com>

Currently, when tring to set up a serial console with a higher
baud rate, it would fallback to 921600.

Tested-by: Jianqun Xu <jay.xu at rock-chips.com>
Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>

---

Changes in v2:
remove baud_rates as Alan's suggestion.

 drivers/tty/serial/serial_core.c | 33 ++-------------------------------
 1 file changed, 2 insertions(+), 31 deletions(-)

diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index def5199..150547c 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -1892,26 +1892,6 @@ uart_parse_options(char *options, int *baud, int *parity, int *bits, int *flow)
 }
 EXPORT_SYMBOL_GPL(uart_parse_options);
 
-struct baud_rates {
-	unsigned int rate;
-	unsigned int cflag;
-};
-
-static const struct baud_rates baud_rates[] = {
-	{ 921600, B921600 },
-	{ 460800, B460800 },
-	{ 230400, B230400 },
-	{ 115200, B115200 },
-	{  57600, B57600  },
-	{  38400, B38400  },
-	{  19200, B19200  },
-	{   9600, B9600   },
-	{   4800, B4800   },
-	{   2400, B2400   },
-	{   1200, B1200   },
-	{      0, B38400  }
-};
-
 /**
  *	uart_set_options - setup the serial console parameters
  *	@port: pointer to the serial ports uart_port structure
@@ -1927,7 +1907,6 @@ uart_set_options(struct uart_port *port, struct console *co,
 {
 	struct ktermios termios;
 	static struct ktermios dummy;
-	int i;
 
 	/*
 	 * Ensure that the serial console lock is initialised
@@ -1942,16 +1921,8 @@ uart_set_options(struct uart_port *port, struct console *co,
 
 	memset(&termios, 0, sizeof(struct ktermios));
 
-	termios.c_cflag = CREAD | HUPCL | CLOCAL;
-
-	/*
-	 * Construct a cflag setting.
-	 */
-	for (i = 0; baud_rates[i].rate; i++)
-		if (baud_rates[i].rate <= baud)
-			break;
-
-	termios.c_cflag |= baud_rates[i].cflag;
+	termios.c_cflag |= CREAD | HUPCL | CLOCAL;
+	tty_termios_encode_baud_rate(&termios, baud, baud);
 
 	if (bits == 7)
 		termios.c_cflag |= CS7;
-- 
2.1.4




From shawn.lin at rock-chips.com  Mon Jan  4 00:36:30 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Mon, 4 Jan 2016 16:36:30 +0800
Subject: [PATCH 2/2] phy: add a driver for the Rockchip SoC internal eMMC
 PHY
In-Reply-To: <568A230F.3000605@ti.com>
References: <1451353928-23973-1-git-send-email-shawn.lin@rock-chips.com>
 <568A230F.3000605@ti.com>
Message-ID: <568A2F0E.8070608@rock-chips.com>

Hi Kishon,
On 2016/1/4 15:45, Kishon Vijay Abraham I wrote:
> Hi,
>
> On Tuesday 29 December 2015 07:22 AM, Shawn Lin wrote:
>> This patch to add a generic PHY driver for ROCKCHIP eMMC PHY.

[...]

>> +
>> +struct rockchip_emmc_phy {
>> +	unsigned int	reg_offset;
>> +	struct regmap	*reg_base;
>> +	struct phy	*phy;
>
> The phy looks unnecessary.

right, got it.

>> +	bool		state;
>
> hmm.. I want to have some sort of state machine in phy core so that individual
> PHY drivers don't have to maintain the state. However I'm not sure if all the
> PHY's will require such mechanism.
>

In general, phy core is the best place to maintain it.
Presumably, phy driver maintainer is much likely to
maintain the corresponding caller driver at the same time, so he/she
should be in charge of maintaining the on/off, init/exit pairs. From
this point, it doesn't need the state machine. But, I'm not sure if the
caller driver will always keep the on/off pairs correct.


Anyway, I will remove it from this driver. And may we request a RFC
for all sub-phy drivers to discuss this issue if we want to add the
state machine into phy core. How about?


>> +};
>> +
>> +static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy,

[...]

>> +}
>> +
>> +static int rockchip_emmc_phy_init(struct phy *phy)
>> +{
>> +	rockchip_emmc_phy_power_on(phy);
>
> do only phy initialization here, power on can be done later.

yep.

>> +	return 0;
>> +}
>> +
>> +static int rockchip_emmc_phy_exit(struct phy *phy)
>> +{
>> +	rockchip_emmc_phy_power_off(phy);
>
> same here.
>
> Thanks
> Kishon
>
>
>


-- 
Best Regards
Shawn Lin



From sjoerd.simons at collabora.co.uk  Mon Jan  4 01:10:29 2016
From: sjoerd.simons at collabora.co.uk (Sjoerd Simons)
Date: Mon, 04 Jan 2016 10:10:29 +0100
Subject: [PATCH v2] ARM: dts: rockchip: Adding LEDs handling via
 leds-gpio for the Radxa Rock2 Square
In-Reply-To: <1451553232-2533-1-git-send-email-romain.perier@gmail.com>
References: <1451553232-2533-1-git-send-email-romain.perier@gmail.com>
Message-ID: <1451898629.3269.11.camel@collabora.co.uk>

On Thu, 2015-12-31 at 10:13 +0100, Romain Perier wrote:
> Describe the two user-controllable LEDs on Rock2 Square boards.
> All information have been retrieved from the schematics and the
> vendor
> devicetree. The default-triggers mimic the behaviour of the vendor-
> kernel to
> keep functionalities in sync.
> 
> Signed-off-by: Romain Perier <romain.perier at gmail.com>

Reviewed-by: Sjoerd Simons <sjoerd.simons at collabora.co.uk>

> ---
> 
> Changes in v2:
> - Changed labels according to what is defined in the schematics
> - Added a long commit message describing this patch in details
> 
> ?arch/arm/boot/dts/rk3288-rock2-square.dts | 17 +++++++++++++++++
> ?1 file changed, 17 insertions(+)
> 
> diff --git a/arch/arm/boot/dts/rk3288-rock2-square.dts
> b/arch/arm/boot/dts/rk3288-rock2-square.dts
> index c5453a0..0813428 100644
> --- a/arch/arm/boot/dts/rk3288-rock2-square.dts
> +++ b/arch/arm/boot/dts/rk3288-rock2-square.dts
> @@ -56,6 +56,23 @@
> ?		pinctrl-0 = <&ir_int>;
> ?	};
> ?
> +	gpio-leds {
> +		compatible = "gpio-leds";
> +
> +		heartbeat {
> +			gpios = <&gpio7 15 GPIO_ACTIVE_LOW>;
> +			label = "rock2:green:state1";
> +			linux,default-trigger = "heartbeat";
> +		};
> +
> +		mmc {
> +			gpios = <&gpio0 11 GPIO_ACTIVE_LOW>;
> +			label = "rock2:blue:state2";
> +			linux,default-trigger = "mmc0";
> +		};
> +	};
> +
> +
> ?	sound {
> ?		compatible = "simple-audio-card";
> ?		simple-audio-card,name = "SPDIF";

-- 
Sjoerd Simons
Collabora Ltd.


From kever.yang at rock-chips.com  Mon Jan  4 01:14:04 2016
From: kever.yang at rock-chips.com (Kever Yang)
Date: Mon, 04 Jan 2016 17:14:04 +0800
Subject: [RESEND PATCH] rtc: rk808: rename rtc-rk808.c to rtc-rk8xx.c
In-Reply-To: <1451571295-4193-1-git-send-email-zhangqing@rock-chips.com>
References: <1451571295-4193-1-git-send-email-zhangqing@rock-chips.com>
Message-ID: <568A37DC.40208@rock-chips.com>

Hi Zhang Qing,

On 12/31/2015 10:14 PM, zhangqing wrote:
> make rtc-rk8xx.c compatible for all pmic chips.
> for pmic chips(rk808\rk807\rk816\rk818) in the future.
The commit message will be better like this:
Rename the file to rtc-rk8xx.c to compatible other Rockchip PMIC chips
like rk807/rk816/rk818 and other chips in the the future.

Also rename the variable, MACRO, function and etc. names in relate file.

<You'd better share which chips have you test with new driver here.>
> Signed-off-by: zhangqing <zhangqing at rock-chips.com>
Prefer to use: Zhang Qing <zhangqing at rock-chips.com >
> ---
>   drivers/mfd/rk808.c                      |   2 +-
>   drivers/rtc/Kconfig                      |   8 +-
>   drivers/rtc/Makefile                     |   2 +-
>   drivers/rtc/{rtc-rk808.c => rtc-rk8xx.c} | 218 ++++++++++++++++++-------------
>   4 files changed, 131 insertions(+), 99 deletions(-)
>   rename drivers/rtc/{rtc-rk808.c => rtc-rk8xx.c} (64%)
>
> diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c
> index 4b1e439..21da22b 100644
> --- a/drivers/mfd/rk808.c
> +++ b/drivers/mfd/rk808.c
> @@ -77,7 +77,7 @@ static const struct mfd_cell rk808s[] = {
>   	{ .name = "rk808-clkout", },
>   	{ .name = "rk808-regulator", },
>   	{
> -		.name = "rk808-rtc",
> +		.name = "rk8xx-rtc",
>   		.num_resources = ARRAY_SIZE(rtc_resources),
>   		.resources = &rtc_resources[0],
>   	},
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 376322f..d669473d 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -325,15 +325,15 @@ config RTC_DRV_MAX77686
>   	  This driver can also be built as a module. If so, the module
>   	  will be called rtc-max77686.
>   
> -config RTC_DRV_RK808
> -	tristate "Rockchip RK808 RTC"
> +config RTC_DRV_RK8XX
> +	tristate "Rockchip RK8XX RTC"
>   	depends on MFD_RK808
>   	help
>   	  If you say yes here you will get support for the
> -	  RTC of RK808 PMIC.
> +	  RTC of RK8XX PMIC.
>   
>   	  This driver can also be built as a module. If so, the module
> -	  will be called rk808-rtc.
> +	  will be called rk8xx-rtc.
>   
>   config RTC_DRV_MAX77802
>   	tristate "Maxim 77802 RTC"
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 62d61b2..5b1384a 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -120,7 +120,7 @@ obj-$(CONFIG_RTC_DRV_PUV3)	+= rtc-puv3.o
>   obj-$(CONFIG_RTC_DRV_PXA)	+= rtc-pxa.o
>   obj-$(CONFIG_RTC_DRV_R9701)	+= rtc-r9701.o
>   obj-$(CONFIG_RTC_DRV_RC5T583)	+= rtc-rc5t583.o
> -obj-$(CONFIG_RTC_DRV_RK808)	+= rtc-rk808.o
> +obj-$(CONFIG_RTC_DRV_RK8XX)	+= rtc-rk8xx.o
>   obj-$(CONFIG_RTC_DRV_RP5C01)	+= rtc-rp5c01.o
>   obj-$(CONFIG_RTC_DRV_RS5C313)	+= rtc-rs5c313.o
>   obj-$(CONFIG_RTC_DRV_RS5C348)	+= rtc-rs5c348.o
> diff --git a/drivers/rtc/rtc-rk808.c b/drivers/rtc/rtc-rk8xx.c
> similarity index 64%
> rename from drivers/rtc/rtc-rk808.c
> rename to drivers/rtc/rtc-rk8xx.c
> index 35c9aad..5d946bf 100644
> --- a/drivers/rtc/rtc-rk808.c
> +++ b/drivers/rtc/rtc-rk8xx.c
> @@ -1,5 +1,5 @@
>   /*
> - * RTC driver for Rockchip RK808
> + * RTC driver for Rockchip RK8XX
>    *
>    * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
>    *
> @@ -20,14 +20,33 @@
>   #include <linux/kernel.h>
>   #include <linux/rtc.h>
>   #include <linux/bcd.h>
> -#include <linux/mfd/rk808.h>
>   #include <linux/platform_device.h>
>   #include <linux/i2c.h>
> +#include <linux/regmap.h>
> +
> +#define RK8XX_SECONDS_REG			0x00
> +#define RK8XX_MINUTES_REG			0x01
> +#define RK8XX_HOURS_REG				0x02
> +#define RK8XX_DAYS_REG				0x03
> +#define RK8XX_MONTHS_REG			0x04
> +#define RK8XX_YEARS_REG				0x05
> +#define RK8XX_WEEKS_REG				0x06
> +#define RK8XX_ALARM_SECONDS_REG			0x08
> +#define RK8XX_ALARM_MINUTES_REG			0x09
> +#define RK8XX_ALARM_HOURS_REG			0x0A
> +#define RK8XX_ALARM_DAYS_REG			0x0B
> +#define RK8XX_ALARM_MONTHS_REG			0x0C
> +#define RK8XX_ALARM_YEARS_REG			0x0D
> +#define RK8XX_RTC_CTRL_REG			0x10
> +#define RK8XX_RTC_STATUS_REG			0x11
> +#define RK8XX_RTC_INT_REG			0x12
> +#define RK8XX_RTC_COMP_LSB_REG			0x13
> +#define RK8XX_RTC_COMP_MSB_REG			0x14
>   
>   /* RTC_CTRL_REG bitfields */
>   #define BIT_RTC_CTRL_REG_STOP_RTC_M		BIT(0)
>   
> -/* RK808 has a shadowed register for saving a "frozen" RTC time.
> +/* RK8xx has a shadowed register for saving a "frozen" RTC time.
>    * When user setting "GET_TIME" to 1, the time will save in this shadowed
>    * register. If set "READSEL" to 1, user read rtc time register, actually
>    * get the time of that moment. If we need the real time, clr this bit.
> @@ -47,17 +66,25 @@
>   
>   /* REG_SECONDS_REG through REG_YEARS_REG is how many registers? */
>   
> -#define NUM_TIME_REGS	(RK808_WEEKS_REG - RK808_SECONDS_REG + 1)
> -#define NUM_ALARM_REGS	(RK808_ALARM_YEARS_REG - RK808_ALARM_SECONDS_REG + 1)
> +#define NUM_TIME_REGS	(RK8XX_WEEKS_REG - RK8XX_SECONDS_REG + 1)
> +#define NUM_ALARM_REGS	(RK8XX_ALARM_YEARS_REG - RK8XX_ALARM_SECONDS_REG + 1)
>   
> -struct rk808_rtc {
> -	struct rk808 *rk808;
> +static const struct regmap_config rk8xx_rtc_regmap_config = {
> +	.reg_bits = 8,
> +	.val_bits = 8,
> +	.max_register = RK8XX_RTC_COMP_MSB_REG,
> +	.cache_type = REGCACHE_RBTREE,
> +};
> +
> +struct rk8xx_rtc {
>   	struct rtc_device *rtc;
> +	struct i2c_client *i2c;
> +	struct regmap *regmap;
Maybe you need some comment here or in commit message for this change,
seems this is more than "rename".

Thanks,
- Kever



From alexandre.belloni at free-electrons.com  Mon Jan  4 01:45:46 2016
From: alexandre.belloni at free-electrons.com (Alexandre Belloni)
Date: Mon, 4 Jan 2016 10:45:46 +0100
Subject: [RESEND PATCH] rtc: rk808: rename rtc-rk808.c to rtc-rk8xx.c
In-Reply-To: <1451571295-4193-1-git-send-email-zhangqing@rock-chips.com>
References: <1451571295-4193-1-git-send-email-zhangqing@rock-chips.com>
Message-ID: <20160104094546.GB32724@piout.net>

Hi,

On 31/12/2015 at 06:14:55 -0800, zhangqing wrote :
> make rtc-rk8xx.c compatible for all pmic chips.
> for pmic chips(rk808\rk807\rk816\rk818) in the future.
> 

I'm not sure it is useful to do that renaming. It is usual to have one
driver that supports multiple chips named with the forst chip it
supported.

Also, what would happen if for example rk855 is not compatible at all
with the previous implementations?

> Signed-off-by: zhangqing <zhangqing at rock-chips.com>
> ---
>  drivers/mfd/rk808.c                      |   2 +-
>  drivers/rtc/Kconfig                      |   8 +-
>  drivers/rtc/Makefile                     |   2 +-
>  drivers/rtc/{rtc-rk808.c => rtc-rk8xx.c} | 218 ++++++++++++++++++-------------
>  4 files changed, 131 insertions(+), 99 deletions(-)
>  rename drivers/rtc/{rtc-rk808.c => rtc-rk8xx.c} (64%)
> 
> diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c
> index 4b1e439..21da22b 100644
> --- a/drivers/mfd/rk808.c
> +++ b/drivers/mfd/rk808.c
> @@ -77,7 +77,7 @@ static const struct mfd_cell rk808s[] = {
>  	{ .name = "rk808-clkout", },
>  	{ .name = "rk808-regulator", },
>  	{
> -		.name = "rk808-rtc",
> +		.name = "rk8xx-rtc",
>  		.num_resources = ARRAY_SIZE(rtc_resources),
>  		.resources = &rtc_resources[0],
>  	},
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 376322f..d669473d 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -325,15 +325,15 @@ config RTC_DRV_MAX77686
>  	  This driver can also be built as a module. If so, the module
>  	  will be called rtc-max77686.
>  
> -config RTC_DRV_RK808
> -	tristate "Rockchip RK808 RTC"
> +config RTC_DRV_RK8XX
> +	tristate "Rockchip RK8XX RTC"
>  	depends on MFD_RK808
>  	help
>  	  If you say yes here you will get support for the
> -	  RTC of RK808 PMIC.
> +	  RTC of RK8XX PMIC.
>  
>  	  This driver can also be built as a module. If so, the module
> -	  will be called rk808-rtc.
> +	  will be called rk8xx-rtc.
>  
>  config RTC_DRV_MAX77802
>  	tristate "Maxim 77802 RTC"
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 62d61b2..5b1384a 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -120,7 +120,7 @@ obj-$(CONFIG_RTC_DRV_PUV3)	+= rtc-puv3.o
>  obj-$(CONFIG_RTC_DRV_PXA)	+= rtc-pxa.o
>  obj-$(CONFIG_RTC_DRV_R9701)	+= rtc-r9701.o
>  obj-$(CONFIG_RTC_DRV_RC5T583)	+= rtc-rc5t583.o
> -obj-$(CONFIG_RTC_DRV_RK808)	+= rtc-rk808.o
> +obj-$(CONFIG_RTC_DRV_RK8XX)	+= rtc-rk8xx.o
>  obj-$(CONFIG_RTC_DRV_RP5C01)	+= rtc-rp5c01.o
>  obj-$(CONFIG_RTC_DRV_RS5C313)	+= rtc-rs5c313.o
>  obj-$(CONFIG_RTC_DRV_RS5C348)	+= rtc-rs5c348.o
> diff --git a/drivers/rtc/rtc-rk808.c b/drivers/rtc/rtc-rk8xx.c
> similarity index 64%
> rename from drivers/rtc/rtc-rk808.c
> rename to drivers/rtc/rtc-rk8xx.c
> index 35c9aad..5d946bf 100644
> --- a/drivers/rtc/rtc-rk808.c
> +++ b/drivers/rtc/rtc-rk8xx.c
> @@ -1,5 +1,5 @@
>  /*
> - * RTC driver for Rockchip RK808
> + * RTC driver for Rockchip RK8XX
>   *
>   * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
>   *
> @@ -20,14 +20,33 @@
>  #include <linux/kernel.h>
>  #include <linux/rtc.h>
>  #include <linux/bcd.h>
> -#include <linux/mfd/rk808.h>
>  #include <linux/platform_device.h>
>  #include <linux/i2c.h>
> +#include <linux/regmap.h>
> +
> +#define RK8XX_SECONDS_REG			0x00
> +#define RK8XX_MINUTES_REG			0x01
> +#define RK8XX_HOURS_REG				0x02
> +#define RK8XX_DAYS_REG				0x03
> +#define RK8XX_MONTHS_REG			0x04
> +#define RK8XX_YEARS_REG				0x05
> +#define RK8XX_WEEKS_REG				0x06
> +#define RK8XX_ALARM_SECONDS_REG			0x08
> +#define RK8XX_ALARM_MINUTES_REG			0x09
> +#define RK8XX_ALARM_HOURS_REG			0x0A
> +#define RK8XX_ALARM_DAYS_REG			0x0B
> +#define RK8XX_ALARM_MONTHS_REG			0x0C
> +#define RK8XX_ALARM_YEARS_REG			0x0D
> +#define RK8XX_RTC_CTRL_REG			0x10
> +#define RK8XX_RTC_STATUS_REG			0x11
> +#define RK8XX_RTC_INT_REG			0x12
> +#define RK8XX_RTC_COMP_LSB_REG			0x13
> +#define RK8XX_RTC_COMP_MSB_REG			0x14
>  
>  /* RTC_CTRL_REG bitfields */
>  #define BIT_RTC_CTRL_REG_STOP_RTC_M		BIT(0)
>  
> -/* RK808 has a shadowed register for saving a "frozen" RTC time.
> +/* RK8xx has a shadowed register for saving a "frozen" RTC time.
>   * When user setting "GET_TIME" to 1, the time will save in this shadowed
>   * register. If set "READSEL" to 1, user read rtc time register, actually
>   * get the time of that moment. If we need the real time, clr this bit.
> @@ -47,17 +66,25 @@
>  
>  /* REG_SECONDS_REG through REG_YEARS_REG is how many registers? */
>  
> -#define NUM_TIME_REGS	(RK808_WEEKS_REG - RK808_SECONDS_REG + 1)
> -#define NUM_ALARM_REGS	(RK808_ALARM_YEARS_REG - RK808_ALARM_SECONDS_REG + 1)
> +#define NUM_TIME_REGS	(RK8XX_WEEKS_REG - RK8XX_SECONDS_REG + 1)
> +#define NUM_ALARM_REGS	(RK8XX_ALARM_YEARS_REG - RK8XX_ALARM_SECONDS_REG + 1)
>  
> -struct rk808_rtc {
> -	struct rk808 *rk808;
> +static const struct regmap_config rk8xx_rtc_regmap_config = {
> +	.reg_bits = 8,
> +	.val_bits = 8,
> +	.max_register = RK8XX_RTC_COMP_MSB_REG,
> +	.cache_type = REGCACHE_RBTREE,
> +};
> +
> +struct rk8xx_rtc {
>  	struct rtc_device *rtc;
> +	struct i2c_client *i2c;
> +	struct regmap *regmap;
>  	int irq;
>  };
>  
>  /*
> - * The Rockchip calendar used by the RK808 counts November with 31 days. We use
> + * The Rockchip calendar used by the RK8xx counts November with 31 days. We use
>   * these translation functions to convert its dates to/from the Gregorian
>   * calendar used by the rest of the world. We arbitrarily define Jan 1st, 2016
>   * as the day when both calendars were in sync, and treat all other dates
> @@ -74,6 +101,7 @@ static void rockchip_to_gregorian(struct rtc_time *tm)
>  {
>  	/* If it's Nov 31st, rtc_tm_to_time64() will count that like Dec 1st */
>  	time64_t time = rtc_tm_to_time64(tm);
> +
>  	rtc_time64_to_tm(time + nov2dec_transitions(tm) * 86400, tm);
>  }
>  
> @@ -81,6 +109,7 @@ static void gregorian_to_rockchip(struct rtc_time *tm)
>  {
>  	time64_t extra_days = nov2dec_transitions(tm);
>  	time64_t time = rtc_tm_to_time64(tm);
> +
>  	rtc_time64_to_tm(time - extra_days * 86400, tm);
>  
>  	/* Compensate if we went back over Nov 31st (will work up to 2381) */
> @@ -93,15 +122,14 @@ static void gregorian_to_rockchip(struct rtc_time *tm)
>  }
>  
>  /* Read current time and date in RTC */
> -static int rk808_rtc_readtime(struct device *dev, struct rtc_time *tm)
> +static int rk8xx_rtc_readtime(struct device *dev, struct rtc_time *tm)
>  {
> -	struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev);
> -	struct rk808 *rk808 = rk808_rtc->rk808;
> +	struct rk8xx_rtc *rk8xx_rtc = dev_get_drvdata(dev);
>  	u8 rtc_data[NUM_TIME_REGS];
>  	int ret;
>  
>  	/* Force an update of the shadowed registers right now */
> -	ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
> +	ret = regmap_update_bits(rk8xx_rtc->regmap, RK8XX_RTC_CTRL_REG,
>  				 BIT_RTC_CTRL_REG_RTC_GET_TIME,
>  				 BIT_RTC_CTRL_REG_RTC_GET_TIME);
>  	if (ret) {
> @@ -115,7 +143,7 @@ static int rk808_rtc_readtime(struct device *dev, struct rtc_time *tm)
>  	 * 32khz. If we clear the GET_TIME bit here, the time of i2c transfer
>  	 * certainly more than 31.25us: 16 * 2.5us at 400kHz bus frequency.
>  	 */
> -	ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
> +	ret = regmap_update_bits(rk8xx_rtc->regmap, RK8XX_RTC_CTRL_REG,
>  				 BIT_RTC_CTRL_REG_RTC_GET_TIME,
>  				 0);
>  	if (ret) {
> @@ -123,7 +151,7 @@ static int rk808_rtc_readtime(struct device *dev, struct rtc_time *tm)
>  		return ret;
>  	}
>  
> -	ret = regmap_bulk_read(rk808->regmap, RK808_SECONDS_REG,
> +	ret = regmap_bulk_read(rk8xx_rtc->regmap, RK8XX_SECONDS_REG,
>  			       rtc_data, NUM_TIME_REGS);
>  	if (ret) {
>  		dev_err(dev, "Failed to bulk read rtc_data: %d\n", ret);
> @@ -146,10 +174,9 @@ static int rk808_rtc_readtime(struct device *dev, struct rtc_time *tm)
>  }
>  
>  /* Set current time and date in RTC */
> -static int rk808_rtc_set_time(struct device *dev, struct rtc_time *tm)
> +static int rk8xx_rtc_set_time(struct device *dev, struct rtc_time *tm)
>  {
> -	struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev);
> -	struct rk808 *rk808 = rk808_rtc->rk808;
> +	struct rk8xx_rtc *rk8xx_rtc = dev_get_drvdata(dev);
>  	u8 rtc_data[NUM_TIME_REGS];
>  	int ret;
>  
> @@ -166,7 +193,7 @@ static int rk808_rtc_set_time(struct device *dev, struct rtc_time *tm)
>  	rtc_data[6] = bin2bcd(tm->tm_wday);
>  
>  	/* Stop RTC while updating the RTC registers */
> -	ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
> +	ret = regmap_update_bits(rk8xx_rtc->regmap, RK8XX_RTC_CTRL_REG,
>  				 BIT_RTC_CTRL_REG_STOP_RTC_M,
>  				 BIT_RTC_CTRL_REG_STOP_RTC_M);
>  	if (ret) {
> @@ -174,14 +201,14 @@ static int rk808_rtc_set_time(struct device *dev, struct rtc_time *tm)
>  		return ret;
>  	}
>  
> -	ret = regmap_bulk_write(rk808->regmap, RK808_SECONDS_REG,
> +	ret = regmap_bulk_write(rk8xx_rtc->regmap, RK8XX_SECONDS_REG,
>  				rtc_data, NUM_TIME_REGS);
>  	if (ret) {
>  		dev_err(dev, "Failed to bull write rtc_data: %d\n", ret);
>  		return ret;
>  	}
>  	/* Start RTC again */
> -	ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
> +	ret = regmap_update_bits(rk8xx_rtc->regmap, RK8XX_RTC_CTRL_REG,
>  				 BIT_RTC_CTRL_REG_STOP_RTC_M, 0);
>  	if (ret) {
>  		dev_err(dev, "Failed to update RTC control: %d\n", ret);
> @@ -191,15 +218,15 @@ static int rk808_rtc_set_time(struct device *dev, struct rtc_time *tm)
>  }
>  
>  /* Read alarm time and date in RTC */
> -static int rk808_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
> +static int rk8xx_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
>  {
> -	struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev);
> -	struct rk808 *rk808 = rk808_rtc->rk808;
> +	struct rk8xx_rtc *rk8xx_rtc = dev_get_drvdata(dev);
>  	u8 alrm_data[NUM_ALARM_REGS];
>  	uint32_t int_reg;
>  	int ret;
>  
> -	ret = regmap_bulk_read(rk808->regmap, RK808_ALARM_SECONDS_REG,
> +	ret = regmap_bulk_read(rk8xx_rtc->regmap,
> +			       RK8XX_ALARM_SECONDS_REG,
>  			       alrm_data, NUM_ALARM_REGS);
>  
>  	alrm->time.tm_sec = bcd2bin(alrm_data[0] & SECONDS_REG_MSK);
> @@ -210,7 +237,8 @@ static int rk808_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
>  	alrm->time.tm_year = (bcd2bin(alrm_data[5] & YEARS_REG_MSK)) + 100;
>  	rockchip_to_gregorian(&alrm->time);
>  
> -	ret = regmap_read(rk808->regmap, RK808_RTC_INT_REG, &int_reg);
> +	ret = regmap_read(rk8xx_rtc->regmap,
> +			  RK8XX_RTC_INT_REG, &int_reg);
>  	if (ret) {
>  		dev_err(dev, "Failed to read RTC INT REG: %d\n", ret);
>  		return ret;
> @@ -226,37 +254,34 @@ static int rk808_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
>  	return 0;
>  }
>  
> -static int rk808_rtc_stop_alarm(struct rk808_rtc *rk808_rtc)
> +static int rk8xx_rtc_stop_alarm(struct rk8xx_rtc *rk8xx_rtc)
>  {
> -	struct rk808 *rk808 = rk808_rtc->rk808;
>  	int ret;
>  
> -	ret = regmap_update_bits(rk808->regmap, RK808_RTC_INT_REG,
> +	ret = regmap_update_bits(rk8xx_rtc->regmap, RK8XX_RTC_INT_REG,
>  				 BIT_RTC_INTERRUPTS_REG_IT_ALARM_M, 0);
>  
>  	return ret;
>  }
>  
> -static int rk808_rtc_start_alarm(struct rk808_rtc *rk808_rtc)
> +static int rk8xx_rtc_start_alarm(struct rk8xx_rtc *rk8xx_rtc)
>  {
> -	struct rk808 *rk808 = rk808_rtc->rk808;
>  	int ret;
>  
> -	ret = regmap_update_bits(rk808->regmap, RK808_RTC_INT_REG,
> +	ret = regmap_update_bits(rk8xx_rtc->regmap, RK8XX_RTC_INT_REG,
>  				 BIT_RTC_INTERRUPTS_REG_IT_ALARM_M,
>  				 BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
>  
>  	return ret;
>  }
>  
> -static int rk808_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
> +static int rk8xx_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
>  {
> -	struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev);
> -	struct rk808 *rk808 = rk808_rtc->rk808;
> +	struct rk8xx_rtc *rk8xx_rtc = dev_get_drvdata(dev);
>  	u8 alrm_data[NUM_ALARM_REGS];
>  	int ret;
>  
> -	ret = rk808_rtc_stop_alarm(rk808_rtc);
> +	ret = rk8xx_rtc_stop_alarm(rk8xx_rtc);
>  	if (ret) {
>  		dev_err(dev, "Failed to stop alarm: %d\n", ret);
>  		return ret;
> @@ -274,14 +299,15 @@ static int rk808_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
>  	alrm_data[4] = bin2bcd(alrm->time.tm_mon + 1);
>  	alrm_data[5] = bin2bcd(alrm->time.tm_year - 100);
>  
> -	ret = regmap_bulk_write(rk808->regmap, RK808_ALARM_SECONDS_REG,
> +	ret = regmap_bulk_write(rk8xx_rtc->regmap,
> +				RK8XX_ALARM_SECONDS_REG,
>  				alrm_data, NUM_ALARM_REGS);
>  	if (ret) {
>  		dev_err(dev, "Failed to bulk write: %d\n", ret);
>  		return ret;
>  	}
>  	if (alrm->enabled) {
> -		ret = rk808_rtc_start_alarm(rk808_rtc);
> +		ret = rk8xx_rtc_start_alarm(rk8xx_rtc);
>  		if (ret) {
>  			dev_err(dev, "Failed to start alarm: %d\n", ret);
>  			return ret;
> @@ -290,15 +316,15 @@ static int rk808_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
>  	return 0;
>  }
>  
> -static int rk808_rtc_alarm_irq_enable(struct device *dev,
> +static int rk8xx_rtc_alarm_irq_enable(struct device *dev,
>  				      unsigned int enabled)
>  {
> -	struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev);
> +	struct rk8xx_rtc *rk8xx_rtc = dev_get_drvdata(dev);
>  
>  	if (enabled)
> -		return rk808_rtc_start_alarm(rk808_rtc);
> +		return rk8xx_rtc_start_alarm(rk8xx_rtc);
>  
> -	return rk808_rtc_stop_alarm(rk808_rtc);
> +	return rk8xx_rtc_stop_alarm(rk8xx_rtc);
>  }
>  
>  /*
> @@ -311,14 +337,13 @@ static int rk808_rtc_alarm_irq_enable(struct device *dev,
>   * bit 2: every hour
>   * bit 3: every day
>   */
> -static irqreturn_t rk808_alarm_irq(int irq, void *data)
> +static irqreturn_t rk8xx_alarm_irq(int irq, void *data)
>  {
> -	struct rk808_rtc *rk808_rtc = data;
> -	struct rk808 *rk808 = rk808_rtc->rk808;
> -	struct i2c_client *client = rk808->i2c;
> +	struct rk8xx_rtc *rk8xx_rtc = data;
> +	struct i2c_client *client = rk8xx_rtc->i2c;
>  	int ret;
>  
> -	ret = regmap_write(rk808->regmap, RK808_RTC_STATUS_REG,
> +	ret = regmap_write(rk8xx_rtc->regmap, RK8XX_RTC_STATUS_REG,
>  			   RTC_STATUS_MASK);
>  	if (ret) {
>  		dev_err(&client->dev,
> @@ -326,29 +351,29 @@ static irqreturn_t rk808_alarm_irq(int irq, void *data)
>  		return ret;
>  	}
>  
> -	rtc_update_irq(rk808_rtc->rtc, 1, RTC_IRQF | RTC_AF);
> +	rtc_update_irq(rk8xx_rtc->rtc, 1, RTC_IRQF | RTC_AF);
>  	dev_dbg(&client->dev,
> -		 "%s:irq=%d\n", __func__, irq);
> +		"%s:irq=%d\n", __func__, irq);
>  	return IRQ_HANDLED;
>  }
>  
> -static const struct rtc_class_ops rk808_rtc_ops = {
> -	.read_time = rk808_rtc_readtime,
> -	.set_time = rk808_rtc_set_time,
> -	.read_alarm = rk808_rtc_readalarm,
> -	.set_alarm = rk808_rtc_setalarm,
> -	.alarm_irq_enable = rk808_rtc_alarm_irq_enable,
> +static const struct rtc_class_ops rk8xx_rtc_ops = {
> +	.read_time = rk8xx_rtc_readtime,
> +	.set_time = rk8xx_rtc_set_time,
> +	.read_alarm = rk8xx_rtc_readalarm,
> +	.set_alarm = rk8xx_rtc_setalarm,
> +	.alarm_irq_enable = rk8xx_rtc_alarm_irq_enable,
>  };
>  
>  #ifdef CONFIG_PM_SLEEP
>  /* Turn off the alarm if it should not be a wake source. */
> -static int rk808_rtc_suspend(struct device *dev)
> +static int rk8xx_rtc_suspend(struct device *dev)
>  {
>  	struct platform_device *pdev = to_platform_device(dev);
> -	struct rk808_rtc *rk808_rtc = dev_get_drvdata(&pdev->dev);
> +	struct rk8xx_rtc *rk8xx_rtc = dev_get_drvdata(&pdev->dev);
>  
>  	if (device_may_wakeup(dev))
> -		enable_irq_wake(rk808_rtc->irq);
> +		enable_irq_wake(rk8xx_rtc->irq);
>  
>  	return 0;
>  }
> @@ -356,37 +381,44 @@ static int rk808_rtc_suspend(struct device *dev)
>  /* Enable the alarm if it should be enabled (in case it was disabled to
>   * prevent use as a wake source).
>   */
> -static int rk808_rtc_resume(struct device *dev)
> +static int rk8xx_rtc_resume(struct device *dev)
>  {
>  	struct platform_device *pdev = to_platform_device(dev);
> -	struct rk808_rtc *rk808_rtc = dev_get_drvdata(&pdev->dev);
> +	struct rk8xx_rtc *rk8xx_rtc = dev_get_drvdata(&pdev->dev);
>  
>  	if (device_may_wakeup(dev))
> -		disable_irq_wake(rk808_rtc->irq);
> +		disable_irq_wake(rk8xx_rtc->irq);
>  
>  	return 0;
>  }
>  #endif
>  
> -static SIMPLE_DEV_PM_OPS(rk808_rtc_pm_ops,
> -	rk808_rtc_suspend, rk808_rtc_resume);
> +static SIMPLE_DEV_PM_OPS(rk8xx_rtc_pm_ops,
> +	rk8xx_rtc_suspend, rk8xx_rtc_resume);
>  
> -static int rk808_rtc_probe(struct platform_device *pdev)
> +static int rk8xx_rtc_probe(struct platform_device *pdev)
>  {
> -	struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent);
> -	struct rk808_rtc *rk808_rtc;
> +	struct i2c_client *client = to_i2c_client(pdev->dev.parent);
> +	struct rk8xx_rtc *rk8xx_rtc;
>  	struct rtc_time tm;
>  	int ret;
>  
> -	rk808_rtc = devm_kzalloc(&pdev->dev, sizeof(*rk808_rtc), GFP_KERNEL);
> -	if (rk808_rtc == NULL)
> +	rk8xx_rtc = devm_kzalloc(&pdev->dev, sizeof(*rk8xx_rtc), GFP_KERNEL);
> +	if (rk8xx_rtc == NULL)
>  		return -ENOMEM;
>  
> -	platform_set_drvdata(pdev, rk808_rtc);
> -	rk808_rtc->rk808 = rk808;
> +	rk8xx_rtc->regmap = devm_regmap_init_i2c(client,
> +					     &rk8xx_rtc_regmap_config);
> +	if (IS_ERR(rk8xx_rtc->regmap)) {
> +		dev_err(&pdev->dev, "regmap initialization failed\n");
> +		return PTR_ERR(rk8xx_rtc->regmap);
> +	}
> +
> +	platform_set_drvdata(pdev, rk8xx_rtc);
> +	rk8xx_rtc->i2c = client;
>  
>  	/* start rtc running by default, and use shadowed timer. */
> -	ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
> +	ret = regmap_update_bits(rk8xx_rtc->regmap, RK8XX_RTC_CTRL_REG,
>  				 BIT_RTC_CTRL_REG_STOP_RTC_M |
>  				 BIT_RTC_CTRL_REG_RTC_READSEL_M,
>  				 BIT_RTC_CTRL_REG_RTC_READSEL_M);
> @@ -396,7 +428,7 @@ static int rk808_rtc_probe(struct platform_device *pdev)
>  		return ret;
>  	}
>  
> -	ret = regmap_write(rk808->regmap, RK808_RTC_STATUS_REG,
> +	ret = regmap_write(rk8xx_rtc->regmap, RK8XX_RTC_STATUS_REG,
>  			   RTC_STATUS_MASK);
>  	if (ret) {
>  		dev_err(&pdev->dev,
> @@ -405,7 +437,7 @@ static int rk808_rtc_probe(struct platform_device *pdev)
>  	}
>  
>  	/* set init time */
> -	ret = rk808_rtc_readtime(&pdev->dev, &tm);
> +	ret = rk8xx_rtc_readtime(&pdev->dev, &tm);
>  	if (ret) {
>  		dev_err(&pdev->dev, "Failed to read RTC time\n");
>  		return ret;
> @@ -416,45 +448,45 @@ static int rk808_rtc_probe(struct platform_device *pdev)
>  
>  	device_init_wakeup(&pdev->dev, 1);
>  
> -	rk808_rtc->rtc = devm_rtc_device_register(&pdev->dev, "rk808-rtc",
> -						  &rk808_rtc_ops, THIS_MODULE);
> -	if (IS_ERR(rk808_rtc->rtc)) {
> -		ret = PTR_ERR(rk808_rtc->rtc);
> +	rk8xx_rtc->rtc = devm_rtc_device_register(&pdev->dev, "rk8xx-rtc",
> +						  &rk8xx_rtc_ops, THIS_MODULE);
> +	if (IS_ERR(rk8xx_rtc->rtc)) {
> +		ret = PTR_ERR(rk8xx_rtc->rtc);
>  		return ret;
>  	}
>  
> -	rk808_rtc->irq = platform_get_irq(pdev, 0);
> -	if (rk808_rtc->irq < 0) {
> -		if (rk808_rtc->irq != -EPROBE_DEFER)
> +	rk8xx_rtc->irq = platform_get_irq(pdev, 0);
> +	if (rk8xx_rtc->irq < 0) {
> +		if (rk8xx_rtc->irq != -EPROBE_DEFER)
>  			dev_err(&pdev->dev, "Wake up is not possible as irq = %d\n",
> -				rk808_rtc->irq);
> -		return rk808_rtc->irq;
> +				rk8xx_rtc->irq);
> +		return rk8xx_rtc->irq;
>  	}
>  
> -	/* request alarm irq of rk808 */
> -	ret = devm_request_threaded_irq(&pdev->dev, rk808_rtc->irq, NULL,
> -					rk808_alarm_irq, 0,
> -					"RTC alarm", rk808_rtc);
> +	/* request alarm irq of rk8xx */
> +	ret = devm_request_threaded_irq(&pdev->dev, rk8xx_rtc->irq, NULL,
> +					rk8xx_alarm_irq, 0,
> +					"RTC alarm", rk8xx_rtc);
>  	if (ret) {
>  		dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n",
> -			rk808_rtc->irq, ret);
> +			rk8xx_rtc->irq, ret);
>  	}
>  
>  	return ret;
>  }
>  
> -static struct platform_driver rk808_rtc_driver = {
> -	.probe = rk808_rtc_probe,
> +static struct platform_driver rk8xx_rtc_driver = {
> +	.probe = rk8xx_rtc_probe,
>  	.driver = {
> -		.name = "rk808-rtc",
> -		.pm = &rk808_rtc_pm_ops,
> +		.name = "rk8xx-rtc",
> +		.pm = &rk8xx_rtc_pm_ops,
>  	},
>  };
>  
> -module_platform_driver(rk808_rtc_driver);
> +module_platform_driver(rk8xx_rtc_driver);
>  
> -MODULE_DESCRIPTION("RTC driver for the rk808 series PMICs");
> +MODULE_DESCRIPTION("RTC driver for the rk8xx series PMICs");
>  MODULE_AUTHOR("Chris Zhong <zyw at rock-chips.com>");
>  MODULE_AUTHOR("Zhang Qing <zhangqing at rock-chips.com>");
>  MODULE_LICENSE("GPL");
> -MODULE_ALIAS("platform:rk808-rtc");
> +MODULE_ALIAS("platform:rk8xx-rtc");
> -- 
> 1.9.1
> 
> 

-- 
Alexandre Belloni, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com


From ykk at rock-chips.com  Mon Jan  4 03:50:05 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Mon,  4 Jan 2016 19:50:05 +0800
Subject: [RFC PATCH v1 0/2] Add RK3229 vop support
Message-ID: <1451908205-25225-1-git-send-email-ykk@rock-chips.com>

Add RK3229 vop support


Yakir Yang (2):
  drm/rockchip: vop: add rk3229 vop support
  dt-bindings: add document for rk3229-vop

 .../bindings/display/rockchip/rockchip-vop.txt     |   1 +
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c        |   7 +-
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h        |   2 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c        | 120 +++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_vop_reg.h        |  90 ++++++++++++++++
 5 files changed, 218 insertions(+), 2 deletions(-)

-- 
1.9.1




From ykk at rock-chips.com  Mon Jan  4 03:50:18 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Mon,  4 Jan 2016 19:50:18 +0800
Subject: [RFC PATCH v1 1/2] drm/rockchip: vop: add rk3229 vop support
Message-ID: <1451908218-25278-1-git-send-email-ykk@rock-chips.com>

RK3228 registers layout is simalar to RK3288 layout, only the
interruput registers is different to RK3288.

RK3228 support two overlay plane and one hwc plane, max output
resolution is 4K. it support IOMMU, and its IOMMU same as rk3288's.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c |   7 +-
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   2 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 120 ++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_vop_reg.h |  90 +++++++++++++++++++++
 4 files changed, 217 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index d83bf87..3c83097 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -63,9 +63,12 @@
 #define VOP_INTR_SET_TYPE(vop, name, type, v) \
 	do { \
 		int i, reg = 0; \
-		for (i = 0; i < vop->data->intr->nintrs; i++) { \
-			if (vop->data->intr->intrs[i] & type) \
+		const struct vop_intr *intr = vop->data->intr; \
+		for (i = 0; i < intr->nintrs; i++) { \
+			if (intr->intrs[i] & type) { \
 				reg |= (v) << i; \
+				reg |= intr->write_mask ? (1 << (i + 16)) : 0; \
+			} \
 		} \
 		VOP_INTR_SET(vop, name, reg); \
 	} while (0)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index 071ff0b..1e839e8 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -60,6 +60,7 @@ struct vop_ctrl {
 };
 
 struct vop_intr {
+	bool write_mask;
 	const int *intrs;
 	uint32_t nintrs;
 	struct vop_reg enable;
@@ -136,6 +137,7 @@ struct vop_data {
 };
 
 /* interrupt define */
+#define DUMMY_INTR			(0 << 0)
 #define DSP_HOLD_VALID_INTR		(1 << 0)
 #define FS_INTR				(1 << 1)
 #define LINE_FLAG_INTR			(1 << 2)
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 3166b46..bbcd128 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -174,6 +174,7 @@ static const int rk3288_vop_intrs[] = {
 };
 
 static const struct vop_intr rk3288_vop_intr = {
+	.write_mask = false,
 	.intrs = rk3288_vop_intrs,
 	.nintrs = ARRAY_SIZE(rk3288_vop_intrs),
 	.status = VOP_REG(RK3288_INTR_CTRL0, 0xf, 0),
@@ -190,6 +191,122 @@ static const struct vop_data rk3288_vop = {
 	.win_size = ARRAY_SIZE(rk3288_vop_win_data),
 };
 
+static const struct vop_scl_extension rk3229_win_full_scl_ext = {
+	.cbcr_vsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 31),
+	.cbcr_vsu_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 30),
+	.cbcr_hsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 28),
+	.cbcr_ver_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 26),
+	.cbcr_hor_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 24),
+	.yrgb_vsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 23),
+	.yrgb_vsu_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 22),
+	.yrgb_hsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 20),
+	.yrgb_ver_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 18),
+	.yrgb_hor_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 16),
+	.line_load_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 15),
+	.cbcr_axi_gather_num = VOP_REG(RK3229_WIN0_CTRL1, 0x7, 12),
+	.yrgb_axi_gather_num = VOP_REG(RK3229_WIN0_CTRL1, 0xf, 8),
+	.vsd_cbcr_gt2 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 7),
+	.vsd_cbcr_gt4 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 6),
+	.vsd_yrgb_gt2 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 5),
+	.vsd_yrgb_gt4 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 4),
+	.bic_coe_sel = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 2),
+	.cbcr_axi_gather_en = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 1),
+	.yrgb_axi_gather_en = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 0),
+	.lb_mode = VOP_REG(RK3229_WIN0_CTRL0, 0x7, 5),
+};
+
+static const struct vop_scl_regs rk3229_win_full_scl = {
+	.ext = &rk3229_win_full_scl_ext,
+	.scale_yrgb_x = VOP_REG(RK3229_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
+	.scale_yrgb_y = VOP_REG(RK3229_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
+	.scale_cbcr_x = VOP_REG(RK3229_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0),
+	.scale_cbcr_y = VOP_REG(RK3229_WIN0_SCL_FACTOR_CBR, 0xffff, 16),
+};
+
+static const struct vop_win_phy rk3229_win01_data = {
+	.scl = &rk3229_win_full_scl,
+	.data_formats = formats_win_full,
+	.nformats = ARRAY_SIZE(formats_win_full),
+	.enable = VOP_REG(RK3229_WIN0_CTRL0, 0x1, 0),
+	.format = VOP_REG(RK3229_WIN0_CTRL0, 0x7, 1),
+	.rb_swap = VOP_REG(RK3229_WIN0_CTRL0, 0x1, 12),
+	.act_info = VOP_REG(RK3229_WIN0_ACT_INFO, 0x1fff1fff, 0),
+	.dsp_info = VOP_REG(RK3229_WIN0_DSP_INFO, 0x0fff0fff, 0),
+	.dsp_st = VOP_REG(RK3229_WIN0_DSP_ST, 0x1fff1fff, 0),
+	.yrgb_mst = VOP_REG(RK3229_WIN0_YRGB_MST, 0xffffffff, 0),
+	.uv_mst = VOP_REG(RK3229_WIN0_CBR_MST, 0xffffffff, 0),
+	.yrgb_vir = VOP_REG(RK3229_WIN0_VIR, 0x3fff, 0),
+	.uv_vir = VOP_REG(RK3229_WIN0_VIR, 0x3fff, 16),
+	.src_alpha_ctl = VOP_REG(RK3229_WIN0_SRC_ALPHA_CTRL, 0xff, 0),
+	.dst_alpha_ctl = VOP_REG(RK3229_WIN0_DST_ALPHA_CTRL, 0xff, 0),
+};
+
+static const struct vop_win_data rk3229_vop_win_data[] = {
+	{ .base = 0x00, .phy = &rk3229_win01_data,
+	  .type = DRM_PLANE_TYPE_PRIMARY },
+	{ .base = 0x40, .phy = &rk3229_win01_data,
+	  .type = DRM_PLANE_TYPE_CURSOR },
+};
+
+static const struct vop_ctrl rk3229_ctrl_data = {
+	.cfg_done = VOP_REG(RK3229_REG_CFG_DONE, 0x1, 0),
+	.standby = VOP_REG(RK3229_SYS_CTRL, 0x1, 22),
+	.gate_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 23),
+	.mmu_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 20),
+	.rgb_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 12),
+	.hdmi_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 13),
+	.edp_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 14),
+	.mipi_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 15),
+	.data_blank = VOP_REG(RK3229_DSP_CTRL0, 0x1, 19),
+	.out_mode = VOP_REG(RK3229_DSP_CTRL0, 0xf, 0),
+	.pin_pol = VOP_REG(RK3229_DSP_CTRL1, 0xf, 20),
+	.dither_up = VOP_REG(RK3229_DSP_CTRL1, 0x1, 6),
+	.htotal_pw = VOP_REG(RK3229_DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
+	.hact_st_end = VOP_REG(RK3229_DSP_HACT_ST_END, 0x1fff1fff, 0),
+	.vtotal_pw = VOP_REG(RK3229_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
+	.vact_st_end = VOP_REG(RK3229_DSP_VACT_ST_END, 0x1fff1fff, 0),
+	.hpost_st_end = VOP_REG(RK3229_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
+	.vpost_st_end = VOP_REG(RK3229_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
+};
+
+static const int rk3229_vop_intrs[] = {
+	FS_INTR,
+	DUMMY_INTR,
+	DUMMY_INTR,
+	LINE_FLAG_INTR,
+	DUMMY_INTR,
+	DUMMY_INTR,
+	DUMMY_INTR,
+	DUMMY_INTR,
+	DUMMY_INTR,
+	DUMMY_INTR,
+	DUMMY_INTR,
+	DUMMY_INTR,
+	DUMMY_INTR,
+	DSP_HOLD_VALID_INTR,
+};
+
+static const struct vop_intr rk3229_intr = {
+	.write_mask = true,
+	.intrs = rk3229_vop_intrs,
+	.nintrs = ARRAY_SIZE(rk3229_vop_intrs),
+	.status = VOP_REG(RK3229_INTR_STATUS0, 0xffffffff, 0),
+	.clear = VOP_REG(RK3229_INTR_CLEAR0, 0xffffffff, 0),
+	.enable = VOP_REG(RK3229_INTR_EN0, 0xffffffff, 0),
+};
+
+static const struct vop_reg_data rk3229_vop_init_reg_table[] = {
+};
+
+static const struct vop_data rk3229_vop = {
+	.init_table = rk3229_vop_init_reg_table,
+	.table_size = ARRAY_SIZE(rk3229_vop_init_reg_table),
+	.intr = &rk3229_intr,
+	.ctrl = &rk3229_ctrl_data,
+	.win = rk3229_vop_win_data,
+	.win_size = ARRAY_SIZE(rk3229_vop_win_data),
+};
+
 static const struct vop_scl_regs rk3066_win_scl = {
 	.scale_yrgb_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
 	.scale_yrgb_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
@@ -240,6 +357,7 @@ static const int rk3036_vop_intrs[] = {
 };
 
 static const struct vop_intr rk3036_intr = {
+	.write_mask = false,
 	.intrs = rk3036_vop_intrs,
 	.nintrs = ARRAY_SIZE(rk3036_vop_intrs),
 	.status = VOP_REG(RK3036_INT_STATUS, 0xf, 0),
@@ -274,6 +392,8 @@ static const struct vop_data rk3036_vop = {
 static const struct of_device_id vop_driver_dt_match[] = {
 	{ .compatible = "rockchip,rk3288-vop",
 	  .data = &rk3288_vop },
+	{ .compatible = "rockchip,rk3229-vop",
+	  .data = &rk3229_vop },
 	{ .compatible = "rockchip,rk3036-vop",
 	  .data = &rk3036_vop },
 	{},
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
index d4b46cb..8c86b86 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
@@ -15,6 +15,96 @@
 #ifndef _ROCKCHIP_VOP_REG_H
 #define _ROCKCHIP_VOP_REG_H
 
+/* rk3228 register definition */
+#define RK3228_REG_CFG_DONE			0x0000
+#define RK3228_VERSION_INFO			0x0004
+#define RK3228_SYS_CTRL				0x0008
+#define RK3228_SYS_CTRL1			0x000c
+#define RK3228_DSP_CTRL0			0x0010
+#define RK3228_DSP_CTRL1			0x0014
+#define RK3228_DSP_BG				0x0018
+#define RK3228_WIN0_CTRL0			0x0030
+#define RK3228_WIN0_CTRL1			0x0034
+#define RK3228_WIN0_COLOR_KEY			0x0038
+#define RK3228_WIN0_VIR				0x003c
+#define RK3228_WIN0_YRGB_MST			0x0040
+#define RK3228_WIN0_CBR_MST			0x0044
+#define RK3228_WIN0_ACT_INFO			0x0048
+#define RK3228_WIN0_DSP_INFO			0x004c
+#define RK3228_WIN0_DSP_ST			0x0050
+#define RK3228_WIN0_SCL_FACTOR_YRGB		0x0054
+#define RK3228_WIN0_SCL_FACTOR_CBR		0x0058
+#define RK3228_WIN0_SCL_OFFSET			0x005c
+#define RK3228_WIN0_SRC_ALPHA_CTRL		0x0060
+#define RK3228_WIN0_DST_ALPHA_CTRL		0x0064
+#define RK3228_WIN0_FADING_CTRL			0x0068
+/* win1 register */
+#define RK3228_WIN1_CTRL0			0x0070
+#define RK3228_WIN1_CTRL1			0x0074
+#define RK3228_WIN1_COLOR_KEY			0x0078
+#define RK3228_WIN1_VIR				0x007c
+#define RK3228_WIN1_YRGB_MST			0x0080
+#define RK3228_WIN1_CBR_MST			0x0084
+#define RK3228_WIN1_ACT_INFO			0x0088
+#define RK3228_WIN1_DSP_INFO			0x008c
+#define RK3228_WIN1_DSP_ST			0x0090
+#define RK3228_WIN1_SCL_FACTOR_YRGB		0x0094
+#define RK3228_WIN1_SCL_FACTOR_CBR		0x0098
+#define RK3228_WIN1_SCL_OFFSET			0x009c
+#define RK3228_WIN1_SRC_ALPHA_CTRL		0x00a0
+#define RK3228_WIN1_DST_ALPHA_CTRL		0x00a4
+#define RK3228_WIN1_FADING_CTRL			0x00a8
+#define RK3228_WIN1_CTRL2			0x00ac
+/* hwc register */
+#define RK3228_HWC_CTRL0			0x0150
+#define RK3228_HWC_CTRL1			0x0154
+#define RK3228_HWC_MST				0x0158
+#define RK3228_HWC_DSP_ST			0x015c
+#define RK3228_HWC_SRC_ALPHA_CTRL		0x0160
+#define RK3228_HWC_DST_ALPHA_CTRL		0x0164
+#define RK3228_HWC_FADING_CTRL			0x0168
+#define RK3228_HWC_RESERVED			0x016c
+/* post process register */
+#define RK3228_POST_DSP_HACT_INFO		0x0170
+#define RK3228_POST_DSP_VACT_INFO		0x0174
+#define RK3228_POST_SCL_FACTOR_YRGB		0x0178
+#define RK3228_POST_RESERVED			0x017c
+#define RK3228_POST_SCL_CTRL			0x0180
+#define RK3228_POST_DSP_VACT_INFO_F1		0x0184
+#define RK3228_DSP_HTOTAL_HS_END		0x0188
+#define RK3228_DSP_HACT_ST_END			0x018c
+#define RK3228_DSP_VTOTAL_VS_END		0x0190
+#define RK3228_DSP_VACT_ST_END			0x0194
+#define RK3228_DSP_VS_ST_END_F1			0x0198
+#define RK3228_DSP_VACT_ST_END_F1		0x019c
+/* Brightness contrast */
+#define RK3228_BCSH_COLOR_BAR			0x01b0
+#define RK3228_BCSH_BCS				0x01b4
+#define RK3228_BCSH_H				0x01b8
+#define RK3228_BCSH_CTRL			0x01bc
+/* FRC registers */
+#define RK3228_FRC_LOWER01_0			0x01e8
+#define RK3228_FRC_LOWER01_1			0x01ec
+#define RK3228_FRC_LOWER10_0			0x01f0
+#define RK3228_FRC_LOWER10_1			0x01f4
+#define RK3228_FRC_LOWER11_0			0x01f8
+#define RK3228_FRC_LOWER11_1			0x01fc
+/* Interrupt registers */
+#define RK3228_INTR_EN0				0x0280
+#define RK3228_INTR_CLEAR0			0x0284
+#define RK3228_INTR_STATUS0			0x0288
+#define RK3228_INTR_RAW_STATUS0			0x028c
+#define RK3228_INTR_EN1				0x0290
+#define RK3228_INTR_CLEAR1			0x0294
+#define RK3228_INTR_STATUS1			0x0298
+#define RK3228_INTR_RAW_STATUS1			0x029c
+#define RK3228_LINE_FLAG			0x02a0
+#define RK3228_VOP_STATUS			0x02a4
+#define RK3228_BLANKING_VALUE			0x02a8
+#define RK3228_WIN0_DSP_BG			0x02b0
+#define RK3228_WIN1_DSP_BG			0x02b4
+/* register definition end */
+
 /* rk3288 register definition */
 #define RK3288_REG_CFG_DONE			0x0000
 #define RK3288_VERSION_INFO			0x0004
-- 
1.9.1




From ykk at rock-chips.com  Mon Jan  4 03:50:30 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Mon,  4 Jan 2016 19:50:30 +0800
Subject: [RFC PATCH v1 2/2] dt-bindings: add document for rk3229-vop
Message-ID: <1451908230-25329-1-git-send-email-ykk@rock-chips.com>


Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
 Documentation/devicetree/bindings/display/rockchip/rockchip-vop.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip-vop.txt b/Documentation/devicetree/bindings/display/rockchip/rockchip-vop.txt
index 5489b59..75ebc20 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip-vop.txt
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip-vop.txt
@@ -7,6 +7,7 @@ buffer to an external LCD interface.
 Required properties:
 - compatible: value should be one of the following
 		"rockchip,rk3288-vop";
+		"rockchip,rk3229-vop";
 		"rockchip,rk3036-vop";
 
 - interrupts: should contain a list of all VOP IP block interrupts in the
-- 
1.9.1




From ykk at rock-chips.com  Mon Jan  4 03:53:58 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Mon,  4 Jan 2016 19:53:58 +0800
Subject: [RFC PATCH v1 1/2] drm/rockchip: vop: add rk3229 vop support
In-Reply-To: <1451908218-25278-1-git-send-email-ykk@rock-chips.com>
References: <1451908218-25278-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1451908438-26581-1-git-send-email-ykk@rock-chips.com>

RK3229 registers layout is simalar to RK3288 layout, only the
interruput registers is different to RK3288.

RK3229 support two overlay plane and one hwc plane, max output
resolution is 4K. it support IOMMU, and its IOMMU same as rk3288's.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c |   7 +-
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   2 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 120 ++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_vop_reg.h |  90 +++++++++++++++++++++
 4 files changed, 217 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index d83bf87..3c83097 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -63,9 +63,12 @@
 #define VOP_INTR_SET_TYPE(vop, name, type, v) \
 	do { \
 		int i, reg = 0; \
-		for (i = 0; i < vop->data->intr->nintrs; i++) { \
-			if (vop->data->intr->intrs[i] & type) \
+		const struct vop_intr *intr = vop->data->intr; \
+		for (i = 0; i < intr->nintrs; i++) { \
+			if (intr->intrs[i] & type) { \
 				reg |= (v) << i; \
+				reg |= intr->write_mask ? (1 << (i + 16)) : 0; \
+			} \
 		} \
 		VOP_INTR_SET(vop, name, reg); \
 	} while (0)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index 071ff0b..1e839e8 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -60,6 +60,7 @@ struct vop_ctrl {
 };
 
 struct vop_intr {
+	bool write_mask;
 	const int *intrs;
 	uint32_t nintrs;
 	struct vop_reg enable;
@@ -136,6 +137,7 @@ struct vop_data {
 };
 
 /* interrupt define */
+#define DUMMY_INTR			(0 << 0)
 #define DSP_HOLD_VALID_INTR		(1 << 0)
 #define FS_INTR				(1 << 1)
 #define LINE_FLAG_INTR			(1 << 2)
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 3166b46..bbcd128 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -174,6 +174,7 @@ static const int rk3288_vop_intrs[] = {
 };
 
 static const struct vop_intr rk3288_vop_intr = {
+	.write_mask = false,
 	.intrs = rk3288_vop_intrs,
 	.nintrs = ARRAY_SIZE(rk3288_vop_intrs),
 	.status = VOP_REG(RK3288_INTR_CTRL0, 0xf, 0),
@@ -190,6 +191,122 @@ static const struct vop_data rk3288_vop = {
 	.win_size = ARRAY_SIZE(rk3288_vop_win_data),
 };
 
+static const struct vop_scl_extension rk3229_win_full_scl_ext = {
+	.cbcr_vsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 31),
+	.cbcr_vsu_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 30),
+	.cbcr_hsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 28),
+	.cbcr_ver_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 26),
+	.cbcr_hor_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 24),
+	.yrgb_vsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 23),
+	.yrgb_vsu_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 22),
+	.yrgb_hsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 20),
+	.yrgb_ver_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 18),
+	.yrgb_hor_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 16),
+	.line_load_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 15),
+	.cbcr_axi_gather_num = VOP_REG(RK3229_WIN0_CTRL1, 0x7, 12),
+	.yrgb_axi_gather_num = VOP_REG(RK3229_WIN0_CTRL1, 0xf, 8),
+	.vsd_cbcr_gt2 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 7),
+	.vsd_cbcr_gt4 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 6),
+	.vsd_yrgb_gt2 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 5),
+	.vsd_yrgb_gt4 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 4),
+	.bic_coe_sel = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 2),
+	.cbcr_axi_gather_en = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 1),
+	.yrgb_axi_gather_en = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 0),
+	.lb_mode = VOP_REG(RK3229_WIN0_CTRL0, 0x7, 5),
+};
+
+static const struct vop_scl_regs rk3229_win_full_scl = {
+	.ext = &rk3229_win_full_scl_ext,
+	.scale_yrgb_x = VOP_REG(RK3229_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
+	.scale_yrgb_y = VOP_REG(RK3229_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
+	.scale_cbcr_x = VOP_REG(RK3229_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0),
+	.scale_cbcr_y = VOP_REG(RK3229_WIN0_SCL_FACTOR_CBR, 0xffff, 16),
+};
+
+static const struct vop_win_phy rk3229_win01_data = {
+	.scl = &rk3229_win_full_scl,
+	.data_formats = formats_win_full,
+	.nformats = ARRAY_SIZE(formats_win_full),
+	.enable = VOP_REG(RK3229_WIN0_CTRL0, 0x1, 0),
+	.format = VOP_REG(RK3229_WIN0_CTRL0, 0x7, 1),
+	.rb_swap = VOP_REG(RK3229_WIN0_CTRL0, 0x1, 12),
+	.act_info = VOP_REG(RK3229_WIN0_ACT_INFO, 0x1fff1fff, 0),
+	.dsp_info = VOP_REG(RK3229_WIN0_DSP_INFO, 0x0fff0fff, 0),
+	.dsp_st = VOP_REG(RK3229_WIN0_DSP_ST, 0x1fff1fff, 0),
+	.yrgb_mst = VOP_REG(RK3229_WIN0_YRGB_MST, 0xffffffff, 0),
+	.uv_mst = VOP_REG(RK3229_WIN0_CBR_MST, 0xffffffff, 0),
+	.yrgb_vir = VOP_REG(RK3229_WIN0_VIR, 0x3fff, 0),
+	.uv_vir = VOP_REG(RK3229_WIN0_VIR, 0x3fff, 16),
+	.src_alpha_ctl = VOP_REG(RK3229_WIN0_SRC_ALPHA_CTRL, 0xff, 0),
+	.dst_alpha_ctl = VOP_REG(RK3229_WIN0_DST_ALPHA_CTRL, 0xff, 0),
+};
+
+static const struct vop_win_data rk3229_vop_win_data[] = {
+	{ .base = 0x00, .phy = &rk3229_win01_data,
+	  .type = DRM_PLANE_TYPE_PRIMARY },
+	{ .base = 0x40, .phy = &rk3229_win01_data,
+	  .type = DRM_PLANE_TYPE_CURSOR },
+};
+
+static const struct vop_ctrl rk3229_ctrl_data = {
+	.cfg_done = VOP_REG(RK3229_REG_CFG_DONE, 0x1, 0),
+	.standby = VOP_REG(RK3229_SYS_CTRL, 0x1, 22),
+	.gate_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 23),
+	.mmu_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 20),
+	.rgb_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 12),
+	.hdmi_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 13),
+	.edp_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 14),
+	.mipi_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 15),
+	.data_blank = VOP_REG(RK3229_DSP_CTRL0, 0x1, 19),
+	.out_mode = VOP_REG(RK3229_DSP_CTRL0, 0xf, 0),
+	.pin_pol = VOP_REG(RK3229_DSP_CTRL1, 0xf, 20),
+	.dither_up = VOP_REG(RK3229_DSP_CTRL1, 0x1, 6),
+	.htotal_pw = VOP_REG(RK3229_DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
+	.hact_st_end = VOP_REG(RK3229_DSP_HACT_ST_END, 0x1fff1fff, 0),
+	.vtotal_pw = VOP_REG(RK3229_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
+	.vact_st_end = VOP_REG(RK3229_DSP_VACT_ST_END, 0x1fff1fff, 0),
+	.hpost_st_end = VOP_REG(RK3229_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
+	.vpost_st_end = VOP_REG(RK3229_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
+};
+
+static const int rk3229_vop_intrs[] = {
+	FS_INTR,
+	DUMMY_INTR,
+	DUMMY_INTR,
+	LINE_FLAG_INTR,
+	DUMMY_INTR,
+	DUMMY_INTR,
+	DUMMY_INTR,
+	DUMMY_INTR,
+	DUMMY_INTR,
+	DUMMY_INTR,
+	DUMMY_INTR,
+	DUMMY_INTR,
+	DUMMY_INTR,
+	DSP_HOLD_VALID_INTR,
+};
+
+static const struct vop_intr rk3229_intr = {
+	.write_mask = true,
+	.intrs = rk3229_vop_intrs,
+	.nintrs = ARRAY_SIZE(rk3229_vop_intrs),
+	.status = VOP_REG(RK3229_INTR_STATUS0, 0xffffffff, 0),
+	.clear = VOP_REG(RK3229_INTR_CLEAR0, 0xffffffff, 0),
+	.enable = VOP_REG(RK3229_INTR_EN0, 0xffffffff, 0),
+};
+
+static const struct vop_reg_data rk3229_vop_init_reg_table[] = {
+};
+
+static const struct vop_data rk3229_vop = {
+	.init_table = rk3229_vop_init_reg_table,
+	.table_size = ARRAY_SIZE(rk3229_vop_init_reg_table),
+	.intr = &rk3229_intr,
+	.ctrl = &rk3229_ctrl_data,
+	.win = rk3229_vop_win_data,
+	.win_size = ARRAY_SIZE(rk3229_vop_win_data),
+};
+
 static const struct vop_scl_regs rk3066_win_scl = {
 	.scale_yrgb_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
 	.scale_yrgb_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
@@ -240,6 +357,7 @@ static const int rk3036_vop_intrs[] = {
 };
 
 static const struct vop_intr rk3036_intr = {
+	.write_mask = false,
 	.intrs = rk3036_vop_intrs,
 	.nintrs = ARRAY_SIZE(rk3036_vop_intrs),
 	.status = VOP_REG(RK3036_INT_STATUS, 0xf, 0),
@@ -274,6 +392,8 @@ static const struct vop_data rk3036_vop = {
 static const struct of_device_id vop_driver_dt_match[] = {
 	{ .compatible = "rockchip,rk3288-vop",
 	  .data = &rk3288_vop },
+	{ .compatible = "rockchip,rk3229-vop",
+	  .data = &rk3229_vop },
 	{ .compatible = "rockchip,rk3036-vop",
 	  .data = &rk3036_vop },
 	{},
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
index d4b46cb..0280388 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
@@ -15,6 +15,96 @@
 #ifndef _ROCKCHIP_VOP_REG_H
 #define _ROCKCHIP_VOP_REG_H
 
+/* rk3229 register definition */
+#define RK3229_REG_CFG_DONE			0x0000
+#define RK3229_VERSION_INFO			0x0004
+#define RK3229_SYS_CTRL				0x0008
+#define RK3229_SYS_CTRL1			0x000c
+#define RK3229_DSP_CTRL0			0x0010
+#define RK3229_DSP_CTRL1			0x0014
+#define RK3229_DSP_BG				0x0018
+#define RK3229_WIN0_CTRL0			0x0030
+#define RK3229_WIN0_CTRL1			0x0034
+#define RK3229_WIN0_COLOR_KEY			0x0038
+#define RK3229_WIN0_VIR				0x003c
+#define RK3229_WIN0_YRGB_MST			0x0040
+#define RK3229_WIN0_CBR_MST			0x0044
+#define RK3229_WIN0_ACT_INFO			0x0048
+#define RK3229_WIN0_DSP_INFO			0x004c
+#define RK3229_WIN0_DSP_ST			0x0050
+#define RK3229_WIN0_SCL_FACTOR_YRGB		0x0054
+#define RK3229_WIN0_SCL_FACTOR_CBR		0x0058
+#define RK3229_WIN0_SCL_OFFSET			0x005c
+#define RK3229_WIN0_SRC_ALPHA_CTRL		0x0060
+#define RK3229_WIN0_DST_ALPHA_CTRL		0x0064
+#define RK3229_WIN0_FADING_CTRL			0x0068
+/* win1 register */
+#define RK3229_WIN1_CTRL0			0x0070
+#define RK3229_WIN1_CTRL1			0x0074
+#define RK3229_WIN1_COLOR_KEY			0x0078
+#define RK3229_WIN1_VIR				0x007c
+#define RK3229_WIN1_YRGB_MST			0x0080
+#define RK3229_WIN1_CBR_MST			0x0084
+#define RK3229_WIN1_ACT_INFO			0x0088
+#define RK3229_WIN1_DSP_INFO			0x008c
+#define RK3229_WIN1_DSP_ST			0x0090
+#define RK3229_WIN1_SCL_FACTOR_YRGB		0x0094
+#define RK3229_WIN1_SCL_FACTOR_CBR		0x0098
+#define RK3229_WIN1_SCL_OFFSET			0x009c
+#define RK3229_WIN1_SRC_ALPHA_CTRL		0x00a0
+#define RK3229_WIN1_DST_ALPHA_CTRL		0x00a4
+#define RK3229_WIN1_FADING_CTRL			0x00a8
+#define RK3229_WIN1_CTRL2			0x00ac
+/* hwc register */
+#define RK3229_HWC_CTRL0			0x0150
+#define RK3229_HWC_CTRL1			0x0154
+#define RK3229_HWC_MST				0x0158
+#define RK3229_HWC_DSP_ST			0x015c
+#define RK3229_HWC_SRC_ALPHA_CTRL		0x0160
+#define RK3229_HWC_DST_ALPHA_CTRL		0x0164
+#define RK3229_HWC_FADING_CTRL			0x0168
+#define RK3229_HWC_RESERVED			0x016c
+/* post process register */
+#define RK3229_POST_DSP_HACT_INFO		0x0170
+#define RK3229_POST_DSP_VACT_INFO		0x0174
+#define RK3229_POST_SCL_FACTOR_YRGB		0x0178
+#define RK3229_POST_RESERVED			0x017c
+#define RK3229_POST_SCL_CTRL			0x0180
+#define RK3229_POST_DSP_VACT_INFO_F1		0x0184
+#define RK3229_DSP_HTOTAL_HS_END		0x0188
+#define RK3229_DSP_HACT_ST_END			0x018c
+#define RK3229_DSP_VTOTAL_VS_END		0x0190
+#define RK3229_DSP_VACT_ST_END			0x0194
+#define RK3229_DSP_VS_ST_END_F1			0x0198
+#define RK3229_DSP_VACT_ST_END_F1		0x019c
+/* Brightness contrast */
+#define RK3229_BCSH_COLOR_BAR			0x01b0
+#define RK3229_BCSH_BCS				0x01b4
+#define RK3229_BCSH_H				0x01b8
+#define RK3229_BCSH_CTRL			0x01bc
+/* FRC registers */
+#define RK3229_FRC_LOWER01_0			0x01e8
+#define RK3229_FRC_LOWER01_1			0x01ec
+#define RK3229_FRC_LOWER10_0			0x01f0
+#define RK3229_FRC_LOWER10_1			0x01f4
+#define RK3229_FRC_LOWER11_0			0x01f8
+#define RK3229_FRC_LOWER11_1			0x01fc
+/* Interrupt registers */
+#define RK3229_INTR_EN0				0x0280
+#define RK3229_INTR_CLEAR0			0x0284
+#define RK3229_INTR_STATUS0			0x0288
+#define RK3229_INTR_RAW_STATUS0			0x028c
+#define RK3229_INTR_EN1				0x0290
+#define RK3229_INTR_CLEAR1			0x0294
+#define RK3229_INTR_STATUS1			0x0298
+#define RK3229_INTR_RAW_STATUS1			0x029c
+#define RK3229_LINE_FLAG			0x02a0
+#define RK3229_VOP_STATUS			0x02a4
+#define RK3229_BLANKING_VALUE			0x02a8
+#define RK3229_WIN0_DSP_BG			0x02b0
+#define RK3229_WIN1_DSP_BG			0x02b4
+/* register definition end */
+
 /* rk3288 register definition */
 #define RK3288_REG_CFG_DONE			0x0000
 #define RK3288_VERSION_INFO			0x0004
-- 
1.9.1




From ykk at rock-chips.com  Mon Jan  4 03:57:20 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Mon, 4 Jan 2016 19:57:20 +0800
Subject: [RFC PATCH v1 1/2] drm/rockchip: vop: add rk3229 vop support
In-Reply-To: <1451908218-25278-1-git-send-email-ykk@rock-chips.com>
References: <1451908218-25278-1-git-send-email-ykk@rock-chips.com>
Message-ID: <568A5E20.8060901@rock-chips.com>

Sorry, there shouldn't have the "3228" prefix,
     %s/3228/3229

I have send the new patch out, please ignore this one.

Thanks,
- Yakir

On 01/04/2016 07:50 PM, Yakir Yang wrote:
> RK3228 registers layout is simalar to RK3288 layout, only the
> interruput registers is different to RK3288.
>
> RK3228 support two overlay plane and one hwc plane, max output
> resolution is 4K. it support IOMMU, and its IOMMU same as rk3288's.
>
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
> ---
>   drivers/gpu/drm/rockchip/rockchip_drm_vop.c |   7 +-
>   drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   2 +
>   drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 120 ++++++++++++++++++++++++++++
>   drivers/gpu/drm/rockchip/rockchip_vop_reg.h |  90 +++++++++++++++++++++
>   4 files changed, 217 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> index d83bf87..3c83097 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> @@ -63,9 +63,12 @@
>   #define VOP_INTR_SET_TYPE(vop, name, type, v) \
>   	do { \
>   		int i, reg = 0; \
> -		for (i = 0; i < vop->data->intr->nintrs; i++) { \
> -			if (vop->data->intr->intrs[i] & type) \
> +		const struct vop_intr *intr = vop->data->intr; \
> +		for (i = 0; i < intr->nintrs; i++) { \
> +			if (intr->intrs[i] & type) { \
>   				reg |= (v) << i; \
> +				reg |= intr->write_mask ? (1 << (i + 16)) : 0; \
> +			} \
>   		} \
>   		VOP_INTR_SET(vop, name, reg); \
>   	} while (0)
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> index 071ff0b..1e839e8 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> @@ -60,6 +60,7 @@ struct vop_ctrl {
>   };
>   
>   struct vop_intr {
> +	bool write_mask;
>   	const int *intrs;
>   	uint32_t nintrs;
>   	struct vop_reg enable;
> @@ -136,6 +137,7 @@ struct vop_data {
>   };
>   
>   /* interrupt define */
> +#define DUMMY_INTR			(0 << 0)
>   #define DSP_HOLD_VALID_INTR		(1 << 0)
>   #define FS_INTR				(1 << 1)
>   #define LINE_FLAG_INTR			(1 << 2)
> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> index 3166b46..bbcd128 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> @@ -174,6 +174,7 @@ static const int rk3288_vop_intrs[] = {
>   };
>   
>   static const struct vop_intr rk3288_vop_intr = {
> +	.write_mask = false,
>   	.intrs = rk3288_vop_intrs,
>   	.nintrs = ARRAY_SIZE(rk3288_vop_intrs),
>   	.status = VOP_REG(RK3288_INTR_CTRL0, 0xf, 0),
> @@ -190,6 +191,122 @@ static const struct vop_data rk3288_vop = {
>   	.win_size = ARRAY_SIZE(rk3288_vop_win_data),
>   };
>   
> +static const struct vop_scl_extension rk3229_win_full_scl_ext = {
> +	.cbcr_vsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 31),
> +	.cbcr_vsu_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 30),
> +	.cbcr_hsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 28),
> +	.cbcr_ver_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 26),
> +	.cbcr_hor_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 24),
> +	.yrgb_vsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 23),
> +	.yrgb_vsu_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 22),
> +	.yrgb_hsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 20),
> +	.yrgb_ver_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 18),
> +	.yrgb_hor_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 16),
> +	.line_load_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 15),
> +	.cbcr_axi_gather_num = VOP_REG(RK3229_WIN0_CTRL1, 0x7, 12),
> +	.yrgb_axi_gather_num = VOP_REG(RK3229_WIN0_CTRL1, 0xf, 8),
> +	.vsd_cbcr_gt2 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 7),
> +	.vsd_cbcr_gt4 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 6),
> +	.vsd_yrgb_gt2 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 5),
> +	.vsd_yrgb_gt4 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 4),
> +	.bic_coe_sel = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 2),
> +	.cbcr_axi_gather_en = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 1),
> +	.yrgb_axi_gather_en = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 0),
> +	.lb_mode = VOP_REG(RK3229_WIN0_CTRL0, 0x7, 5),
> +};
> +
> +static const struct vop_scl_regs rk3229_win_full_scl = {
> +	.ext = &rk3229_win_full_scl_ext,
> +	.scale_yrgb_x = VOP_REG(RK3229_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
> +	.scale_yrgb_y = VOP_REG(RK3229_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
> +	.scale_cbcr_x = VOP_REG(RK3229_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0),
> +	.scale_cbcr_y = VOP_REG(RK3229_WIN0_SCL_FACTOR_CBR, 0xffff, 16),
> +};
> +
> +static const struct vop_win_phy rk3229_win01_data = {
> +	.scl = &rk3229_win_full_scl,
> +	.data_formats = formats_win_full,
> +	.nformats = ARRAY_SIZE(formats_win_full),
> +	.enable = VOP_REG(RK3229_WIN0_CTRL0, 0x1, 0),
> +	.format = VOP_REG(RK3229_WIN0_CTRL0, 0x7, 1),
> +	.rb_swap = VOP_REG(RK3229_WIN0_CTRL0, 0x1, 12),
> +	.act_info = VOP_REG(RK3229_WIN0_ACT_INFO, 0x1fff1fff, 0),
> +	.dsp_info = VOP_REG(RK3229_WIN0_DSP_INFO, 0x0fff0fff, 0),
> +	.dsp_st = VOP_REG(RK3229_WIN0_DSP_ST, 0x1fff1fff, 0),
> +	.yrgb_mst = VOP_REG(RK3229_WIN0_YRGB_MST, 0xffffffff, 0),
> +	.uv_mst = VOP_REG(RK3229_WIN0_CBR_MST, 0xffffffff, 0),
> +	.yrgb_vir = VOP_REG(RK3229_WIN0_VIR, 0x3fff, 0),
> +	.uv_vir = VOP_REG(RK3229_WIN0_VIR, 0x3fff, 16),
> +	.src_alpha_ctl = VOP_REG(RK3229_WIN0_SRC_ALPHA_CTRL, 0xff, 0),
> +	.dst_alpha_ctl = VOP_REG(RK3229_WIN0_DST_ALPHA_CTRL, 0xff, 0),
> +};
> +
> +static const struct vop_win_data rk3229_vop_win_data[] = {
> +	{ .base = 0x00, .phy = &rk3229_win01_data,
> +	  .type = DRM_PLANE_TYPE_PRIMARY },
> +	{ .base = 0x40, .phy = &rk3229_win01_data,
> +	  .type = DRM_PLANE_TYPE_CURSOR },
> +};
> +
> +static const struct vop_ctrl rk3229_ctrl_data = {
> +	.cfg_done = VOP_REG(RK3229_REG_CFG_DONE, 0x1, 0),
> +	.standby = VOP_REG(RK3229_SYS_CTRL, 0x1, 22),
> +	.gate_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 23),
> +	.mmu_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 20),
> +	.rgb_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 12),
> +	.hdmi_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 13),
> +	.edp_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 14),
> +	.mipi_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 15),
> +	.data_blank = VOP_REG(RK3229_DSP_CTRL0, 0x1, 19),
> +	.out_mode = VOP_REG(RK3229_DSP_CTRL0, 0xf, 0),
> +	.pin_pol = VOP_REG(RK3229_DSP_CTRL1, 0xf, 20),
> +	.dither_up = VOP_REG(RK3229_DSP_CTRL1, 0x1, 6),
> +	.htotal_pw = VOP_REG(RK3229_DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
> +	.hact_st_end = VOP_REG(RK3229_DSP_HACT_ST_END, 0x1fff1fff, 0),
> +	.vtotal_pw = VOP_REG(RK3229_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
> +	.vact_st_end = VOP_REG(RK3229_DSP_VACT_ST_END, 0x1fff1fff, 0),
> +	.hpost_st_end = VOP_REG(RK3229_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
> +	.vpost_st_end = VOP_REG(RK3229_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
> +};
> +
> +static const int rk3229_vop_intrs[] = {
> +	FS_INTR,
> +	DUMMY_INTR,
> +	DUMMY_INTR,
> +	LINE_FLAG_INTR,
> +	DUMMY_INTR,
> +	DUMMY_INTR,
> +	DUMMY_INTR,
> +	DUMMY_INTR,
> +	DUMMY_INTR,
> +	DUMMY_INTR,
> +	DUMMY_INTR,
> +	DUMMY_INTR,
> +	DUMMY_INTR,
> +	DSP_HOLD_VALID_INTR,
> +};
> +
> +static const struct vop_intr rk3229_intr = {
> +	.write_mask = true,
> +	.intrs = rk3229_vop_intrs,
> +	.nintrs = ARRAY_SIZE(rk3229_vop_intrs),
> +	.status = VOP_REG(RK3229_INTR_STATUS0, 0xffffffff, 0),
> +	.clear = VOP_REG(RK3229_INTR_CLEAR0, 0xffffffff, 0),
> +	.enable = VOP_REG(RK3229_INTR_EN0, 0xffffffff, 0),
> +};
> +
> +static const struct vop_reg_data rk3229_vop_init_reg_table[] = {
> +};
> +
> +static const struct vop_data rk3229_vop = {
> +	.init_table = rk3229_vop_init_reg_table,
> +	.table_size = ARRAY_SIZE(rk3229_vop_init_reg_table),
> +	.intr = &rk3229_intr,
> +	.ctrl = &rk3229_ctrl_data,
> +	.win = rk3229_vop_win_data,
> +	.win_size = ARRAY_SIZE(rk3229_vop_win_data),
> +};
> +
>   static const struct vop_scl_regs rk3066_win_scl = {
>   	.scale_yrgb_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
>   	.scale_yrgb_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
> @@ -240,6 +357,7 @@ static const int rk3036_vop_intrs[] = {
>   };
>   
>   static const struct vop_intr rk3036_intr = {
> +	.write_mask = false,
>   	.intrs = rk3036_vop_intrs,
>   	.nintrs = ARRAY_SIZE(rk3036_vop_intrs),
>   	.status = VOP_REG(RK3036_INT_STATUS, 0xf, 0),
> @@ -274,6 +392,8 @@ static const struct vop_data rk3036_vop = {
>   static const struct of_device_id vop_driver_dt_match[] = {
>   	{ .compatible = "rockchip,rk3288-vop",
>   	  .data = &rk3288_vop },
> +	{ .compatible = "rockchip,rk3229-vop",
> +	  .data = &rk3229_vop },
>   	{ .compatible = "rockchip,rk3036-vop",
>   	  .data = &rk3036_vop },
>   	{},
> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
> index d4b46cb..8c86b86 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
> @@ -15,6 +15,96 @@
>   #ifndef _ROCKCHIP_VOP_REG_H
>   #define _ROCKCHIP_VOP_REG_H
>   
> +/* rk3228 register definition */
> +#define RK3228_REG_CFG_DONE			0x0000
> +#define RK3228_VERSION_INFO			0x0004
> +#define RK3228_SYS_CTRL				0x0008
> +#define RK3228_SYS_CTRL1			0x000c
> +#define RK3228_DSP_CTRL0			0x0010
> +#define RK3228_DSP_CTRL1			0x0014
> +#define RK3228_DSP_BG				0x0018
> +#define RK3228_WIN0_CTRL0			0x0030
> +#define RK3228_WIN0_CTRL1			0x0034
> +#define RK3228_WIN0_COLOR_KEY			0x0038
> +#define RK3228_WIN0_VIR				0x003c
> +#define RK3228_WIN0_YRGB_MST			0x0040
> +#define RK3228_WIN0_CBR_MST			0x0044
> +#define RK3228_WIN0_ACT_INFO			0x0048
> +#define RK3228_WIN0_DSP_INFO			0x004c
> +#define RK3228_WIN0_DSP_ST			0x0050
> +#define RK3228_WIN0_SCL_FACTOR_YRGB		0x0054
> +#define RK3228_WIN0_SCL_FACTOR_CBR		0x0058
> +#define RK3228_WIN0_SCL_OFFSET			0x005c
> +#define RK3228_WIN0_SRC_ALPHA_CTRL		0x0060
> +#define RK3228_WIN0_DST_ALPHA_CTRL		0x0064
> +#define RK3228_WIN0_FADING_CTRL			0x0068
> +/* win1 register */
> +#define RK3228_WIN1_CTRL0			0x0070
> +#define RK3228_WIN1_CTRL1			0x0074
> +#define RK3228_WIN1_COLOR_KEY			0x0078
> +#define RK3228_WIN1_VIR				0x007c
> +#define RK3228_WIN1_YRGB_MST			0x0080
> +#define RK3228_WIN1_CBR_MST			0x0084
> +#define RK3228_WIN1_ACT_INFO			0x0088
> +#define RK3228_WIN1_DSP_INFO			0x008c
> +#define RK3228_WIN1_DSP_ST			0x0090
> +#define RK3228_WIN1_SCL_FACTOR_YRGB		0x0094
> +#define RK3228_WIN1_SCL_FACTOR_CBR		0x0098
> +#define RK3228_WIN1_SCL_OFFSET			0x009c
> +#define RK3228_WIN1_SRC_ALPHA_CTRL		0x00a0
> +#define RK3228_WIN1_DST_ALPHA_CTRL		0x00a4
> +#define RK3228_WIN1_FADING_CTRL			0x00a8
> +#define RK3228_WIN1_CTRL2			0x00ac
> +/* hwc register */
> +#define RK3228_HWC_CTRL0			0x0150
> +#define RK3228_HWC_CTRL1			0x0154
> +#define RK3228_HWC_MST				0x0158
> +#define RK3228_HWC_DSP_ST			0x015c
> +#define RK3228_HWC_SRC_ALPHA_CTRL		0x0160
> +#define RK3228_HWC_DST_ALPHA_CTRL		0x0164
> +#define RK3228_HWC_FADING_CTRL			0x0168
> +#define RK3228_HWC_RESERVED			0x016c
> +/* post process register */
> +#define RK3228_POST_DSP_HACT_INFO		0x0170
> +#define RK3228_POST_DSP_VACT_INFO		0x0174
> +#define RK3228_POST_SCL_FACTOR_YRGB		0x0178
> +#define RK3228_POST_RESERVED			0x017c
> +#define RK3228_POST_SCL_CTRL			0x0180
> +#define RK3228_POST_DSP_VACT_INFO_F1		0x0184
> +#define RK3228_DSP_HTOTAL_HS_END		0x0188
> +#define RK3228_DSP_HACT_ST_END			0x018c
> +#define RK3228_DSP_VTOTAL_VS_END		0x0190
> +#define RK3228_DSP_VACT_ST_END			0x0194
> +#define RK3228_DSP_VS_ST_END_F1			0x0198
> +#define RK3228_DSP_VACT_ST_END_F1		0x019c
> +/* Brightness contrast */
> +#define RK3228_BCSH_COLOR_BAR			0x01b0
> +#define RK3228_BCSH_BCS				0x01b4
> +#define RK3228_BCSH_H				0x01b8
> +#define RK3228_BCSH_CTRL			0x01bc
> +/* FRC registers */
> +#define RK3228_FRC_LOWER01_0			0x01e8
> +#define RK3228_FRC_LOWER01_1			0x01ec
> +#define RK3228_FRC_LOWER10_0			0x01f0
> +#define RK3228_FRC_LOWER10_1			0x01f4
> +#define RK3228_FRC_LOWER11_0			0x01f8
> +#define RK3228_FRC_LOWER11_1			0x01fc
> +/* Interrupt registers */
> +#define RK3228_INTR_EN0				0x0280
> +#define RK3228_INTR_CLEAR0			0x0284
> +#define RK3228_INTR_STATUS0			0x0288
> +#define RK3228_INTR_RAW_STATUS0			0x028c
> +#define RK3228_INTR_EN1				0x0290
> +#define RK3228_INTR_CLEAR1			0x0294
> +#define RK3228_INTR_STATUS1			0x0298
> +#define RK3228_INTR_RAW_STATUS1			0x029c
> +#define RK3228_LINE_FLAG			0x02a0
> +#define RK3228_VOP_STATUS			0x02a4
> +#define RK3228_BLANKING_VALUE			0x02a8
> +#define RK3228_WIN0_DSP_BG			0x02b0
> +#define RK3228_WIN1_DSP_BG			0x02b4
> +/* register definition end */
> +
>   /* rk3288 register definition */
>   #define RK3288_REG_CFG_DONE			0x0000
>   #define RK3288_VERSION_INFO			0x0004




From heiko at sntech.de  Mon Jan  4 04:23:41 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Mon, 04 Jan 2016 13:23:41 +0100
Subject: [RFC PATCH v1 1/2] drm/rockchip: vop: add rk3229 vop support
In-Reply-To: <1451908438-26581-1-git-send-email-ykk@rock-chips.com>
References: <1451908218-25278-1-git-send-email-ykk@rock-chips.com>
 <1451908438-26581-1-git-send-email-ykk@rock-chips.com>
Message-ID: <11434893.zL5bTOLgHP@phil>

Hi Yakir,

Am Montag, 4. Januar 2016, 19:53:58 schrieb Yakir Yang:
> RK3229 registers layout is simalar to RK3288 layout, only the
> interruput registers is different to RK3288.
> 
> RK3229 support two overlay plane and one hwc plane, max output
> resolution is 4K. it support IOMMU, and its IOMMU same as rk3288's.
> 
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
> ---
>  drivers/gpu/drm/rockchip/rockchip_drm_vop.c |   7 +-
>  drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   2 +
>  drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 120
> ++++++++++++++++++++++++++++ drivers/gpu/drm/rockchip/rockchip_vop_reg.h
> |  90 +++++++++++++++++++++ 4 files changed, 217 insertions(+), 2
> deletions(-)
> 
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index d83bf87..3c83097
> 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> @@ -63,9 +63,12 @@
>  #define VOP_INTR_SET_TYPE(vop, name, type, v) \
>  	do { \
>  		int i, reg = 0; \
> -		for (i = 0; i < vop->data->intr->nintrs; i++) { \
> -			if (vop->data->intr->intrs[i] & type) \
> +		const struct vop_intr *intr = vop->data->intr; \
> +		for (i = 0; i < intr->nintrs; i++) { \
> +			if (intr->intrs[i] & type) { \
>  				reg |= (v) << i; \
> +				reg |= intr->write_mask ? (1 << (i + 16)) : 0; \
> +			} \
>  		} \
>  		VOP_INTR_SET(vop, name, reg); \
>  	} while (0)

I do believe this part, as well as setting the default .write_mask = false 
for the existing parts should get its own patch + a bit more explanation
on what this does and why it's needed.

---- 8< ----
drm/rockchip: Add support for interrupt registers using write-masks

Some new display-controllers are need to set write-masks to enable writes
to interrupt registers. Allow this to be set on a per-vop basis.
---- 8< ----

or something like that, and then patches 2+3 being the rk3229 support + 
binding.


> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h index 071ff0b..1e839e8
> 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> @@ -60,6 +60,7 @@ struct vop_ctrl {
>  };
> 
>  struct vop_intr {
> +	bool write_mask;
>  	const int *intrs;
>  	uint32_t nintrs;
>  	struct vop_reg enable;
> @@ -136,6 +137,7 @@ struct vop_data {
>  };
> 
>  /* interrupt define */
> +#define DUMMY_INTR			(0 << 0)
>  #define DSP_HOLD_VALID_INTR		(1 << 0)
>  #define FS_INTR				(1 << 1)
>  #define LINE_FLAG_INTR			(1 << 2)
> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c index 3166b46..bbcd128
> 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> @@ -174,6 +174,7 @@ static const int rk3288_vop_intrs[] = {
>  };
> 
>  static const struct vop_intr rk3288_vop_intr = {
> +	.write_mask = false,
>  	.intrs = rk3288_vop_intrs,
>  	.nintrs = ARRAY_SIZE(rk3288_vop_intrs),
>  	.status = VOP_REG(RK3288_INTR_CTRL0, 0xf, 0),
> @@ -190,6 +191,122 @@ static const struct vop_data rk3288_vop = {
>  	.win_size = ARRAY_SIZE(rk3288_vop_win_data),
>  };
> 
> +static const struct vop_scl_extension rk3229_win_full_scl_ext = {
> +	.cbcr_vsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 31),
> +	.cbcr_vsu_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 30),
> +	.cbcr_hsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 28),
> +	.cbcr_ver_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 26),
> +	.cbcr_hor_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 24),
> +	.yrgb_vsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 23),
> +	.yrgb_vsu_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 22),
> +	.yrgb_hsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 20),
> +	.yrgb_ver_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 18),
> +	.yrgb_hor_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 16),
> +	.line_load_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 15),
> +	.cbcr_axi_gather_num = VOP_REG(RK3229_WIN0_CTRL1, 0x7, 12),
> +	.yrgb_axi_gather_num = VOP_REG(RK3229_WIN0_CTRL1, 0xf, 8),
> +	.vsd_cbcr_gt2 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 7),
> +	.vsd_cbcr_gt4 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 6),
> +	.vsd_yrgb_gt2 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 5),
> +	.vsd_yrgb_gt4 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 4),
> +	.bic_coe_sel = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 2),
> +	.cbcr_axi_gather_en = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 1),
> +	.yrgb_axi_gather_en = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 0),
> +	.lb_mode = VOP_REG(RK3229_WIN0_CTRL0, 0x7, 5),
> +};
> +
> +static const struct vop_scl_regs rk3229_win_full_scl = {
> +	.ext = &rk3229_win_full_scl_ext,
> +	.scale_yrgb_x = VOP_REG(RK3229_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
> +	.scale_yrgb_y = VOP_REG(RK3229_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
> +	.scale_cbcr_x = VOP_REG(RK3229_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0),
> +	.scale_cbcr_y = VOP_REG(RK3229_WIN0_SCL_FACTOR_CBR, 0xffff, 16),
> +};
> +
> +static const struct vop_win_phy rk3229_win01_data = {
> +	.scl = &rk3229_win_full_scl,
> +	.data_formats = formats_win_full,
> +	.nformats = ARRAY_SIZE(formats_win_full),
> +	.enable = VOP_REG(RK3229_WIN0_CTRL0, 0x1, 0),
> +	.format = VOP_REG(RK3229_WIN0_CTRL0, 0x7, 1),
> +	.rb_swap = VOP_REG(RK3229_WIN0_CTRL0, 0x1, 12),
> +	.act_info = VOP_REG(RK3229_WIN0_ACT_INFO, 0x1fff1fff, 0),
> +	.dsp_info = VOP_REG(RK3229_WIN0_DSP_INFO, 0x0fff0fff, 0),
> +	.dsp_st = VOP_REG(RK3229_WIN0_DSP_ST, 0x1fff1fff, 0),
> +	.yrgb_mst = VOP_REG(RK3229_WIN0_YRGB_MST, 0xffffffff, 0),
> +	.uv_mst = VOP_REG(RK3229_WIN0_CBR_MST, 0xffffffff, 0),
> +	.yrgb_vir = VOP_REG(RK3229_WIN0_VIR, 0x3fff, 0),
> +	.uv_vir = VOP_REG(RK3229_WIN0_VIR, 0x3fff, 16),
> +	.src_alpha_ctl = VOP_REG(RK3229_WIN0_SRC_ALPHA_CTRL, 0xff, 0),
> +	.dst_alpha_ctl = VOP_REG(RK3229_WIN0_DST_ALPHA_CTRL, 0xff, 0),
> +};
> +
> +static const struct vop_win_data rk3229_vop_win_data[] = {
> +	{ .base = 0x00, .phy = &rk3229_win01_data,
> +	  .type = DRM_PLANE_TYPE_PRIMARY },
> +	{ .base = 0x40, .phy = &rk3229_win01_data,
> +	  .type = DRM_PLANE_TYPE_CURSOR },
> +};
> +
> +static const struct vop_ctrl rk3229_ctrl_data = {
> +	.cfg_done = VOP_REG(RK3229_REG_CFG_DONE, 0x1, 0),
> +	.standby = VOP_REG(RK3229_SYS_CTRL, 0x1, 22),
> +	.gate_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 23),
> +	.mmu_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 20),
> +	.rgb_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 12),
> +	.hdmi_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 13),
> +	.edp_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 14),
> +	.mipi_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 15),
> +	.data_blank = VOP_REG(RK3229_DSP_CTRL0, 0x1, 19),
> +	.out_mode = VOP_REG(RK3229_DSP_CTRL0, 0xf, 0),
> +	.pin_pol = VOP_REG(RK3229_DSP_CTRL1, 0xf, 20),
> +	.dither_up = VOP_REG(RK3229_DSP_CTRL1, 0x1, 6),
> +	.htotal_pw = VOP_REG(RK3229_DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
> +	.hact_st_end = VOP_REG(RK3229_DSP_HACT_ST_END, 0x1fff1fff, 0),
> +	.vtotal_pw = VOP_REG(RK3229_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
> +	.vact_st_end = VOP_REG(RK3229_DSP_VACT_ST_END, 0x1fff1fff, 0),
> +	.hpost_st_end = VOP_REG(RK3229_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
> +	.vpost_st_end = VOP_REG(RK3229_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
> +};
> +
> +static const int rk3229_vop_intrs[] = {
> +	FS_INTR,
> +	DUMMY_INTR,
> +	DUMMY_INTR,

I'm not sure we need that extra "DUMMY_INTR" define - a simple 0 should be 
enough as well to mark the positions as unused.


The rest looks nice
Heiko


> +	LINE_FLAG_INTR,
> +	DUMMY_INTR,
> +	DUMMY_INTR,
> +	DUMMY_INTR,
> +	DUMMY_INTR,
> +	DUMMY_INTR,
> +	DUMMY_INTR,
> +	DUMMY_INTR,
> +	DUMMY_INTR,
> +	DUMMY_INTR,
> +	DSP_HOLD_VALID_INTR,
> +};
> +
> +static const struct vop_intr rk3229_intr = {
> +	.write_mask = true,
> +	.intrs = rk3229_vop_intrs,
> +	.nintrs = ARRAY_SIZE(rk3229_vop_intrs),
> +	.status = VOP_REG(RK3229_INTR_STATUS0, 0xffffffff, 0),
> +	.clear = VOP_REG(RK3229_INTR_CLEAR0, 0xffffffff, 0),
> +	.enable = VOP_REG(RK3229_INTR_EN0, 0xffffffff, 0),
> +};
> +
> +static const struct vop_reg_data rk3229_vop_init_reg_table[] = {
> +};
> +
> +static const struct vop_data rk3229_vop = {
> +	.init_table = rk3229_vop_init_reg_table,
> +	.table_size = ARRAY_SIZE(rk3229_vop_init_reg_table),
> +	.intr = &rk3229_intr,
> +	.ctrl = &rk3229_ctrl_data,
> +	.win = rk3229_vop_win_data,
> +	.win_size = ARRAY_SIZE(rk3229_vop_win_data),
> +};
> +
>  static const struct vop_scl_regs rk3066_win_scl = {
>  	.scale_yrgb_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
>  	.scale_yrgb_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
> @@ -240,6 +357,7 @@ static const int rk3036_vop_intrs[] = {
>  };
> 
>  static const struct vop_intr rk3036_intr = {
> +	.write_mask = false,
>  	.intrs = rk3036_vop_intrs,
>  	.nintrs = ARRAY_SIZE(rk3036_vop_intrs),
>  	.status = VOP_REG(RK3036_INT_STATUS, 0xf, 0),
> @@ -274,6 +392,8 @@ static const struct vop_data rk3036_vop = {
>  static const struct of_device_id vop_driver_dt_match[] = {
>  	{ .compatible = "rockchip,rk3288-vop",
>  	  .data = &rk3288_vop },
> +	{ .compatible = "rockchip,rk3229-vop",
> +	  .data = &rk3229_vop },
>  	{ .compatible = "rockchip,rk3036-vop",
>  	  .data = &rk3036_vop },
>  	{},
> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
> b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h index d4b46cb..0280388
> 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
> @@ -15,6 +15,96 @@
>  #ifndef _ROCKCHIP_VOP_REG_H
>  #define _ROCKCHIP_VOP_REG_H
> 
> +/* rk3229 register definition */
> +#define RK3229_REG_CFG_DONE			0x0000
> +#define RK3229_VERSION_INFO			0x0004
> +#define RK3229_SYS_CTRL				0x0008
> +#define RK3229_SYS_CTRL1			0x000c
> +#define RK3229_DSP_CTRL0			0x0010
> +#define RK3229_DSP_CTRL1			0x0014
> +#define RK3229_DSP_BG				0x0018
> +#define RK3229_WIN0_CTRL0			0x0030
> +#define RK3229_WIN0_CTRL1			0x0034
> +#define RK3229_WIN0_COLOR_KEY			0x0038
> +#define RK3229_WIN0_VIR				0x003c
> +#define RK3229_WIN0_YRGB_MST			0x0040
> +#define RK3229_WIN0_CBR_MST			0x0044
> +#define RK3229_WIN0_ACT_INFO			0x0048
> +#define RK3229_WIN0_DSP_INFO			0x004c
> +#define RK3229_WIN0_DSP_ST			0x0050
> +#define RK3229_WIN0_SCL_FACTOR_YRGB		0x0054
> +#define RK3229_WIN0_SCL_FACTOR_CBR		0x0058
> +#define RK3229_WIN0_SCL_OFFSET			0x005c
> +#define RK3229_WIN0_SRC_ALPHA_CTRL		0x0060
> +#define RK3229_WIN0_DST_ALPHA_CTRL		0x0064
> +#define RK3229_WIN0_FADING_CTRL			0x0068
> +/* win1 register */
> +#define RK3229_WIN1_CTRL0			0x0070
> +#define RK3229_WIN1_CTRL1			0x0074
> +#define RK3229_WIN1_COLOR_KEY			0x0078
> +#define RK3229_WIN1_VIR				0x007c
> +#define RK3229_WIN1_YRGB_MST			0x0080
> +#define RK3229_WIN1_CBR_MST			0x0084
> +#define RK3229_WIN1_ACT_INFO			0x0088
> +#define RK3229_WIN1_DSP_INFO			0x008c
> +#define RK3229_WIN1_DSP_ST			0x0090
> +#define RK3229_WIN1_SCL_FACTOR_YRGB		0x0094
> +#define RK3229_WIN1_SCL_FACTOR_CBR		0x0098
> +#define RK3229_WIN1_SCL_OFFSET			0x009c
> +#define RK3229_WIN1_SRC_ALPHA_CTRL		0x00a0
> +#define RK3229_WIN1_DST_ALPHA_CTRL		0x00a4
> +#define RK3229_WIN1_FADING_CTRL			0x00a8
> +#define RK3229_WIN1_CTRL2			0x00ac
> +/* hwc register */
> +#define RK3229_HWC_CTRL0			0x0150
> +#define RK3229_HWC_CTRL1			0x0154
> +#define RK3229_HWC_MST				0x0158
> +#define RK3229_HWC_DSP_ST			0x015c
> +#define RK3229_HWC_SRC_ALPHA_CTRL		0x0160
> +#define RK3229_HWC_DST_ALPHA_CTRL		0x0164
> +#define RK3229_HWC_FADING_CTRL			0x0168
> +#define RK3229_HWC_RESERVED			0x016c
> +/* post process register */
> +#define RK3229_POST_DSP_HACT_INFO		0x0170
> +#define RK3229_POST_DSP_VACT_INFO		0x0174
> +#define RK3229_POST_SCL_FACTOR_YRGB		0x0178
> +#define RK3229_POST_RESERVED			0x017c
> +#define RK3229_POST_SCL_CTRL			0x0180
> +#define RK3229_POST_DSP_VACT_INFO_F1		0x0184
> +#define RK3229_DSP_HTOTAL_HS_END		0x0188
> +#define RK3229_DSP_HACT_ST_END			0x018c
> +#define RK3229_DSP_VTOTAL_VS_END		0x0190
> +#define RK3229_DSP_VACT_ST_END			0x0194
> +#define RK3229_DSP_VS_ST_END_F1			0x0198
> +#define RK3229_DSP_VACT_ST_END_F1		0x019c
> +/* Brightness contrast */
> +#define RK3229_BCSH_COLOR_BAR			0x01b0
> +#define RK3229_BCSH_BCS				0x01b4
> +#define RK3229_BCSH_H				0x01b8
> +#define RK3229_BCSH_CTRL			0x01bc
> +/* FRC registers */
> +#define RK3229_FRC_LOWER01_0			0x01e8
> +#define RK3229_FRC_LOWER01_1			0x01ec
> +#define RK3229_FRC_LOWER10_0			0x01f0
> +#define RK3229_FRC_LOWER10_1			0x01f4
> +#define RK3229_FRC_LOWER11_0			0x01f8
> +#define RK3229_FRC_LOWER11_1			0x01fc
> +/* Interrupt registers */
> +#define RK3229_INTR_EN0				0x0280
> +#define RK3229_INTR_CLEAR0			0x0284
> +#define RK3229_INTR_STATUS0			0x0288
> +#define RK3229_INTR_RAW_STATUS0			0x028c
> +#define RK3229_INTR_EN1				0x0290
> +#define RK3229_INTR_CLEAR1			0x0294
> +#define RK3229_INTR_STATUS1			0x0298
> +#define RK3229_INTR_RAW_STATUS1			0x029c
> +#define RK3229_LINE_FLAG			0x02a0
> +#define RK3229_VOP_STATUS			0x02a4
> +#define RK3229_BLANKING_VALUE			0x02a8
> +#define RK3229_WIN0_DSP_BG			0x02b0
> +#define RK3229_WIN1_DSP_BG			0x02b4
> +/* register definition end */
> +
>  /* rk3288 register definition */
>  #define RK3288_REG_CFG_DONE			0x0000
>  #define RK3288_VERSION_INFO			0x0004



From a.zummo at towertech.it  Mon Jan  4 05:59:38 2016
From: a.zummo at towertech.it (Alessandro Zummo)
Date: Mon, 4 Jan 2016 14:59:38 +0100
Subject: [RESEND PATCH] rtc: rk808: rename rtc-rk808.c to rtc-rk8xx.c
In-Reply-To: <20160104094546.GB32724@piout.net>
References: <1451571295-4193-1-git-send-email-zhangqing@rock-chips.com>
 <20160104094546.GB32724@piout.net>
Message-ID: <20160104145938.6c053b5a@linux.lan.towertech.it>

On Mon, 4 Jan 2016 10:45:46 +0100
Alexandre Belloni <alexandre.belloni at free-electrons.com> wrote:

> I'm not sure it is useful to do that renaming. It is usual to have one
> driver that supports multiple chips named with the forst chip it
> supported.
> 
> Also, what would happen if for example rk855 is not compatible at all
> with the previous implementations?

 Alexandre is absolutely right. There's no need to rename a driver,
 it would just piss off people who are used to that name and
 have it in their scripts. Like when your eth0 gets renamed
 to some obscure enXXX <g>.

-- 

 Best regards,

 Alessandro Zummo - CEO,
  Tower Technologies - Torino, Italy

  http://www.towertech.it



From robh at kernel.org  Mon Jan  4 06:11:21 2016
From: robh at kernel.org (Rob Herring)
Date: Mon, 4 Jan 2016 08:11:21 -0600
Subject: [RFC PATCH v1 2/2] dt-bindings: add document for rk3229-vop
In-Reply-To: <1451908230-25329-1-git-send-email-ykk@rock-chips.com>
References: <1451908230-25329-1-git-send-email-ykk@rock-chips.com>
Message-ID: <20160104141121.GA22245@rob-hp-laptop>

On Mon, Jan 04, 2016 at 07:50:30PM +0800, Yakir Yang wrote:
> 
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
> ---
>  Documentation/devicetree/bindings/display/rockchip/rockchip-vop.txt | 1 +
>  1 file changed, 1 insertion(+)

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


From wsa at the-dreams.de  Mon Jan  4 11:40:42 2016
From: wsa at the-dreams.de (Wolfram Sang)
Date: Mon, 4 Jan 2016 20:40:42 +0100
Subject: [PATCH v1 1/3] i2c: rk3x: add calc_divs ops for new version
In-Reply-To: <CAHp75VfkteDvC0KUXOXDik9rQeanTXLn9Qq3wux-0rxk5ZotNA@mail.gmail.com>
References: <1449844384-6236-1-git-send-email-wdc@rock-chips.com>
 <566F5FAC.3080404@rock-chips.com>
 <CAHp75VfkteDvC0KUXOXDik9rQeanTXLn9Qq3wux-0rxk5ZotNA@mail.gmail.com>
Message-ID: <20160104194042.GD1522@katana>

> >> +static int rk3x_i2c_v0_calc_divs(unsigned long clk_rate, unsigned long
> >> scl_rate,
> >> +                                unsigned long scl_rise_ns,
> >> +                                unsigned long scl_fall_ns,
> >> +                                unsigned long sda_fall_ns,
> 
> Wolfram did some sturct to assign the parameters from device properties.
> It might be re-used here.

Yes, I think it makes sense to convert the driver first to use
the new i2c_parse_fw_timings() function and see if it fits (or if we need
to extend it perhaps).

David, does this make sense to you?

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160104/dbabbb89/attachment.sig>

From shawn.lin at rock-chips.com  Mon Jan  4 17:07:14 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Tue,  5 Jan 2016 09:07:14 +0800
Subject: [PATCH v2 1/2] Documentation: bindings: add dt documentation for
 Rockchip eMMC PHY
Message-ID: <1451956034-24421-1-git-send-email-shawn.lin@rock-chips.com>

This patch adds a binding that describes the Rockchip eMMC PHYs
found on Rockchip SoCs eMMC interface.

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
---

Changes in v2: None

 .../devicetree/bindings/phy/rockchip-emmc-phy.txt     | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/rockchip-emmc-phy.txt

diff --git a/Documentation/devicetree/bindings/phy/rockchip-emmc-phy.txt b/Documentation/devicetree/bindings/phy/rockchip-emmc-phy.txt
new file mode 100644
index 0000000..61916f1
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/rockchip-emmc-phy.txt
@@ -0,0 +1,19 @@
+Rockchip EMMC PHY
+-----------------------
+
+Required properties:
+ - compatible: rockchip,rk3399-emmc-phy
+ - rockchip,grf : phandle to the syscon managing the "general
+   register files"
+ - #phy-cells: must be 0
+ - reg: PHY configure reg address offset in "general
+   register files"
+
+Example:
+
+emmcphy: phy {
+	compatible = "rockchip,rk3399-emmc-phy";
+	rockchip,grf = <&grf>;
+	reg = <0xf780>;
+	#phy-cells = <0>;
+};
-- 
2.3.7




From shawn.lin at rock-chips.com  Mon Jan  4 17:08:01 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Tue,  5 Jan 2016 09:08:01 +0800
Subject: [PATCH v2 2/2] phy: add a driver for the Rockchip SoC internal eMMC
 PHY
In-Reply-To: <1451956034-24421-1-git-send-email-shawn.lin@rock-chips.com>
References: <1451956034-24421-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <1451956081-24501-1-git-send-email-shawn.lin@rock-chips.com>

This patch to add a generic PHY driver for ROCKCHIP eMMC PHY.
Access the PHY via registers provided by GRF (general register
files) module.

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>

---

Changes in v2:
- remove phy from rockchip_emmc_phy
- remove state from rockchip_emmc_phy
- remove on/off from init/exit

 drivers/phy/Kconfig             |   7 ++
 drivers/phy/Makefile            |   1 +
 drivers/phy/phy-rockchip-emmc.c | 241 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 249 insertions(+)
 create mode 100644 drivers/phy/phy-rockchip-emmc.c

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 03cb3ea..47d16e3 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -320,6 +320,13 @@ config PHY_ROCKCHIP_USB
 	help
 	  Enable this to support the Rockchip USB 2.0 PHY.
 
+config PHY_ROCKCHIP_EMMC
+	tristate "Rockchip EMMC PHY Driver"
+	depends on ARCH_ROCKCHIP && OF
+	select GENERIC_PHY
+	help
+	  Enable this to support the Rockchip EMMC PHY.
+
 config PHY_ST_SPEAR1310_MIPHY
 	tristate "ST SPEAR1310-MIPHY driver"
 	select GENERIC_PHY
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 075db1a..ed8f175 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -35,6 +35,7 @@ phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2)	+= phy-s5pv210-usb2.o
 obj-$(CONFIG_PHY_EXYNOS5_USBDRD)	+= phy-exynos5-usbdrd.o
 obj-$(CONFIG_PHY_QCOM_APQ8064_SATA)	+= phy-qcom-apq8064-sata.o
 obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
+obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o
 obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA)	+= phy-qcom-ipq806x-sata.o
 obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY)	+= phy-spear1310-miphy.o
 obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)	+= phy-spear1340-miphy.o
diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c
new file mode 100644
index 0000000..6a157c2
--- /dev/null
+++ b/drivers/phy/phy-rockchip-emmc.c
@@ -0,0 +1,241 @@
+/*
+ * Rockchip emmc PHY driver
+ *
+ * Copyright (C) 2015 Shawn Lin <shawn.lin at rock-chips.com>
+ * Copyright (C) 2015 ROCKCHIP, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+/*
+ * The higher 16-bit of this register is used for write protection
+ * only if BIT(x + 16) set to 1 the BIT(x) can be written.
+ */
+#define HIWORD_UPDATE(val, mask, shift) \
+		((val) << (shift) | (mask) << ((shift) + 16))
+
+/* Register definition */
+#define GRF_EMMCPHY_CON0	0x0
+#define GRF_EMMCPHY_CON1	0x4
+#define GRF_EMMCPHY_CON2	0x8
+#define GRF_EMMCPHY_CON3	0xc
+#define GRF_EMMCPHY_CON4	0x10
+#define GRF_EMMCPHY_CON5	0x14
+#define GRF_EMMCPHY_CON6	0x18
+#define GRF_EMMCPHY_STATUS	0x20
+
+#define PHYCTRL_PDB_MASK	0x1
+#define PHYCTRL_PDB_SHIFT	0x0
+#define PHYCTRL_PDB_PWR_ON	0x1
+#define PHYCTRL_PDB_PWR_OFF	0x0
+#define PHYCTRL_ENDLL_MASK	0x1
+#define PHYCTRL_ENDLL_SHIFT     0x1
+#define PHYCTRL_ENDLL_ENABLE	0x1
+#define PHYCTRL_ENDLL_DISABLE	0x0
+#define PHYCTRL_CALDONE_MASK	0x1
+#define PHYCTRL_CALDONE_SHIFT   0x6
+#define PHYCTRL_CALDONE_DONE	0x1
+#define PHYCTRL_CALDONE_GOING	0x0
+#define PHYCTRL_DLLRDY_MASK	0x1
+#define PHYCTRL_DLLRDY_SHIFT	0x5
+#define PHYCTRL_DLLRDY_DONE	0x1
+#define PHYCTRL_DLLRDY_GOING	0x0
+
+struct rockchip_emmc_phy {
+	unsigned int	reg_offset;
+	struct regmap	*reg_base;
+};
+
+static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy,
+				   bool on_off)
+{
+	unsigned int caldone;
+	unsigned int dllrdy;
+
+	/*
+	 * Keep phyctrl_pdb and phyctrl_endll low to allow
+	 * initialization of CALIO state M/C DFFs
+	 */
+	regmap_write(rk_phy->reg_base,
+		     rk_phy->reg_offset + GRF_EMMCPHY_CON6,
+		     HIWORD_UPDATE(PHYCTRL_PDB_PWR_OFF,
+				   PHYCTRL_PDB_MASK,
+				   PHYCTRL_PDB_SHIFT));
+	regmap_write(rk_phy->reg_base,
+		     rk_phy->reg_offset + GRF_EMMCPHY_CON6,
+		     HIWORD_UPDATE(PHYCTRL_ENDLL_DISABLE,
+				   PHYCTRL_ENDLL_MASK,
+				   PHYCTRL_ENDLL_SHIFT));
+
+	/* Already finish power_off above */
+	if (on_off == PHYCTRL_PDB_PWR_OFF)
+		return 0;
+
+	/*
+	 * According to the user manual, calpad calibration
+	 * cycle takes more than 2us without the minimal recommended
+	 * value, so we may need a little margin here
+	 */
+	udelay(3);
+	regmap_write(rk_phy->reg_base,
+		     rk_phy->reg_offset + GRF_EMMCPHY_CON6,
+		     HIWORD_UPDATE(PHYCTRL_PDB_PWR_ON,
+				   PHYCTRL_PDB_MASK,
+				   PHYCTRL_PDB_SHIFT));
+
+	/*
+	 * According to the user manual, it asks driver to
+	 * wait 5us for calpad busy trimming
+	 */
+	udelay(5);
+	regmap_read(rk_phy->reg_base,
+		    rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
+		    &caldone);
+	caldone = (caldone >> PHYCTRL_CALDONE_SHIFT) & PHYCTRL_CALDONE_MASK;
+	if (caldone != PHYCTRL_CALDONE_DONE) {
+		pr_err("rockchip_emmc_phy_power: caldone timeout.\n");
+		return -ETIMEDOUT;
+	}
+
+	/*
+	 * Enable analog DLL circuits, and we need extra 10.2us
+	 * for dll to be ready for work.
+	 */
+	regmap_write(rk_phy->reg_base,
+		     rk_phy->reg_offset + GRF_EMMCPHY_CON6,
+		     HIWORD_UPDATE(PHYCTRL_ENDLL_ENABLE,
+				   PHYCTRL_ENDLL_MASK,
+				   PHYCTRL_ENDLL_SHIFT));
+	udelay(11);
+	regmap_read(rk_phy->reg_base,
+		    rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
+		    &dllrdy);
+	dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK;
+	if (dllrdy != PHYCTRL_DLLRDY_DONE) {
+		pr_err("rockchip_emmc_phy_power: dllrdy timeout.\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int rockchip_emmc_phy_power_off(struct phy *phy)
+{
+	struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
+	int ret = 0;
+
+	/* Power down emmc phy analog blocks */
+	ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_OFF);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int rockchip_emmc_phy_power_on(struct phy *phy)
+{
+	struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
+	int ret = 0;
+
+	/* Power up emmc phy analog blocks */
+	ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_ON);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int rockchip_emmc_phy_init(struct phy *phy)
+{
+	return 0;
+}
+
+static int rockchip_emmc_phy_exit(struct phy *phy)
+{
+	return 0;
+}
+
+static const struct phy_ops ops = {
+	.init		= rockchip_emmc_phy_init,
+	.exit		= rockchip_emmc_phy_exit,
+	.power_on	= rockchip_emmc_phy_power_on,
+	.power_off	= rockchip_emmc_phy_power_off,
+	.owner		= THIS_MODULE,
+};
+
+static int rockchip_emmc_phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rockchip_emmc_phy *rk_phy;
+	struct phy *generic_phy;
+	struct phy_provider *phy_provider;
+	struct regmap *grf;
+	unsigned int reg_offset;
+
+	grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
+	if (IS_ERR(grf)) {
+		dev_err(dev, "Missing rockchip,grf property\n");
+		return PTR_ERR(grf);
+	}
+
+	rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL);
+	if (!rk_phy)
+		return -ENOMEM;
+
+	if (of_property_read_u32(dev->of_node, "reg", &reg_offset)) {
+		dev_err(dev, "missing reg property in node %s\n",
+			dev->of_node->name);
+		return -EINVAL;
+	}
+
+	rk_phy->reg_offset = reg_offset;
+	rk_phy->reg_base = grf;
+
+	generic_phy = devm_phy_create(dev, dev->of_node, &ops);
+	if (IS_ERR(generic_phy)) {
+		dev_err(dev, "failed to create PHY\n");
+		return PTR_ERR(generic_phy);
+	}
+
+	phy_set_drvdata(generic_phy, rk_phy);
+	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+	return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id rockchip_emmc_phy_dt_ids[] = {
+	{ .compatible = "rockchip,rk3399-emmc-phy" },
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, rockchip_emmc_phy_dt_ids);
+
+static struct platform_driver rockchip_emmc_driver = {
+	.probe		= rockchip_emmc_phy_probe,
+	.driver		= {
+		.name	= "rockchip-emmc-phy",
+		.of_match_table = rockchip_emmc_phy_dt_ids,
+	},
+};
+
+module_platform_driver(rockchip_emmc_driver);
+
+MODULE_AUTHOR("Shawn Lin <shawn.lin at rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip EMMC PHY driver");
+MODULE_LICENSE("GPL v2");
-- 
2.3.7




From jay.xu at rock-chips.com  Mon Jan  4 19:02:18 2016
From: jay.xu at rock-chips.com (jianqun.xu)
Date: Tue,  5 Jan 2016 11:02:18 +0800
Subject: [PATCH] i2c: rk3x: init module as subsys call
Message-ID: <1451962938-17398-1-git-send-email-jay.xu@rock-chips.com>

From: Xu Jianqun <jay.xu at rock-chips.com>

There is a requirement from pmic device, which is on the i2c bus,
that the pmic needs to be called earlier then devices powered by
the outputs of the pmic, if not, the devices maybe fail to probe.

For example, a pmic on i2c0, and touchscreen device on i2c2,
i2c0: - pmic(rk818)
i2c2: - ts(gt911), powered by rk818 on i2c0

The problem will happen if the i2c2 node in dts file is ordered
before i2c0 node, then ts(gt911) will be probed before pmic(rk818),
since the power from the pmic(rk818) for ts(gt911) hasn't enabled,
so ts(gt911) will fail to probe due to the failure of i2c test.

But if we set the i2c0 node before i2c2, there is no this issue.

The stable way to make sure that pmic can be intalized before other
peripher devices is to make the pmic module be subsys call, the i2c
module need to be subsys call firstly.

Signed-off-by: Xu Jianqun <jay.xu at rock-chips.com>
---
 drivers/i2c/busses/i2c-rk3x.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c
index c1935eb..00e5959 100644
--- a/drivers/i2c/busses/i2c-rk3x.c
+++ b/drivers/i2c/busses/i2c-rk3x.c
@@ -1037,7 +1037,17 @@ static struct platform_driver rk3x_i2c_driver = {
 	},
 };
 
-module_platform_driver(rk3x_i2c_driver);
+static int __init rk3x_i2c_init(void)
+{
+	return platform_driver_register(&rk3x_i2c_driver);
+}
+subsys_initcall(rk3x_i2c_init);
+
+static void __exit rk3x_i2c_exit(void)
+{
+	platform_driver_unregister(&rk3x_i2c_driver);
+}
+module_exit(rk3x_i2c_exit);
 
 MODULE_DESCRIPTION("Rockchip RK3xxx I2C Bus driver");
 MODULE_AUTHOR("Max Schwarz <max.schwarz at online.de>");
-- 
1.9.1




From ykk at rock-chips.com  Mon Jan  4 19:16:21 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Tue, 5 Jan 2016 11:16:21 +0800
Subject: [RFC PATCH v1 2/2] dt-bindings: add document for rk3229-vop
In-Reply-To: <20160104141121.GA22245@rob-hp-laptop>
References: <1451908230-25329-1-git-send-email-ykk@rock-chips.com>
 <20160104141121.GA22245@rob-hp-laptop>
Message-ID: <568B3585.6010704@rock-chips.com>

Thanks, Rob

- Yakir

On 01/04/2016 10:11 PM, Rob Herring wrote:
> On Mon, Jan 04, 2016 at 07:50:30PM +0800, Yakir Yang wrote:
>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
>> ---
>>   Documentation/devicetree/bindings/display/rockchip/rockchip-vop.txt | 1 +
>>   1 file changed, 1 insertion(+)
> Acked-by: Rob Herring <robh at kernel.org>
>
>
>




From ykk at rock-chips.com  Mon Jan  4 19:21:15 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Tue, 5 Jan 2016 11:21:15 +0800
Subject: [RFC PATCH v1 1/2] drm/rockchip: vop: add rk3229 vop support
In-Reply-To: <11434893.zL5bTOLgHP@phil>
References: <1451908218-25278-1-git-send-email-ykk@rock-chips.com>
 <1451908438-26581-1-git-send-email-ykk@rock-chips.com>
 <11434893.zL5bTOLgHP@phil>
Message-ID: <568B36AB.4070105@rock-chips.com>

Hi Heiko,

On 01/04/2016 08:23 PM, Heiko Stuebner wrote:
> Hi Yakir,
>
> Am Montag, 4. Januar 2016, 19:53:58 schrieb Yakir Yang:
>> RK3229 registers layout is simalar to RK3288 layout, only the
>> interruput registers is different to RK3288.
>>
>> RK3229 support two overlay plane and one hwc plane, max output
>> resolution is 4K. it support IOMMU, and its IOMMU same as rk3288's.
>>
>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
>> ---
>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.c |   7 +-
>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   2 +
>>   drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 120
>> ++++++++++++++++++++++++++++ drivers/gpu/drm/rockchip/rockchip_vop_reg.h
>> |  90 +++++++++++++++++++++ 4 files changed, 217 insertions(+), 2
>> deletions(-)
>>
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index d83bf87..3c83097
>> 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> @@ -63,9 +63,12 @@
>>   #define VOP_INTR_SET_TYPE(vop, name, type, v) \
>>   	do { \
>>   		int i, reg = 0; \
>> -		for (i = 0; i < vop->data->intr->nintrs; i++) { \
>> -			if (vop->data->intr->intrs[i] & type) \
>> +		const struct vop_intr *intr = vop->data->intr; \
>> +		for (i = 0; i < intr->nintrs; i++) { \
>> +			if (intr->intrs[i] & type) { \
>>   				reg |= (v) << i; \
>> +				reg |= intr->write_mask ? (1 << (i + 16)) : 0; \
>> +			} \
>>   		} \
>>   		VOP_INTR_SET(vop, name, reg); \
>>   	} while (0)
> I do believe this part, as well as setting the default .write_mask = false
> for the existing parts should get its own patch + a bit more explanation
> on what this does and why it's needed.
>
> ---- 8< ----
> drm/rockchip: Add support for interrupt registers using write-masks
>
> Some new display-controllers are need to set write-masks to enable writes
> to interrupt registers. Allow this to be set on a per-vop basis.
> ---- 8< ----
>
> or something like that, and then patches 2+3 being the rk3229 support +
> binding.

Great, will send the new version out.

>
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>> b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h index 071ff0b..1e839e8
>> 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>> @@ -60,6 +60,7 @@ struct vop_ctrl {
>>   };
>>
>>   struct vop_intr {
>> +	bool write_mask;
>>   	const int *intrs;
>>   	uint32_t nintrs;
>>   	struct vop_reg enable;
>> @@ -136,6 +137,7 @@ struct vop_data {
>>   };
>>
>>   /* interrupt define */
>> +#define DUMMY_INTR			(0 << 0)
>>   #define DSP_HOLD_VALID_INTR		(1 << 0)
>>   #define FS_INTR				(1 << 1)
>>   #define LINE_FLAG_INTR			(1 << 2)
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>> b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c index 3166b46..bbcd128
>> 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>> @@ -174,6 +174,7 @@ static const int rk3288_vop_intrs[] = {
>>   };
>>
>>   static const struct vop_intr rk3288_vop_intr = {
>> +	.write_mask = false,
>>   	.intrs = rk3288_vop_intrs,
>>   	.nintrs = ARRAY_SIZE(rk3288_vop_intrs),
>>   	.status = VOP_REG(RK3288_INTR_CTRL0, 0xf, 0),
>> @@ -190,6 +191,122 @@ static const struct vop_data rk3288_vop = {
>>   	.win_size = ARRAY_SIZE(rk3288_vop_win_data),
>>   };
>>
>> +static const struct vop_scl_extension rk3229_win_full_scl_ext = {
>> +	.cbcr_vsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 31),
>> +	.cbcr_vsu_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 30),
>> +	.cbcr_hsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 28),
>> +	.cbcr_ver_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 26),
>> +	.cbcr_hor_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 24),
>> +	.yrgb_vsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 23),
>> +	.yrgb_vsu_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 22),
>> +	.yrgb_hsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 20),
>> +	.yrgb_ver_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 18),
>> +	.yrgb_hor_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 16),
>> +	.line_load_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 15),
>> +	.cbcr_axi_gather_num = VOP_REG(RK3229_WIN0_CTRL1, 0x7, 12),
>> +	.yrgb_axi_gather_num = VOP_REG(RK3229_WIN0_CTRL1, 0xf, 8),
>> +	.vsd_cbcr_gt2 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 7),
>> +	.vsd_cbcr_gt4 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 6),
>> +	.vsd_yrgb_gt2 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 5),
>> +	.vsd_yrgb_gt4 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 4),
>> +	.bic_coe_sel = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 2),
>> +	.cbcr_axi_gather_en = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 1),
>> +	.yrgb_axi_gather_en = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 0),
>> +	.lb_mode = VOP_REG(RK3229_WIN0_CTRL0, 0x7, 5),
>> +};
>> +
>> +static const struct vop_scl_regs rk3229_win_full_scl = {
>> +	.ext = &rk3229_win_full_scl_ext,
>> +	.scale_yrgb_x = VOP_REG(RK3229_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
>> +	.scale_yrgb_y = VOP_REG(RK3229_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
>> +	.scale_cbcr_x = VOP_REG(RK3229_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0),
>> +	.scale_cbcr_y = VOP_REG(RK3229_WIN0_SCL_FACTOR_CBR, 0xffff, 16),
>> +};
>> +
>> +static const struct vop_win_phy rk3229_win01_data = {
>> +	.scl = &rk3229_win_full_scl,
>> +	.data_formats = formats_win_full,
>> +	.nformats = ARRAY_SIZE(formats_win_full),
>> +	.enable = VOP_REG(RK3229_WIN0_CTRL0, 0x1, 0),
>> +	.format = VOP_REG(RK3229_WIN0_CTRL0, 0x7, 1),
>> +	.rb_swap = VOP_REG(RK3229_WIN0_CTRL0, 0x1, 12),
>> +	.act_info = VOP_REG(RK3229_WIN0_ACT_INFO, 0x1fff1fff, 0),
>> +	.dsp_info = VOP_REG(RK3229_WIN0_DSP_INFO, 0x0fff0fff, 0),
>> +	.dsp_st = VOP_REG(RK3229_WIN0_DSP_ST, 0x1fff1fff, 0),
>> +	.yrgb_mst = VOP_REG(RK3229_WIN0_YRGB_MST, 0xffffffff, 0),
>> +	.uv_mst = VOP_REG(RK3229_WIN0_CBR_MST, 0xffffffff, 0),
>> +	.yrgb_vir = VOP_REG(RK3229_WIN0_VIR, 0x3fff, 0),
>> +	.uv_vir = VOP_REG(RK3229_WIN0_VIR, 0x3fff, 16),
>> +	.src_alpha_ctl = VOP_REG(RK3229_WIN0_SRC_ALPHA_CTRL, 0xff, 0),
>> +	.dst_alpha_ctl = VOP_REG(RK3229_WIN0_DST_ALPHA_CTRL, 0xff, 0),
>> +};
>> +
>> +static const struct vop_win_data rk3229_vop_win_data[] = {
>> +	{ .base = 0x00, .phy = &rk3229_win01_data,
>> +	  .type = DRM_PLANE_TYPE_PRIMARY },
>> +	{ .base = 0x40, .phy = &rk3229_win01_data,
>> +	  .type = DRM_PLANE_TYPE_CURSOR },
>> +};
>> +
>> +static const struct vop_ctrl rk3229_ctrl_data = {
>> +	.cfg_done = VOP_REG(RK3229_REG_CFG_DONE, 0x1, 0),
>> +	.standby = VOP_REG(RK3229_SYS_CTRL, 0x1, 22),
>> +	.gate_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 23),
>> +	.mmu_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 20),
>> +	.rgb_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 12),
>> +	.hdmi_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 13),
>> +	.edp_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 14),
>> +	.mipi_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 15),
>> +	.data_blank = VOP_REG(RK3229_DSP_CTRL0, 0x1, 19),
>> +	.out_mode = VOP_REG(RK3229_DSP_CTRL0, 0xf, 0),
>> +	.pin_pol = VOP_REG(RK3229_DSP_CTRL1, 0xf, 20),
>> +	.dither_up = VOP_REG(RK3229_DSP_CTRL1, 0x1, 6),
>> +	.htotal_pw = VOP_REG(RK3229_DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
>> +	.hact_st_end = VOP_REG(RK3229_DSP_HACT_ST_END, 0x1fff1fff, 0),
>> +	.vtotal_pw = VOP_REG(RK3229_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
>> +	.vact_st_end = VOP_REG(RK3229_DSP_VACT_ST_END, 0x1fff1fff, 0),
>> +	.hpost_st_end = VOP_REG(RK3229_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>> +	.vpost_st_end = VOP_REG(RK3229_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
>> +};
>> +
>> +static const int rk3229_vop_intrs[] = {
>> +	FS_INTR,
>> +	DUMMY_INTR,
>> +	DUMMY_INTR,
> I'm not sure we need that extra "DUMMY_INTR" define - a simple 0 should be
> enough as well to mark the positions as unused.
>

Done

Thanks,
- Yakir

> The rest looks nice
> Heiko
>
>
>> +	LINE_FLAG_INTR,
>> +	DUMMY_INTR,
>> +	DUMMY_INTR,
>> +	DUMMY_INTR,
>> +	DUMMY_INTR,
>> +	DUMMY_INTR,
>> +	DUMMY_INTR,
>> +	DUMMY_INTR,
>> +	DUMMY_INTR,
>> +	DUMMY_INTR,
>> +	DSP_HOLD_VALID_INTR,
>> +};
>> +
>> +static const struct vop_intr rk3229_intr = {
>> +	.write_mask = true,
>> +	.intrs = rk3229_vop_intrs,
>> +	.nintrs = ARRAY_SIZE(rk3229_vop_intrs),
>> +	.status = VOP_REG(RK3229_INTR_STATUS0, 0xffffffff, 0),
>> +	.clear = VOP_REG(RK3229_INTR_CLEAR0, 0xffffffff, 0),
>> +	.enable = VOP_REG(RK3229_INTR_EN0, 0xffffffff, 0),
>> +};
>> +
>> +static const struct vop_reg_data rk3229_vop_init_reg_table[] = {
>> +};
>> +
>> +static const struct vop_data rk3229_vop = {
>> +	.init_table = rk3229_vop_init_reg_table,
>> +	.table_size = ARRAY_SIZE(rk3229_vop_init_reg_table),
>> +	.intr = &rk3229_intr,
>> +	.ctrl = &rk3229_ctrl_data,
>> +	.win = rk3229_vop_win_data,
>> +	.win_size = ARRAY_SIZE(rk3229_vop_win_data),
>> +};
>> +
>>   static const struct vop_scl_regs rk3066_win_scl = {
>>   	.scale_yrgb_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
>>   	.scale_yrgb_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
>> @@ -240,6 +357,7 @@ static const int rk3036_vop_intrs[] = {
>>   };
>>
>>   static const struct vop_intr rk3036_intr = {
>> +	.write_mask = false,
>>   	.intrs = rk3036_vop_intrs,
>>   	.nintrs = ARRAY_SIZE(rk3036_vop_intrs),
>>   	.status = VOP_REG(RK3036_INT_STATUS, 0xf, 0),
>> @@ -274,6 +392,8 @@ static const struct vop_data rk3036_vop = {
>>   static const struct of_device_id vop_driver_dt_match[] = {
>>   	{ .compatible = "rockchip,rk3288-vop",
>>   	  .data = &rk3288_vop },
>> +	{ .compatible = "rockchip,rk3229-vop",
>> +	  .data = &rk3229_vop },
>>   	{ .compatible = "rockchip,rk3036-vop",
>>   	  .data = &rk3036_vop },
>>   	{},
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
>> b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h index d4b46cb..0280388
>> 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
>> +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
>> @@ -15,6 +15,96 @@
>>   #ifndef _ROCKCHIP_VOP_REG_H
>>   #define _ROCKCHIP_VOP_REG_H
>>
>> +/* rk3229 register definition */
>> +#define RK3229_REG_CFG_DONE			0x0000
>> +#define RK3229_VERSION_INFO			0x0004
>> +#define RK3229_SYS_CTRL				0x0008
>> +#define RK3229_SYS_CTRL1			0x000c
>> +#define RK3229_DSP_CTRL0			0x0010
>> +#define RK3229_DSP_CTRL1			0x0014
>> +#define RK3229_DSP_BG				0x0018
>> +#define RK3229_WIN0_CTRL0			0x0030
>> +#define RK3229_WIN0_CTRL1			0x0034
>> +#define RK3229_WIN0_COLOR_KEY			0x0038
>> +#define RK3229_WIN0_VIR				0x003c
>> +#define RK3229_WIN0_YRGB_MST			0x0040
>> +#define RK3229_WIN0_CBR_MST			0x0044
>> +#define RK3229_WIN0_ACT_INFO			0x0048
>> +#define RK3229_WIN0_DSP_INFO			0x004c
>> +#define RK3229_WIN0_DSP_ST			0x0050
>> +#define RK3229_WIN0_SCL_FACTOR_YRGB		0x0054
>> +#define RK3229_WIN0_SCL_FACTOR_CBR		0x0058
>> +#define RK3229_WIN0_SCL_OFFSET			0x005c
>> +#define RK3229_WIN0_SRC_ALPHA_CTRL		0x0060
>> +#define RK3229_WIN0_DST_ALPHA_CTRL		0x0064
>> +#define RK3229_WIN0_FADING_CTRL			0x0068
>> +/* win1 register */
>> +#define RK3229_WIN1_CTRL0			0x0070
>> +#define RK3229_WIN1_CTRL1			0x0074
>> +#define RK3229_WIN1_COLOR_KEY			0x0078
>> +#define RK3229_WIN1_VIR				0x007c
>> +#define RK3229_WIN1_YRGB_MST			0x0080
>> +#define RK3229_WIN1_CBR_MST			0x0084
>> +#define RK3229_WIN1_ACT_INFO			0x0088
>> +#define RK3229_WIN1_DSP_INFO			0x008c
>> +#define RK3229_WIN1_DSP_ST			0x0090
>> +#define RK3229_WIN1_SCL_FACTOR_YRGB		0x0094
>> +#define RK3229_WIN1_SCL_FACTOR_CBR		0x0098
>> +#define RK3229_WIN1_SCL_OFFSET			0x009c
>> +#define RK3229_WIN1_SRC_ALPHA_CTRL		0x00a0
>> +#define RK3229_WIN1_DST_ALPHA_CTRL		0x00a4
>> +#define RK3229_WIN1_FADING_CTRL			0x00a8
>> +#define RK3229_WIN1_CTRL2			0x00ac
>> +/* hwc register */
>> +#define RK3229_HWC_CTRL0			0x0150
>> +#define RK3229_HWC_CTRL1			0x0154
>> +#define RK3229_HWC_MST				0x0158
>> +#define RK3229_HWC_DSP_ST			0x015c
>> +#define RK3229_HWC_SRC_ALPHA_CTRL		0x0160
>> +#define RK3229_HWC_DST_ALPHA_CTRL		0x0164
>> +#define RK3229_HWC_FADING_CTRL			0x0168
>> +#define RK3229_HWC_RESERVED			0x016c
>> +/* post process register */
>> +#define RK3229_POST_DSP_HACT_INFO		0x0170
>> +#define RK3229_POST_DSP_VACT_INFO		0x0174
>> +#define RK3229_POST_SCL_FACTOR_YRGB		0x0178
>> +#define RK3229_POST_RESERVED			0x017c
>> +#define RK3229_POST_SCL_CTRL			0x0180
>> +#define RK3229_POST_DSP_VACT_INFO_F1		0x0184
>> +#define RK3229_DSP_HTOTAL_HS_END		0x0188
>> +#define RK3229_DSP_HACT_ST_END			0x018c
>> +#define RK3229_DSP_VTOTAL_VS_END		0x0190
>> +#define RK3229_DSP_VACT_ST_END			0x0194
>> +#define RK3229_DSP_VS_ST_END_F1			0x0198
>> +#define RK3229_DSP_VACT_ST_END_F1		0x019c
>> +/* Brightness contrast */
>> +#define RK3229_BCSH_COLOR_BAR			0x01b0
>> +#define RK3229_BCSH_BCS				0x01b4
>> +#define RK3229_BCSH_H				0x01b8
>> +#define RK3229_BCSH_CTRL			0x01bc
>> +/* FRC registers */
>> +#define RK3229_FRC_LOWER01_0			0x01e8
>> +#define RK3229_FRC_LOWER01_1			0x01ec
>> +#define RK3229_FRC_LOWER10_0			0x01f0
>> +#define RK3229_FRC_LOWER10_1			0x01f4
>> +#define RK3229_FRC_LOWER11_0			0x01f8
>> +#define RK3229_FRC_LOWER11_1			0x01fc
>> +/* Interrupt registers */
>> +#define RK3229_INTR_EN0				0x0280
>> +#define RK3229_INTR_CLEAR0			0x0284
>> +#define RK3229_INTR_STATUS0			0x0288
>> +#define RK3229_INTR_RAW_STATUS0			0x028c
>> +#define RK3229_INTR_EN1				0x0290
>> +#define RK3229_INTR_CLEAR1			0x0294
>> +#define RK3229_INTR_STATUS1			0x0298
>> +#define RK3229_INTR_RAW_STATUS1			0x029c
>> +#define RK3229_LINE_FLAG			0x02a0
>> +#define RK3229_VOP_STATUS			0x02a4
>> +#define RK3229_BLANKING_VALUE			0x02a8
>> +#define RK3229_WIN0_DSP_BG			0x02b0
>> +#define RK3229_WIN1_DSP_BG			0x02b4
>> +/* register definition end */
>> +
>>   /* rk3288 register definition */
>>   #define RK3288_REG_CFG_DONE			0x0000
>>   #define RK3288_VERSION_INFO			0x0004
>
>
>




From ykk at rock-chips.com  Mon Jan  4 19:48:08 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Tue,  5 Jan 2016 11:48:08 +0800
Subject: [PATCH v2 0/3] Add RK3229 vop support
Message-ID: <1451965688-12286-1-git-send-email-ykk@rock-chips.com>


Based on Mark Yao's drm-next branch
[https://github.com/markyzq/kernel-drm-rockchip/tree/drm-rockchip-next-2015-12-28]


Changes in v2:
- Add this patch in v2 (Heiko)
- Separate the write-mask changes out, and remove the DUMMY_INTR marcos (Heiko)
- acked by Rob

Yakir Yang (3):
  drm/rockchip: vop: Add support for interrupt registers using
    write-masks
  drm/rockchip: vop: add rk3229 vop support
  dt-bindings: add document for rk3229-vop

 .../bindings/display/rockchip/rockchip-vop.txt     |   1 +
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c        |   7 +-
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h        |   1 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c        | 112 +++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_vop_reg.h        |  90 +++++++++++++++++
 5 files changed, 209 insertions(+), 2 deletions(-)

-- 
1.9.1




From ykk at rock-chips.com  Mon Jan  4 19:50:58 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Tue,  5 Jan 2016 11:50:58 +0800
Subject: [PATCH v2 1/3] drm/rockchip: vop: Add support for interrupt registers
 using write-masks
In-Reply-To: <1451965688-12286-1-git-send-email-ykk@rock-chips.com>
References: <1451965688-12286-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1451965858-12341-1-git-send-email-ykk@rock-chips.com>

Some new display-controllers are need to set write-masks to enable writes
to interrupt registers. Allow this to be set on a per-vop basis.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v2:
- Add this patch in v2 (Heiko)

 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 7 +++++--
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 1 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 2 ++
 3 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index d83bf87..3c83097 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -63,9 +63,12 @@
 #define VOP_INTR_SET_TYPE(vop, name, type, v) \
 	do { \
 		int i, reg = 0; \
-		for (i = 0; i < vop->data->intr->nintrs; i++) { \
-			if (vop->data->intr->intrs[i] & type) \
+		const struct vop_intr *intr = vop->data->intr; \
+		for (i = 0; i < intr->nintrs; i++) { \
+			if (intr->intrs[i] & type) { \
 				reg |= (v) << i; \
+				reg |= intr->write_mask ? (1 << (i + 16)) : 0; \
+			} \
 		} \
 		VOP_INTR_SET(vop, name, reg); \
 	} while (0)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index 071ff0b..f1bb84d 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -60,6 +60,7 @@ struct vop_ctrl {
 };
 
 struct vop_intr {
+	bool write_mask;
 	const int *intrs;
 	uint32_t nintrs;
 	struct vop_reg enable;
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 3166b46..7fbaf76 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -174,6 +174,7 @@ static const int rk3288_vop_intrs[] = {
 };
 
 static const struct vop_intr rk3288_vop_intr = {
+	.write_mask = false,
 	.intrs = rk3288_vop_intrs,
 	.nintrs = ARRAY_SIZE(rk3288_vop_intrs),
 	.status = VOP_REG(RK3288_INTR_CTRL0, 0xf, 0),
@@ -240,6 +241,7 @@ static const int rk3036_vop_intrs[] = {
 };
 
 static const struct vop_intr rk3036_intr = {
+	.write_mask = false,
 	.intrs = rk3036_vop_intrs,
 	.nintrs = ARRAY_SIZE(rk3036_vop_intrs),
 	.status = VOP_REG(RK3036_INT_STATUS, 0xf, 0),
-- 
1.9.1




From ykk at rock-chips.com  Mon Jan  4 19:58:27 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Tue,  5 Jan 2016 11:58:27 +0800
Subject: [PATCH v2 2/3] drm/rockchip: vop: add rk3229 vop support
In-Reply-To: <1451965688-12286-1-git-send-email-ykk@rock-chips.com>
References: <1451965688-12286-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1451966307-12642-1-git-send-email-ykk@rock-chips.com>

RK3229 registers layout is simalar to RK3288 layout, only the
interruput registers is different to RK3288.

RK3229 support two overlay plane and one hwc plane, max output
resolution is 4K. it support IOMMU, and its IOMMU same as rk3288's.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v2:
- Separate the write-mask changes out, and remove the DUMMY_INTR marcos (Heiko)

 drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 110 ++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_vop_reg.h |  90 +++++++++++++++++++++++
 2 files changed, 200 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 7fbaf76..f1358f9 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -191,6 +191,114 @@ static const struct vop_data rk3288_vop = {
 	.win_size = ARRAY_SIZE(rk3288_vop_win_data),
 };
 
+static const struct vop_scl_extension rk3229_win_full_scl_ext = {
+	.cbcr_vsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 31),
+	.cbcr_vsu_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 30),
+	.cbcr_hsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 28),
+	.cbcr_ver_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 26),
+	.cbcr_hor_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 24),
+	.yrgb_vsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 23),
+	.yrgb_vsu_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 22),
+	.yrgb_hsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 20),
+	.yrgb_ver_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 18),
+	.yrgb_hor_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 16),
+	.line_load_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 15),
+	.cbcr_axi_gather_num = VOP_REG(RK3229_WIN0_CTRL1, 0x7, 12),
+	.yrgb_axi_gather_num = VOP_REG(RK3229_WIN0_CTRL1, 0xf, 8),
+	.vsd_cbcr_gt2 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 7),
+	.vsd_cbcr_gt4 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 6),
+	.vsd_yrgb_gt2 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 5),
+	.vsd_yrgb_gt4 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 4),
+	.bic_coe_sel = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 2),
+	.cbcr_axi_gather_en = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 1),
+	.yrgb_axi_gather_en = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 0),
+	.lb_mode = VOP_REG(RK3229_WIN0_CTRL0, 0x7, 5),
+};
+
+static const struct vop_scl_regs rk3229_win_full_scl = {
+	.ext = &rk3229_win_full_scl_ext,
+	.scale_yrgb_x = VOP_REG(RK3229_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
+	.scale_yrgb_y = VOP_REG(RK3229_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
+	.scale_cbcr_x = VOP_REG(RK3229_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0),
+	.scale_cbcr_y = VOP_REG(RK3229_WIN0_SCL_FACTOR_CBR, 0xffff, 16),
+};
+
+static const struct vop_win_phy rk3229_win01_data = {
+	.scl = &rk3229_win_full_scl,
+	.data_formats = formats_win_full,
+	.nformats = ARRAY_SIZE(formats_win_full),
+	.enable = VOP_REG(RK3229_WIN0_CTRL0, 0x1, 0),
+	.format = VOP_REG(RK3229_WIN0_CTRL0, 0x7, 1),
+	.rb_swap = VOP_REG(RK3229_WIN0_CTRL0, 0x1, 12),
+	.act_info = VOP_REG(RK3229_WIN0_ACT_INFO, 0x1fff1fff, 0),
+	.dsp_info = VOP_REG(RK3229_WIN0_DSP_INFO, 0x0fff0fff, 0),
+	.dsp_st = VOP_REG(RK3229_WIN0_DSP_ST, 0x1fff1fff, 0),
+	.yrgb_mst = VOP_REG(RK3229_WIN0_YRGB_MST, 0xffffffff, 0),
+	.uv_mst = VOP_REG(RK3229_WIN0_CBR_MST, 0xffffffff, 0),
+	.yrgb_vir = VOP_REG(RK3229_WIN0_VIR, 0x3fff, 0),
+	.uv_vir = VOP_REG(RK3229_WIN0_VIR, 0x3fff, 16),
+	.src_alpha_ctl = VOP_REG(RK3229_WIN0_SRC_ALPHA_CTRL, 0xff, 0),
+	.dst_alpha_ctl = VOP_REG(RK3229_WIN0_DST_ALPHA_CTRL, 0xff, 0),
+};
+
+static const struct vop_win_data rk3229_vop_win_data[] = {
+	{ .base = 0x00, .phy = &rk3229_win01_data,
+	  .type = DRM_PLANE_TYPE_PRIMARY },
+	{ .base = 0x40, .phy = &rk3229_win01_data,
+	  .type = DRM_PLANE_TYPE_CURSOR },
+};
+
+static const struct vop_ctrl rk3229_ctrl_data = {
+	.cfg_done = VOP_REG(RK3229_REG_CFG_DONE, 0x1, 0),
+	.standby = VOP_REG(RK3229_SYS_CTRL, 0x1, 22),
+	.gate_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 23),
+	.mmu_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 20),
+	.rgb_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 12),
+	.hdmi_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 13),
+	.edp_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 14),
+	.mipi_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 15),
+	.data_blank = VOP_REG(RK3229_DSP_CTRL0, 0x1, 19),
+	.out_mode = VOP_REG(RK3229_DSP_CTRL0, 0xf, 0),
+	.pin_pol = VOP_REG(RK3229_DSP_CTRL1, 0xf, 20),
+	.dither_up = VOP_REG(RK3229_DSP_CTRL1, 0x1, 6),
+	.htotal_pw = VOP_REG(RK3229_DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
+	.hact_st_end = VOP_REG(RK3229_DSP_HACT_ST_END, 0x1fff1fff, 0),
+	.vtotal_pw = VOP_REG(RK3229_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
+	.vact_st_end = VOP_REG(RK3229_DSP_VACT_ST_END, 0x1fff1fff, 0),
+	.hpost_st_end = VOP_REG(RK3229_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
+	.vpost_st_end = VOP_REG(RK3229_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
+};
+
+static const int rk3229_vop_intrs[] = {
+	FS_INTR,
+	0, 0,
+	LINE_FLAG_INTR,
+	0, 0, 0, 0, 0,
+	0, 0, 0, 0,
+	DSP_HOLD_VALID_INTR,
+};
+
+static const struct vop_intr rk3229_intr = {
+	.write_mask = true,
+	.intrs = rk3229_vop_intrs,
+	.nintrs = ARRAY_SIZE(rk3229_vop_intrs),
+	.status = VOP_REG(RK3229_INTR_STATUS0, 0xffffffff, 0),
+	.clear = VOP_REG(RK3229_INTR_CLEAR0, 0xffffffff, 0),
+	.enable = VOP_REG(RK3229_INTR_EN0, 0xffffffff, 0),
+};
+
+static const struct vop_reg_data rk3229_vop_init_reg_table[] = {
+};
+
+static const struct vop_data rk3229_vop = {
+	.init_table = rk3229_vop_init_reg_table,
+	.table_size = ARRAY_SIZE(rk3229_vop_init_reg_table),
+	.intr = &rk3229_intr,
+	.ctrl = &rk3229_ctrl_data,
+	.win = rk3229_vop_win_data,
+	.win_size = ARRAY_SIZE(rk3229_vop_win_data),
+};
+
 static const struct vop_scl_regs rk3066_win_scl = {
 	.scale_yrgb_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
 	.scale_yrgb_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
@@ -276,6 +384,8 @@ static const struct vop_data rk3036_vop = {
 static const struct of_device_id vop_driver_dt_match[] = {
 	{ .compatible = "rockchip,rk3288-vop",
 	  .data = &rk3288_vop },
+	{ .compatible = "rockchip,rk3229-vop",
+	  .data = &rk3229_vop },
 	{ .compatible = "rockchip,rk3036-vop",
 	  .data = &rk3036_vop },
 	{},
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
index d4b46cb..0280388 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
@@ -15,6 +15,96 @@
 #ifndef _ROCKCHIP_VOP_REG_H
 #define _ROCKCHIP_VOP_REG_H
 
+/* rk3229 register definition */
+#define RK3229_REG_CFG_DONE			0x0000
+#define RK3229_VERSION_INFO			0x0004
+#define RK3229_SYS_CTRL				0x0008
+#define RK3229_SYS_CTRL1			0x000c
+#define RK3229_DSP_CTRL0			0x0010
+#define RK3229_DSP_CTRL1			0x0014
+#define RK3229_DSP_BG				0x0018
+#define RK3229_WIN0_CTRL0			0x0030
+#define RK3229_WIN0_CTRL1			0x0034
+#define RK3229_WIN0_COLOR_KEY			0x0038
+#define RK3229_WIN0_VIR				0x003c
+#define RK3229_WIN0_YRGB_MST			0x0040
+#define RK3229_WIN0_CBR_MST			0x0044
+#define RK3229_WIN0_ACT_INFO			0x0048
+#define RK3229_WIN0_DSP_INFO			0x004c
+#define RK3229_WIN0_DSP_ST			0x0050
+#define RK3229_WIN0_SCL_FACTOR_YRGB		0x0054
+#define RK3229_WIN0_SCL_FACTOR_CBR		0x0058
+#define RK3229_WIN0_SCL_OFFSET			0x005c
+#define RK3229_WIN0_SRC_ALPHA_CTRL		0x0060
+#define RK3229_WIN0_DST_ALPHA_CTRL		0x0064
+#define RK3229_WIN0_FADING_CTRL			0x0068
+/* win1 register */
+#define RK3229_WIN1_CTRL0			0x0070
+#define RK3229_WIN1_CTRL1			0x0074
+#define RK3229_WIN1_COLOR_KEY			0x0078
+#define RK3229_WIN1_VIR				0x007c
+#define RK3229_WIN1_YRGB_MST			0x0080
+#define RK3229_WIN1_CBR_MST			0x0084
+#define RK3229_WIN1_ACT_INFO			0x0088
+#define RK3229_WIN1_DSP_INFO			0x008c
+#define RK3229_WIN1_DSP_ST			0x0090
+#define RK3229_WIN1_SCL_FACTOR_YRGB		0x0094
+#define RK3229_WIN1_SCL_FACTOR_CBR		0x0098
+#define RK3229_WIN1_SCL_OFFSET			0x009c
+#define RK3229_WIN1_SRC_ALPHA_CTRL		0x00a0
+#define RK3229_WIN1_DST_ALPHA_CTRL		0x00a4
+#define RK3229_WIN1_FADING_CTRL			0x00a8
+#define RK3229_WIN1_CTRL2			0x00ac
+/* hwc register */
+#define RK3229_HWC_CTRL0			0x0150
+#define RK3229_HWC_CTRL1			0x0154
+#define RK3229_HWC_MST				0x0158
+#define RK3229_HWC_DSP_ST			0x015c
+#define RK3229_HWC_SRC_ALPHA_CTRL		0x0160
+#define RK3229_HWC_DST_ALPHA_CTRL		0x0164
+#define RK3229_HWC_FADING_CTRL			0x0168
+#define RK3229_HWC_RESERVED			0x016c
+/* post process register */
+#define RK3229_POST_DSP_HACT_INFO		0x0170
+#define RK3229_POST_DSP_VACT_INFO		0x0174
+#define RK3229_POST_SCL_FACTOR_YRGB		0x0178
+#define RK3229_POST_RESERVED			0x017c
+#define RK3229_POST_SCL_CTRL			0x0180
+#define RK3229_POST_DSP_VACT_INFO_F1		0x0184
+#define RK3229_DSP_HTOTAL_HS_END		0x0188
+#define RK3229_DSP_HACT_ST_END			0x018c
+#define RK3229_DSP_VTOTAL_VS_END		0x0190
+#define RK3229_DSP_VACT_ST_END			0x0194
+#define RK3229_DSP_VS_ST_END_F1			0x0198
+#define RK3229_DSP_VACT_ST_END_F1		0x019c
+/* Brightness contrast */
+#define RK3229_BCSH_COLOR_BAR			0x01b0
+#define RK3229_BCSH_BCS				0x01b4
+#define RK3229_BCSH_H				0x01b8
+#define RK3229_BCSH_CTRL			0x01bc
+/* FRC registers */
+#define RK3229_FRC_LOWER01_0			0x01e8
+#define RK3229_FRC_LOWER01_1			0x01ec
+#define RK3229_FRC_LOWER10_0			0x01f0
+#define RK3229_FRC_LOWER10_1			0x01f4
+#define RK3229_FRC_LOWER11_0			0x01f8
+#define RK3229_FRC_LOWER11_1			0x01fc
+/* Interrupt registers */
+#define RK3229_INTR_EN0				0x0280
+#define RK3229_INTR_CLEAR0			0x0284
+#define RK3229_INTR_STATUS0			0x0288
+#define RK3229_INTR_RAW_STATUS0			0x028c
+#define RK3229_INTR_EN1				0x0290
+#define RK3229_INTR_CLEAR1			0x0294
+#define RK3229_INTR_STATUS1			0x0298
+#define RK3229_INTR_RAW_STATUS1			0x029c
+#define RK3229_LINE_FLAG			0x02a0
+#define RK3229_VOP_STATUS			0x02a4
+#define RK3229_BLANKING_VALUE			0x02a8
+#define RK3229_WIN0_DSP_BG			0x02b0
+#define RK3229_WIN1_DSP_BG			0x02b4
+/* register definition end */
+
 /* rk3288 register definition */
 #define RK3288_REG_CFG_DONE			0x0000
 #define RK3288_VERSION_INFO			0x0004
-- 
1.9.1




From ykk at rock-chips.com  Mon Jan  4 20:00:36 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Tue,  5 Jan 2016 12:00:36 +0800
Subject: [PATCH v2 3/3] dt-bindings: add document for rk3229-vop
In-Reply-To: <1451965688-12286-1-git-send-email-ykk@rock-chips.com>
References: <1451965688-12286-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1451966436-12733-1-git-send-email-ykk@rock-chips.com>

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Acked-by: Rob Herring <robh at kernel.org>
---
Changes in v2:
- acked by Rob

 Documentation/devicetree/bindings/display/rockchip/rockchip-vop.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip-vop.txt b/Documentation/devicetree/bindings/display/rockchip/rockchip-vop.txt
index 5489b59..75ebc20 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip-vop.txt
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip-vop.txt
@@ -7,6 +7,7 @@ buffer to an external LCD interface.
 Required properties:
 - compatible: value should be one of the following
 		"rockchip,rk3288-vop";
+		"rockchip,rk3229-vop";
 		"rockchip,rk3036-vop";
 
 - interrupts: should contain a list of all VOP IP block interrupts in the
-- 
1.9.1




From heiko at sntech.de  Mon Jan  4 23:02:41 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Tue, 05 Jan 2016 08:02:41 +0100
Subject: [PATCH] i2c: rk3x: init module as subsys call
In-Reply-To: <1451962938-17398-1-git-send-email-jay.xu@rock-chips.com>
References: <1451962938-17398-1-git-send-email-jay.xu@rock-chips.com>
Message-ID: <2513923.8oltbpbAin@phil>

Hi Jianqun,

Am Dienstag, 5. Januar 2016, 11:02:18 schrieb jianqun.xu:
> From: Xu Jianqun <jay.xu at rock-chips.com>
> 
> There is a requirement from pmic device, which is on the i2c bus,
> that the pmic needs to be called earlier then devices powered by
> the outputs of the pmic, if not, the devices maybe fail to probe.
> 
> For example, a pmic on i2c0, and touchscreen device on i2c2,
> i2c0: - pmic(rk818)
> i2c2: - ts(gt911), powered by rk818 on i2c0
> 
> The problem will happen if the i2c2 node in dts file is ordered
> before i2c0 node, then ts(gt911) will be probed before pmic(rk818),
> since the power from the pmic(rk818) for ts(gt911) hasn't enabled,
> so ts(gt911) will fail to probe due to the failure of i2c test.
> 
> But if we set the i2c0 node before i2c2, there is no this issue.
> 
> The stable way to make sure that pmic can be intalized before other
> peripher devices is to make the pmic module be subsys call, the i2c
> module need to be subsys call firstly.

I do believe that came up in the past already and the direction from then 
was (and most likely still is) that drivers should make use of the probe-
deferral mechanism instead of wiggling with the initcall ordering.

Your touchscreen will have a "xyz-supply" property and I think the 
regulator-framework should already emit a -EPROBE_DEFER at regulator_get, 
when the regulator is specified but not available yet.


Heiko

> 
> Signed-off-by: Xu Jianqun <jay.xu at rock-chips.com>
> ---
>  drivers/i2c/busses/i2c-rk3x.c | 12 +++++++++++-
>  1 file changed, 11 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c
> index c1935eb..00e5959 100644
> --- a/drivers/i2c/busses/i2c-rk3x.c
> +++ b/drivers/i2c/busses/i2c-rk3x.c
> @@ -1037,7 +1037,17 @@ static struct platform_driver rk3x_i2c_driver = {
>  	},
>  };
> 
> -module_platform_driver(rk3x_i2c_driver);
> +static int __init rk3x_i2c_init(void)
> +{
> +	return platform_driver_register(&rk3x_i2c_driver);
> +}
> +subsys_initcall(rk3x_i2c_init);
> +
> +static void __exit rk3x_i2c_exit(void)
> +{
> +	platform_driver_unregister(&rk3x_i2c_driver);
> +}
> +module_exit(rk3x_i2c_exit);
> 
>  MODULE_DESCRIPTION("Rockchip RK3xxx I2C Bus driver");
>  MODULE_AUTHOR("Max Schwarz <max.schwarz at online.de>");



From huangtao at rock-chips.com  Mon Jan  4 23:42:32 2016
From: huangtao at rock-chips.com (Huang, Tao)
Date: Tue, 5 Jan 2016 15:42:32 +0800
Subject: [PATCH] i2c: rk3x: init module as subsys call
In-Reply-To: <2513923.8oltbpbAin@phil>
References: <1451962938-17398-1-git-send-email-jay.xu@rock-chips.com>
 <2513923.8oltbpbAin@phil>
Message-ID: <568B73E8.3010901@rock-chips.com>

Hi, Heiko:

On 2016?01?05? 15:02, Heiko Stuebner wrote:
> Hi Jianqun,
> 
> Am Dienstag, 5. Januar 2016, 11:02:18 schrieb jianqun.xu:
>> From: Xu Jianqun <jay.xu at rock-chips.com>
>>
>> There is a requirement from pmic device, which is on the i2c bus,
>> that the pmic needs to be called earlier then devices powered by
>> the outputs of the pmic, if not, the devices maybe fail to probe.
>>
>> For example, a pmic on i2c0, and touchscreen device on i2c2,
>> i2c0: - pmic(rk818)
>> i2c2: - ts(gt911), powered by rk818 on i2c0
>>
>> The problem will happen if the i2c2 node in dts file is ordered
>> before i2c0 node, then ts(gt911) will be probed before pmic(rk818),
>> since the power from the pmic(rk818) for ts(gt911) hasn't enabled,
>> so ts(gt911) will fail to probe due to the failure of i2c test.
>>
>> But if we set the i2c0 node before i2c2, there is no this issue.
>>
>> The stable way to make sure that pmic can be intalized before other
>> peripher devices is to make the pmic module be subsys call, the i2c
>> module need to be subsys call firstly.
> 
> I do believe that came up in the past already and the direction from then 
> was (and most likely still is) that drivers should make use of the probe-
> deferral mechanism instead of wiggling with the initcall ordering.


I don't think this is a good idea. This will trigger a lots of init call
failed. Before pmic init, all i2c device driver transmit will failed,
and because i2c is slow bus, and i2c transmit may failed by other
reasons, so the i2c driver and i2c device driver will try many times to
make sure the transmit completion. These unnecessary transmission will
make Linux boot very slow.

I2C bus should be subsys, and we can easy resolve this problem, why we
depends on a complicated and slow implementation?

> 
> Your touchscreen will have a "xyz-supply" property and I think the 
> regulator-framework should already emit a -EPROBE_DEFER at regulator_get, 
> when the regulator is specified but not available yet.

Unfortunately, mostly driver do not support regulator api. They are
suppose power is on.

Thanks.
Huang, Tao

> 
> 
> Heiko
> 
>>
>> Signed-off-by: Xu Jianqun <jay.xu at rock-chips.com>
>> ---
>>  drivers/i2c/busses/i2c-rk3x.c | 12 +++++++++++-
>>  1 file changed, 11 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c
>> index c1935eb..00e5959 100644
>> --- a/drivers/i2c/busses/i2c-rk3x.c
>> +++ b/drivers/i2c/busses/i2c-rk3x.c
>> @@ -1037,7 +1037,17 @@ static struct platform_driver rk3x_i2c_driver = {
>>  	},
>>  };
>>
>> -module_platform_driver(rk3x_i2c_driver);
>> +static int __init rk3x_i2c_init(void)
>> +{
>> +	return platform_driver_register(&rk3x_i2c_driver);
>> +}
>> +subsys_initcall(rk3x_i2c_init);
>> +
>> +static void __exit rk3x_i2c_exit(void)
>> +{
>> +	platform_driver_unregister(&rk3x_i2c_driver);
>> +}
>> +module_exit(rk3x_i2c_exit);
>>
>>  MODULE_DESCRIPTION("Rockchip RK3xxx I2C Bus driver");
>>  MODULE_AUTHOR("Max Schwarz <max.schwarz at online.de>");
> 
> 
> 
> 




From jay.xu at rock-chips.com  Mon Jan  4 23:42:55 2016
From: jay.xu at rock-chips.com (Jianqun Xu)
Date: Tue, 5 Jan 2016 15:42:55 +0800
Subject: [PATCH] i2c: rk3x: init module as subsys call
In-Reply-To: <2513923.8oltbpbAin@phil>
References: <1451962938-17398-1-git-send-email-jay.xu@rock-chips.com>
 <2513923.8oltbpbAin@phil>
Message-ID: <568B73FF.9090508@rock-chips.com>

Hi Heiko:

? 05/01/2016 15:02, Heiko Stuebner ??:
> Hi Jianqun,
>
> Am Dienstag, 5. Januar 2016, 11:02:18 schrieb jianqun.xu:
>> From: Xu Jianqun <jay.xu at rock-chips.com>
>>
>> There is a requirement from pmic device, which is on the i2c bus,
>> that the pmic needs to be called earlier then devices powered by
>> the outputs of the pmic, if not, the devices maybe fail to probe.
>>
>> For example, a pmic on i2c0, and touchscreen device on i2c2,
>> i2c0: - pmic(rk818)
>> i2c2: - ts(gt911), powered by rk818 on i2c0
>>
>> The problem will happen if the i2c2 node in dts file is ordered
>> before i2c0 node, then ts(gt911) will be probed before pmic(rk818),
>> since the power from the pmic(rk818) for ts(gt911) hasn't enabled,
>> so ts(gt911) will fail to probe due to the failure of i2c test.
>>
>> But if we set the i2c0 node before i2c2, there is no this issue.
>>
>> The stable way to make sure that pmic can be intalized before other
>> peripher devices is to make the pmic module be subsys call, the i2c
>> module need to be subsys call firstly.
>
> I do believe that came up in the past already and the direction from then
> was (and most likely still is) that drivers should make use of the probe-
> deferral mechanism instead of wiggling with the initcall ordering.
>
> Your touchscreen will have a "xyz-supply" property and I think the
> regulator-framework should already emit a -EPROBE_DEFER at regulator_get,
> when the regulator is specified but not available yet.
>
It seems more reasonable, thanks for advice, I will have a try.
>
> Heiko
>
>>
>> Signed-off-by: Xu Jianqun <jay.xu at rock-chips.com>
>> ---
>>   drivers/i2c/busses/i2c-rk3x.c | 12 +++++++++++-
>>   1 file changed, 11 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c
>> index c1935eb..00e5959 100644
>> --- a/drivers/i2c/busses/i2c-rk3x.c
>> +++ b/drivers/i2c/busses/i2c-rk3x.c
>> @@ -1037,7 +1037,17 @@ static struct platform_driver rk3x_i2c_driver = {
>>   	},
>>   };
>>
>> -module_platform_driver(rk3x_i2c_driver);
>> +static int __init rk3x_i2c_init(void)
>> +{
>> +	return platform_driver_register(&rk3x_i2c_driver);
>> +}
>> +subsys_initcall(rk3x_i2c_init);
>> +
>> +static void __exit rk3x_i2c_exit(void)
>> +{
>> +	platform_driver_unregister(&rk3x_i2c_driver);
>> +}
>> +module_exit(rk3x_i2c_exit);
>>
>>   MODULE_DESCRIPTION("Rockchip RK3xxx I2C Bus driver");
>>   MODULE_AUTHOR("Max Schwarz <max.schwarz at online.de>");
>
>
>
>



From heiko at sntech.de  Tue Jan  5 00:00:20 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Tue, 05 Jan 2016 09:00:20 +0100
Subject: [PATCH] i2c: rk3x: init module as subsys call
In-Reply-To: <568B73E8.3010901@rock-chips.com>
References: <1451962938-17398-1-git-send-email-jay.xu@rock-chips.com>
 <2513923.8oltbpbAin@phil> <568B73E8.3010901@rock-chips.com>
Message-ID: <7893052.zgDX0r4i77@phil>

Hi Tao,

Am Dienstag, 5. Januar 2016, 15:42:32 schrieb Huang, Tao:
> On 2016?01?05? 15:02, Heiko Stuebner wrote:
> > Hi Jianqun,
> > 
> > Am Dienstag, 5. Januar 2016, 11:02:18 schrieb jianqun.xu:
> >> From: Xu Jianqun <jay.xu at rock-chips.com>
> >> 
> >> There is a requirement from pmic device, which is on the i2c bus,
> >> that the pmic needs to be called earlier then devices powered by
> >> the outputs of the pmic, if not, the devices maybe fail to probe.
> >> 
> >> For example, a pmic on i2c0, and touchscreen device on i2c2,
> >> i2c0: - pmic(rk818)
> >> i2c2: - ts(gt911), powered by rk818 on i2c0
> >> 
> >> The problem will happen if the i2c2 node in dts file is ordered
> >> before i2c0 node, then ts(gt911) will be probed before pmic(rk818),
> >> since the power from the pmic(rk818) for ts(gt911) hasn't enabled,
> >> so ts(gt911) will fail to probe due to the failure of i2c test.
> >> 
> >> But if we set the i2c0 node before i2c2, there is no this issue.
> >> 
> >> The stable way to make sure that pmic can be intalized before other
> >> peripher devices is to make the pmic module be subsys call, the i2c
> >> module need to be subsys call firstly.
> > 
> > I do believe that came up in the past already and the direction from
> > then
> > was (and most likely still is) that drivers should make use of the
> > probe-
> > deferral mechanism instead of wiggling with the initcall ordering.
> 
> I don't think this is a good idea. This will trigger a lots of init call
> failed. Before pmic init, all i2c device driver transmit will failed,
> and because i2c is slow bus, and i2c transmit may failed by other
> reasons, so the i2c driver and i2c device driver will try many times to
> make sure the transmit completion. These unnecessary transmission will
> make Linux boot very slow.

In general, the slowdown won't be _this_ much if touchscreen drivers need 
one deferral-round before i2c is available. I'm also only pointing out 
things I remember from the last time this came up. 

rk3x-i2c even was here already:
http://www.spinics.net/lists/linux-i2c/msg16680.html


> I2C bus should be subsys, and we can easy resolve this problem, why we
> depends on a complicated and slow implementation?

because it's the only safe way to do that. Because now you need i2c-init at 
subsys-init time, some months later some other soc may need some other 
ordering, especially needing i2c-init later/earlier.

Going through the deferral mechanism is the only way currently available to 
actually make this work on all socs.

Tomeu from Collabora is working on some better scheme to optimize device 
probing order but it looks like this may be a bit off still.


> > Your touchscreen will have a "xyz-supply" property and I think the
> > regulator-framework should already emit a -EPROBE_DEFER at
> > regulator_get,
> > when the regulator is specified but not available yet.
> 
> Unfortunately, mostly driver do not support regulator api. They are
> suppose power is on.

Having touchscreen drivers support its proper supply-regulators is not 
rocket science ;-) [0] , so I would consider this a bug in the touchscreen 
driver itself.

Just look into the datasheet and add the appropriate supplies to the drivers 
in question.


Heiko

[0] citiing my own work: 
https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/drivers/input/touchscreen/zforce_ts.c#n806

> 
> Thanks.
> Huang, Tao
> 
> > Heiko
> > 
> >> Signed-off-by: Xu Jianqun <jay.xu at rock-chips.com>
> >> ---
> >> 
> >>  drivers/i2c/busses/i2c-rk3x.c | 12 +++++++++++-
> >>  1 file changed, 11 insertions(+), 1 deletion(-)
> >> 
> >> diff --git a/drivers/i2c/busses/i2c-rk3x.c
> >> b/drivers/i2c/busses/i2c-rk3x.c index c1935eb..00e5959 100644
> >> --- a/drivers/i2c/busses/i2c-rk3x.c
> >> +++ b/drivers/i2c/busses/i2c-rk3x.c
> >> @@ -1037,7 +1037,17 @@ static struct platform_driver rk3x_i2c_driver =
> >> {
> >> 
> >>  	},
> >>  
> >>  };
> >> 
> >> -module_platform_driver(rk3x_i2c_driver);
> >> +static int __init rk3x_i2c_init(void)
> >> +{
> >> +	return platform_driver_register(&rk3x_i2c_driver);
> >> +}
> >> +subsys_initcall(rk3x_i2c_init);
> >> +
> >> +static void __exit rk3x_i2c_exit(void)
> >> +{
> >> +	platform_driver_unregister(&rk3x_i2c_driver);
> >> +}
> >> +module_exit(rk3x_i2c_exit);
> >> 
> >>  MODULE_DESCRIPTION("Rockchip RK3xxx I2C Bus driver");
> >>  MODULE_AUTHOR("Max Schwarz <max.schwarz at online.de>");



From huangtao at rock-chips.com  Tue Jan  5 00:31:38 2016
From: huangtao at rock-chips.com (Huang, Tao)
Date: Tue, 5 Jan 2016 16:31:38 +0800
Subject: [RESEND PATCH] rtc: rk808: rename rtc-rk808.c to rtc-rk8xx.c
In-Reply-To: <20160104145938.6c053b5a@linux.lan.towertech.it>
References: <1451571295-4193-1-git-send-email-zhangqing@rock-chips.com>
 <20160104094546.GB32724@piout.net>
 <20160104145938.6c053b5a@linux.lan.towertech.it>
Message-ID: <568B7F6A.3000607@rock-chips.com>

Hi, Alessandro:

On 2016?01?04? 21:59, Alessandro Zummo wrote:
> On Mon, 4 Jan 2016 10:45:46 +0100
> Alexandre Belloni <alexandre.belloni at free-electrons.com> wrote:
> 
>> I'm not sure it is useful to do that renaming. It is usual to have one
>> driver that supports multiple chips named with the forst chip it
>> supported.
>>
>> Also, what would happen if for example rk855 is not compatible at all
>> with the previous implementations?
> 
>  Alexandre is absolutely right. There's no need to rename a driver,
>  it would just piss off people who are used to that name and
>  have it in their scripts. Like when your eth0 gets renamed
>  to some obscure enXXX <g>.
> 

You and Alexandre are right. The rename is just make the driver more
readable, i.e. let people know this driver suit for more PMIC no just
rk808. In fact, I don't care the name is rk808 or rk8xx.

The key change of this patch is try to dis-coupling rk808 driver and RTC
driver. Because of register offset and function is vary between
different PMIC, we believe it is hard to write one PMIC driver to suit
all PMIC. So we hope RTC driver can share between all PMIC from rockchip.

Please review this code:

-static int rk808_rtc_probe(struct platform_device *pdev)
+static int rk8xx_rtc_probe(struct platform_device *pdev)
 {
-	struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent);
	...
+	struct i2c_client *client = to_i2c_client(pdev->dev.parent);

	...

-	rk808_rtc->rk808 = rk808;
+	rk8xx_rtc->regmap = devm_regmap_init_i2c(client,
+					     &rk8xx_rtc_regmap_config);
	...
+	rk8xx_rtc->i2c = client;

Old driver have struct rk808 pointer, which defined on
include/linux/mfd/rk808.h
If we write new PMIC driver, for example rk818, define a new struct
rk818. How can we get this pointer from RTC driver?

So another way to solve this problem is introduce common struct share
between all PMIC driver. For example rk8xx.

We solve this problem by create new regmap to access PMIC. As I say
before, it make RTC driver independent of PMIC driver. Do you agree this
change?

Thanks!
Huang, Tao



From huangtao at rock-chips.com  Tue Jan  5 00:48:59 2016
From: huangtao at rock-chips.com (Huang, Tao)
Date: Tue, 5 Jan 2016 16:48:59 +0800
Subject: [PATCH] i2c: rk3x: init module as subsys call
In-Reply-To: <7893052.zgDX0r4i77@phil>
References: <1451962938-17398-1-git-send-email-jay.xu@rock-chips.com>
 <2513923.8oltbpbAin@phil> <568B73E8.3010901@rock-chips.com>
 <7893052.zgDX0r4i77@phil>
Message-ID: <568B837B.50607@rock-chips.com>

Hi, Heiko:

On 2016?01?05? 16:00, Heiko Stuebner wrote:
> Hi Tao,
> 
> Am Dienstag, 5. Januar 2016, 15:42:32 schrieb Huang, Tao:

>> I don't think this is a good idea. This will trigger a lots of init call
>> failed. Before pmic init, all i2c device driver transmit will failed,
>> and because i2c is slow bus, and i2c transmit may failed by other
>> reasons, so the i2c driver and i2c device driver will try many times to
>> make sure the transmit completion. These unnecessary transmission will
>> make Linux boot very slow.
> 
> In general, the slowdown won't be _this_ much if touchscreen drivers need 
> one deferral-round before i2c is available. I'm also only pointing out 
> things I remember from the last time this came up. 
> 
> rk3x-i2c even was here already:
> http://www.spinics.net/lists/linux-i2c/msg16680.html

OK. I don't agree with the rule, but we will follow it.

> 
> 
>> I2C bus should be subsys, and we can easy resolve this problem, why we
>> depends on a complicated and slow implementation?
> 
> because it's the only safe way to do that. Because now you need i2c-init at 
> subsys-init time, some months later some other soc may need some other 
> ordering, especially needing i2c-init later/earlier.
> 
> Going through the deferral mechanism is the only way currently available to 
> actually make this work on all socs.
> 
> Tomeu from Collabora is working on some better scheme to optimize device 
> probing order but it looks like this may be a bit off still.
> 
> 
>>> Your touchscreen will have a "xyz-supply" property and I think the
>>> regulator-framework should already emit a -EPROBE_DEFER at
>>> regulator_get,
>>> when the regulator is specified but not available yet.
>>
>> Unfortunately, mostly driver do not support regulator api. They are
>> suppose power is on.
> 
> Having touchscreen drivers support its proper supply-regulators is not 
> rocket science ;-) [0] , so I would consider this a bug in the touchscreen 
> driver itself.

I don't just talk about touch screen driver, most i2c device driver such
as input sensor/camera/rtc/battery will suffer. So people will see their
drivers do not work or slow down on rk3368 platform :(

Thanks!
Huang, Tao



From alexandre.belloni at free-electrons.com  Tue Jan  5 00:54:53 2016
From: alexandre.belloni at free-electrons.com (Alexandre Belloni)
Date: Tue, 5 Jan 2016 09:54:53 +0100
Subject: [RESEND PATCH] rtc: rk808: rename rtc-rk808.c to rtc-rk8xx.c
In-Reply-To: <568B7F6A.3000607@rock-chips.com>
References: <1451571295-4193-1-git-send-email-zhangqing@rock-chips.com>
 <20160104094546.GB32724@piout.net>
 <20160104145938.6c053b5a@linux.lan.towertech.it>
 <568B7F6A.3000607@rock-chips.com>
Message-ID: <20160105085135.GG32724@piout.net>

Hi,

On 05/01/2016 at 16:31:38 +0800, Huang, Tao wrote :
> You and Alexandre are right. The rename is just make the driver more
> readable, i.e. let people know this driver suit for more PMIC no just
> rk808. In fact, I don't care the name is rk808 or rk8xx.
> 

For this purpose, you can add the name of the supported PMICs in the
Kconfig.

> The key change of this patch is try to dis-coupling rk808 driver and RTC
> driver. Because of register offset and function is vary between
> different PMIC, we believe it is hard to write one PMIC driver to suit
> all PMIC. So we hope RTC driver can share between all PMIC from rockchip.
> 
> Please review this code:
> 
> -static int rk808_rtc_probe(struct platform_device *pdev)
> +static int rk8xx_rtc_probe(struct platform_device *pdev)
>  {
> -	struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent);
> 	...
> +	struct i2c_client *client = to_i2c_client(pdev->dev.parent);
> 
> 	...
> 
> -	rk808_rtc->rk808 = rk808;
> +	rk8xx_rtc->regmap = devm_regmap_init_i2c(client,
> +					     &rk8xx_rtc_regmap_config);
> 	...
> +	rk8xx_rtc->i2c = client;
> 
> Old driver have struct rk808 pointer, which defined on
> include/linux/mfd/rk808.h
> If we write new PMIC driver, for example rk818, define a new struct
> rk818. How can we get this pointer from RTC driver?
> 
> So another way to solve this problem is introduce common struct share
> between all PMIC driver. For example rk8xx.
> 

That is probably the best solution. If you want to reuse drivers between
pmic, then have a common structure that you can pass to those drivers.

I believe the regmap_init_i2c should stay in the MFD driver.

-- 
Alexandre Belloni, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com


From sy.w at outlook.com  Tue Jan  5 01:16:54 2016
From: sy.w at outlook.com (Shuyu Wei)
Date: Tue, 5 Jan 2016 17:16:54 +0800
Subject: [PATCH] ethernet:arc: Move arc_emac_tx_clean() into arc_emac_tx()
 and disable tx interrupt
Message-ID: <BLU436-SMTP1175E4C81F4C2DFF5BF8FEA8FF30@phx.gbl>

Doing tx_clean() inside poll() may scramble the tx ring buffer if 
tx() is running. This will cause tx to stop working, which can be
reproduced by simultaneously downloading two large files at high speed.

Moving tx_clean() into tx() will prevent this. And tx interrupt is no
longer needed now.

Signed-off-by: Shuyu Wei <shuyu.w at outlook.com>
---

diff --git a/drivers/net/ethernet/arc/emac_main.c b/drivers/net/ethernet/arc/emac_main.c
index abe1eab..34531b3 100644
--- a/drivers/net/ethernet/arc/emac_main.c
+++ b/drivers/net/ethernet/arc/emac_main.c
@@ -311,12 +311,10 @@ static int arc_emac_poll(struct napi_struct *napi, int budget)
 	struct arc_emac_priv *priv = netdev_priv(ndev);
 	unsigned int work_done;
 
-	arc_emac_tx_clean(ndev);
-
 	work_done = arc_emac_rx(ndev, budget);
 	if (work_done < budget) {
 		napi_complete(napi);
-		arc_reg_or(priv, R_ENABLE, RXINT_MASK | TXINT_MASK);
+		arc_reg_or(priv, R_ENABLE, RXINT_MASK);
 	}
 
 	return work_done;
@@ -345,9 +343,9 @@ static irqreturn_t arc_emac_intr(int irq, void *dev_instance)
 	/* Reset all flags except "MDIO complete" */
 	arc_reg_set(priv, R_STATUS, status);
 
-	if (status & (RXINT_MASK | TXINT_MASK)) {
+	if (status & RXINT_MASK) {
 		if (likely(napi_schedule_prep(&priv->napi))) {
-			arc_reg_clr(priv, R_ENABLE, RXINT_MASK | TXINT_MASK);
+			arc_reg_clr(priv, R_ENABLE, RXINT_MASK);
 			__napi_schedule(&priv->napi);
 		}
 	}
@@ -458,7 +456,7 @@ static int arc_emac_open(struct net_device *ndev)
 	arc_reg_set(priv, R_TX_RING, (unsigned int)priv->txbd_dma);
 
 	/* Enable interrupts */
-	arc_reg_set(priv, R_ENABLE, RXINT_MASK | TXINT_MASK | ERR_MASK);
+	arc_reg_set(priv, R_ENABLE, RXINT_MASK | ERR_MASK);
 
 	/* Set CONTROL */
 	arc_reg_set(priv, R_CTRL,
@@ -529,7 +527,7 @@ static int arc_emac_stop(struct net_device *ndev)
 	netif_stop_queue(ndev);
 
 	/* Disable interrupts */
-	arc_reg_clr(priv, R_ENABLE, RXINT_MASK | TXINT_MASK | ERR_MASK);
+	arc_reg_clr(priv, R_ENABLE, RXINT_MASK | ERR_MASK);
 
 	/* Disable EMAC */
 	arc_reg_clr(priv, R_CTRL, EN_MASK);
@@ -587,6 +585,8 @@ static int arc_emac_tx(struct sk_buff *skb, struct net_device *ndev)
 	__le32 *info = &priv->txbd[*txbd_curr].info;
 	dma_addr_t addr;
 
+	arc_emac_tx_clean(ndev);
+
 	if (skb_padto(skb, ETH_ZLEN))
 		return NETDEV_TX_OK;



From wsa at the-dreams.de  Tue Jan  5 02:01:10 2016
From: wsa at the-dreams.de (Wolfram Sang)
Date: Tue, 5 Jan 2016 11:01:10 +0100
Subject: [PATCH] i2c: rk3x: init module as subsys call
In-Reply-To: <568B837B.50607@rock-chips.com>
References: <1451962938-17398-1-git-send-email-jay.xu@rock-chips.com>
 <2513923.8oltbpbAin@phil> <568B73E8.3010901@rock-chips.com>
 <7893052.zgDX0r4i77@phil> <568B837B.50607@rock-chips.com>
Message-ID: <20160105100110.GB1525@katana>


> > Tomeu from Collabora is working on some better scheme to optimize device 
> > probing order but it looks like this may be a bit off still.

...

> I don't just talk about touch screen driver, most i2c device driver such
> as input sensor/camera/rtc/battery will suffer. So people will see their
> drivers do not work or slow down on rk3368 platform :(

I totally agree that the current situation is not ideal. This is why it
has to be *properly fixed* and not workarounded (which caused other side
effects in the past). If you care about it, then please help Tomeu with
his patchset.

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160105/071284c4/attachment.sig>

From wdc at rock-chips.com  Tue Jan  5 04:30:55 2016
From: wdc at rock-chips.com (David Wu)
Date: Tue,  5 Jan 2016 20:30:55 +0800
Subject: [PATCH] pinctrl: rockchip: add support for the rk3399
Message-ID: <1451997055-47655-1-git-send-email-wdc@rock-chips.com>

From: David Wu <david.wu at rock-chips.com>

The pinctrl of rk3399 is much different from other's,
especially the 3bits of drive strength.

Change-Id: I6d0260256f8cf742f940770b317b26571bf42023
Signed-off-by: David Wu <david.wu at rock-chips.com>
---
 .../bindings/pinctrl/rockchip,pinctrl.txt          |   1 +
 drivers/pinctrl/pinctrl-rockchip.c                 | 336 ++++++++++++++++++++-
 2 files changed, 324 insertions(+), 13 deletions(-)

diff --git a/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
index 391ef4b..3bb9456 100644
--- a/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
+++ b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
@@ -22,6 +22,7 @@ Required properties for iomux controller:
   - compatible: one of "rockchip,rk2928-pinctrl", "rockchip,rk3066a-pinctrl"
 		       "rockchip,rk3066b-pinctrl", "rockchip,rk3188-pinctrl"
 		       "rockchip,rk3288-pinctrl", "rockchip,rk3368-pinctrl"
+		       "rockchip,rk3399-pinctrl"
   - rockchip,grf: phandle referencing a syscon providing the
 	 "general register files"
 
diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c
index a065112..eb4da8f 100644
--- a/drivers/pinctrl/pinctrl-rockchip.c
+++ b/drivers/pinctrl/pinctrl-rockchip.c
@@ -64,6 +64,7 @@ enum rockchip_pinctrl_type {
 	RK3188,
 	RK3288,
 	RK3368,
+	RK3399,
 };
 
 /**
@@ -86,6 +87,31 @@ struct rockchip_iomux {
 };
 
 /**
+ * enum type index corresponding to rockchip_perpin_drv_list arrays index.
+ */
+enum rockchip_pin_drv_type {
+	DRV_TYPE_IO_DEFAULT = 0,
+	DRV_TYPE_IO_1V8_OR_3V0,
+	DRV_TYPE_IO_1V8_ONLY,
+	DRV_TYPE_IO_1V8_3V0_AUTO,
+	DRV_TYPE_IO_3V3_ONLY,
+	DRV_TYPE_MAX
+};
+
+/**
+ * @drv_type: drive strength variant using rockchip_perpin_drv_type
+ * @offset: if initialized to -1 it will be autocalculated, by specifying
+ *	    an initial offset value the relevant source offset can be reset
+ *	    to a new value for autocalculating the following drive strength
+ *	    registers. if used chips own cal_drv func instead to calculate
+ *	    registers offset, the variant could be ignored.
+ */
+struct rockchip_drv {
+	enum rockchip_pin_drv_type	drv_type;
+	int				offset;
+};
+
+/**
  * @reg_base: register base of the gpio bank
  * @reg_pull: optional separate register for additional pull settings
  * @clk: clock of the gpio bank
@@ -96,6 +122,7 @@ struct rockchip_iomux {
  * @name: name of the bank
  * @bank_num: number of the bank, to account for holes
  * @iomux: array describing the 4 iomux sources of the bank
+ * @drv: array describing the 4 drive strength sources of the bank
  * @valid: are all necessary informations present
  * @of_node: dt node of this bank
  * @drvdata: common pinctrl basedata
@@ -115,6 +142,7 @@ struct rockchip_pin_bank {
 	char				*name;
 	u8				bank_num;
 	struct rockchip_iomux		iomux[4];
+	struct rockchip_drv		drv[4];
 	bool				valid;
 	struct device_node		*of_node;
 	struct rockchip_pinctrl		*drvdata;
@@ -151,6 +179,47 @@ struct rockchip_pin_bank {
 		},							\
 	}
 
+#define PIN_BANK_DRV_FLAGS(id, pins, label, type0, type1, type2, type3) \
+	{								\
+		.bank_num	= id,					\
+		.nr_pins	= pins,					\
+		.name		= label,				\
+		.iomux		= {					\
+			{ .offset = -1 },				\
+			{ .offset = -1 },				\
+			{ .offset = -1 },				\
+			{ .offset = -1 },				\
+		},							\
+		.drv		= {					\
+			{ .drv_type = type0, .offset = -1 },		\
+			{ .drv_type = type1, .offset = -1 },		\
+			{ .drv_type = type2, .offset = -1 },		\
+			{ .drv_type = type3, .offset = -1 },		\
+		},							\
+	}
+
+#define PIN_BANK_IOMUX_DRV_FLAGS_OFFSET(id, pins, label, iom0, iom1,	\
+					iom2, iom3, drv0, drv1, drv2,	\
+					drv3, offset0, offset1,		\
+					offset2, offset3)		\
+	{								\
+		.bank_num	= id,					\
+		.nr_pins	= pins,					\
+		.name		= label,				\
+		.iomux		= {					\
+			{ .type = iom0, .offset = -1 },			\
+			{ .type = iom1, .offset = -1 },			\
+			{ .type = iom2, .offset = -1 },			\
+			{ .type = iom3, .offset = -1 },			\
+		},							\
+		.drv		= {					\
+			{ .drv_type = drv0, .offset = offset0 },	\
+			{ .drv_type = drv1, .offset = offset1 },	\
+			{ .drv_type = drv2, .offset = offset2 },	\
+			{ .drv_type = drv3, .offset = offset3 },	\
+		},							\
+	}
+
 /**
  */
 struct rockchip_pin_ctrl {
@@ -161,6 +230,9 @@ struct rockchip_pin_ctrl {
 	enum rockchip_pinctrl_type	type;
 	int				grf_mux_offset;
 	int				pmu_mux_offset;
+	int				grf_drv_offset;
+	int				pmu_drv_offset;
+
 	void	(*pull_calc_reg)(struct rockchip_pin_bank *bank,
 				    int pin_num, struct regmap **regmap,
 				    int *reg, u8 *bit);
@@ -676,7 +748,71 @@ static void rk3368_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
 	}
 }
 
-static int rockchip_perpin_drv_list[] = { 2, 4, 8, 12 };
+#define RK3399_PULL_GRF_OFFSET		0xe040
+#define RK3399_PULL_PMU_OFFSET		0x40
+#define RK3399_DRV_3BITS_PER_PIN	3
+
+static void rk3399_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+					 int pin_num, struct regmap **regmap,
+					 int *reg, u8 *bit)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+
+	/* The bank0:16 and bank1:32 pins are located in PMU */
+	if ((bank->bank_num == 0) || (bank->bank_num == 1)) {
+		*regmap = info->regmap_pmu;
+		*reg = RK3399_PULL_PMU_OFFSET;
+
+		*reg += bank->bank_num * RK3188_PULL_BANK_STRIDE;
+
+		*reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4);
+		*bit = pin_num % RK3188_PULL_PINS_PER_REG;
+		*bit *= RK3188_PULL_BITS_PER_PIN;
+	} else {
+		*regmap = info->regmap_base;
+		*reg = RK3399_PULL_GRF_OFFSET;
+
+		/* correct the offset, as we're starting with the 3rd bank */
+		*reg -= 0x20;
+		*reg += bank->bank_num * RK3188_PULL_BANK_STRIDE;
+		*reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4);
+
+		*bit = (pin_num % RK3188_PULL_PINS_PER_REG);
+		*bit *= RK3188_PULL_BITS_PER_PIN;
+	}
+}
+
+static void rk3399_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
+					int pin_num, struct regmap **regmap,
+					int *reg, u8 *bit)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+	int drv_num = (pin_num / 8);
+
+	/*  The bank0:16 and bank1:32 pins are located in PMU */
+	if ((bank->bank_num == 0) || (bank->bank_num == 1))
+		*regmap = info->regmap_pmu;
+	else
+		*regmap = info->regmap_base;
+
+	*reg = bank->drv[drv_num].offset;
+	if ((bank->drv[drv_num].drv_type == DRV_TYPE_IO_1V8_3V0_AUTO) ||
+	    (bank->drv[drv_num].drv_type == DRV_TYPE_IO_3V3_ONLY)) {
+		if ((pin_num % 8) >= 6)
+			*reg += 0x4;
+		*bit = ((pin_num % 8) * 3) % 16;
+	} else {
+		*bit = (pin_num % 8) * 2;
+	}
+}
+
+static int rockchip_perpin_drv_list[DRV_TYPE_MAX][8] = {
+	{ 2, 4, 8, 12, -1, -1, -1, -1 },
+	{ 3, 6, 9, 12, -1, -1, -1, -1 },
+	{ 5, 10, 15, 20, -1, -1, -1, -1 },
+	{ 4, 6, 8, 10, 12, 14, 16, 18 },
+	{ 4, 7, 10, 13, 16, 19, 22, 26 }
+};
 
 static int rockchip_get_drive_perpin(struct rockchip_pin_bank *bank,
 				     int pin_num)
@@ -685,19 +821,60 @@ static int rockchip_get_drive_perpin(struct rockchip_pin_bank *bank,
 	struct rockchip_pin_ctrl *ctrl = info->ctrl;
 	struct regmap *regmap;
 	int reg, ret;
-	u32 data;
+	u32 data, temp, rmask_bits;
 	u8 bit;
+	int drv_type = bank->drv[pin_num / 8].drv_type;
 
 	ctrl->drv_calc_reg(bank, pin_num, &regmap, &reg, &bit);
 
+	switch (drv_type) {
+	case DRV_TYPE_IO_1V8_3V0_AUTO:
+	case DRV_TYPE_IO_3V3_ONLY:
+		if (RK3399_DRV_3BITS_PER_PIN + bit >= 16) {
+			/* need to read regs twice */
+			ret = regmap_read(regmap, reg, &temp);
+			if (ret)
+				return ret;
+
+			temp >>= bit;
+			rmask_bits = 16 - bit;
+			temp &= (1 << rmask_bits) - 1;
+
+			reg = reg + 0x4;
+			ret = regmap_read(regmap, reg, &data);
+			if (ret)
+				return ret;
+
+			rmask_bits = RK3399_DRV_3BITS_PER_PIN
+				+ bit - 16;
+			data &= (1 << rmask_bits) - 1;
+			data <<= 16 - bit;
+			data |= temp;
+
+			return rockchip_perpin_drv_list[drv_type][data];
+		}
+
+		rmask_bits = RK3399_DRV_3BITS_PER_PIN;
+		break;
+	case DRV_TYPE_IO_DEFAULT:
+	case DRV_TYPE_IO_1V8_OR_3V0:
+	case DRV_TYPE_IO_1V8_ONLY:
+		rmask_bits = RK3288_DRV_BITS_PER_PIN;
+		break;
+	default:
+		dev_err(info->dev, "unsupported pinctrl drive type: %d\n",
+			drv_type);
+		return -EINVAL;
+	}
+
 	ret = regmap_read(regmap, reg, &data);
 	if (ret)
 		return ret;
 
 	data >>= bit;
-	data &= (1 << RK3288_DRV_BITS_PER_PIN) - 1;
+	data &= (1 << rmask_bits) - 1;
 
-	return rockchip_perpin_drv_list[data];
+	return rockchip_perpin_drv_list[drv_type][data];
 }
 
 static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank,
@@ -708,16 +885,23 @@ static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank,
 	struct regmap *regmap;
 	unsigned long flags;
 	int reg, ret, i;
-	u32 data, rmask;
+	u32 data, rmask, rmask_bits;
 	u8 bit;
+	int drv_type = bank->drv[pin_num / 8].drv_type;
+
+	dev_dbg(info->dev, "setting drive of GPIO%d-%d to %d\n",
+		bank->bank_num, pin_num, strength);
 
 	ctrl->drv_calc_reg(bank, pin_num, &regmap, &reg, &bit);
 
 	ret = -EINVAL;
-	for (i = 0; i < ARRAY_SIZE(rockchip_perpin_drv_list); i++) {
-		if (rockchip_perpin_drv_list[i] == strength) {
+	for (i = 0; i < ARRAY_SIZE(rockchip_perpin_drv_list[drv_type]); i++) {
+		if (rockchip_perpin_drv_list[drv_type][i] == strength) {
 			ret = i;
 			break;
+		} else if (rockchip_perpin_drv_list[drv_type][i] < 0) {
+			ret = rockchip_perpin_drv_list[drv_type][i];
+			break;
 		}
 	}
 
@@ -729,8 +913,44 @@ static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank,
 
 	spin_lock_irqsave(&bank->slock, flags);
 
+	switch (drv_type) {
+	case DRV_TYPE_IO_1V8_3V0_AUTO:
+	case DRV_TYPE_IO_3V3_ONLY:
+		if (RK3399_DRV_3BITS_PER_PIN + bit >= 16) {
+			/* need to write regs twice */
+			rmask_bits = 16 - bit;
+			/* enable the write to the equivalent lower bits */
+			data = ((1 << rmask_bits) - 1) << (bit + 16);
+			rmask = data | (data >> 16);
+			ret &= (1 << rmask_bits) - 1;
+			data |= (ret << bit);
+
+			ret = regmap_update_bits(regmap, reg, rmask, data);
+			if (ret)
+				return ret;
+
+			ret = i >> rmask_bits;
+			rmask_bits = RK3399_DRV_3BITS_PER_PIN + bit - 16;
+			reg = reg + 0x4;
+			bit = 0;
+		} else {
+			rmask_bits = RK3399_DRV_3BITS_PER_PIN;
+		}
+		break;
+	case DRV_TYPE_IO_DEFAULT:
+	case DRV_TYPE_IO_1V8_OR_3V0:
+	case DRV_TYPE_IO_1V8_ONLY:
+		rmask_bits = RK3288_DRV_BITS_PER_PIN;
+		break;
+	default:
+		dev_err(info->dev, "unsupported pinctrl drive type: %d\n",
+			drv_type);
+		return -EINVAL;
+
+	}
+
 	/* enable the write to the equivalent lower bits */
-	data = ((1 << RK3288_DRV_BITS_PER_PIN) - 1) << (bit + 16);
+	data = ((1 << rmask_bits) - 1) << (bit + 16);
 	rmask = data | (data >> 16);
 	data |= (ret << bit);
 
@@ -767,6 +987,7 @@ static int rockchip_get_pull(struct rockchip_pin_bank *bank, int pin_num)
 	case RK3188:
 	case RK3288:
 	case RK3368:
+	case RK3399:
 		data >>= bit;
 		data &= (1 << RK3188_PULL_BITS_PER_PIN) - 1;
 
@@ -823,6 +1044,7 @@ static int rockchip_set_pull(struct rockchip_pin_bank *bank,
 	case RK3188:
 	case RK3288:
 	case RK3368:
+	case RK3399:
 		spin_lock_irqsave(&bank->slock, flags);
 
 		/* enable the write to the equivalent lower bits */
@@ -1003,6 +1225,7 @@ static bool rockchip_pinconf_pull_valid(struct rockchip_pin_ctrl *ctrl,
 	case RK3188:
 	case RK3288:
 	case RK3368:
+	case RK3399:
 		return (pull != PIN_CONFIG_BIAS_PULL_PIN_DEFAULT);
 	}
 
@@ -1860,7 +2083,7 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
 	struct device_node *np;
 	struct rockchip_pin_ctrl *ctrl;
 	struct rockchip_pin_bank *bank;
-	int grf_offs, pmu_offs, i, j;
+	int grf_offs, pmu_offs, drv_grf_offs, drv_pmu_offs, i, j;
 
 	match = of_match_node(rockchip_pinctrl_dt_match, node);
 	ctrl = (struct rockchip_pin_ctrl *)match->data;
@@ -1884,6 +2107,8 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
 
 	grf_offs = ctrl->grf_mux_offset;
 	pmu_offs = ctrl->pmu_mux_offset;
+	drv_pmu_offs = ctrl->pmu_drv_offset;
+	drv_grf_offs = ctrl->grf_drv_offset;
 	bank = ctrl->pin_banks;
 	for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
 		int bank_pins = 0;
@@ -1893,15 +2118,16 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
 		bank->pin_base = ctrl->nr_pins;
 		ctrl->nr_pins += bank->nr_pins;
 
-		/* calculate iomux offsets */
+		/* calculate iomux and drv offsets */
 		for (j = 0; j < 4; j++) {
 			struct rockchip_iomux *iom = &bank->iomux[j];
+			struct rockchip_drv *drv = &bank->drv[j];
 			int inc;
 
 			if (bank_pins >= bank->nr_pins)
 				break;
 
-			/* preset offset value, set new start value */
+			/* preset iomux offset value, set new start value */
 			if (iom->offset >= 0) {
 				if (iom->type & IOMUX_SOURCE_PMU)
 					pmu_offs = iom->offset;
@@ -1912,8 +2138,19 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
 							pmu_offs : grf_offs;
 			}
 
-			dev_dbg(d->dev, "bank %d, iomux %d has offset 0x%x\n",
-				 i, j, iom->offset);
+			/* preset drv offset value, set new start value */
+			if (drv->offset >= 0) {
+				if (iom->type & IOMUX_SOURCE_PMU)
+					drv_pmu_offs = drv->offset;
+				else
+					drv_grf_offs = drv->offset;
+			} else { /* set current drv offset */
+				drv->offset = (iom->type & IOMUX_SOURCE_PMU) ?
+						drv_pmu_offs : drv_grf_offs;
+			}
+
+			dev_dbg(d->dev, "bank %d, iomux %d has iom_offset 0x%x drv_offset 0x%x\n",
+				i, j, iom->offset, drv->offset);
 
 			/*
 			 * Increase offset according to iomux width.
@@ -1925,6 +2162,21 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
 			else
 				grf_offs += inc;
 
+			/*
+			 * Increase offset according to drv width.
+			 * 3bit drive-strenth'es are spread over two registers.
+			 */
+			if ((drv->drv_type == DRV_TYPE_IO_1V8_3V0_AUTO) ||
+			    (drv->drv_type == DRV_TYPE_IO_3V3_ONLY))
+				inc = 8;
+			else
+				inc = 4;
+
+			if (iom->type & IOMUX_SOURCE_PMU)
+				drv_pmu_offs += inc;
+			else
+				drv_grf_offs += inc;
+
 			bank_pins += 8;
 		}
 	}
@@ -2208,6 +2460,62 @@ static struct rockchip_pin_ctrl rk3368_pin_ctrl = {
 		.drv_calc_reg		= rk3368_calc_drv_reg_and_bit,
 };
 
+static struct rockchip_pin_bank rk3399_pin_banks[] = {
+	PIN_BANK_IOMUX_DRV_FLAGS_OFFSET(0, 32, "gpio0", IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					DRV_TYPE_IO_1V8_ONLY,
+					DRV_TYPE_IO_1V8_ONLY,
+					0,
+					0,
+					0x0,
+					0x8,
+					-1,
+					-1
+					),
+	PIN_BANK_IOMUX_DRV_FLAGS_OFFSET(1, 32, "gpio1", IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					DRV_TYPE_IO_1V8_OR_3V0,
+					DRV_TYPE_IO_1V8_OR_3V0,
+					DRV_TYPE_IO_1V8_OR_3V0,
+					DRV_TYPE_IO_1V8_OR_3V0,
+					0x20,
+					0x28,
+					0x30,
+					0x38
+					),
+	PIN_BANK_DRV_FLAGS(2, 32, "gpio2", DRV_TYPE_IO_1V8_OR_3V0,
+			   DRV_TYPE_IO_1V8_OR_3V0,
+			   DRV_TYPE_IO_1V8_ONLY,
+			   DRV_TYPE_IO_1V8_ONLY
+			   ),
+	PIN_BANK_DRV_FLAGS(3, 32, "gpio3", DRV_TYPE_IO_3V3_ONLY,
+			   DRV_TYPE_IO_3V3_ONLY,
+			   DRV_TYPE_IO_3V3_ONLY,
+			   DRV_TYPE_IO_1V8_OR_3V0
+			   ),
+	PIN_BANK_DRV_FLAGS(4, 32, "gpio4", DRV_TYPE_IO_1V8_OR_3V0,
+			   DRV_TYPE_IO_1V8_3V0_AUTO,
+			   DRV_TYPE_IO_1V8_OR_3V0,
+			   DRV_TYPE_IO_1V8_OR_3V0
+			   ),
+};
+
+static struct rockchip_pin_ctrl rk3399_pin_ctrl = {
+		.pin_banks		= rk3399_pin_banks,
+		.nr_banks		= ARRAY_SIZE(rk3399_pin_banks),
+		.label			= "RK3399-GPIO",
+		.type			= RK3399,
+		.grf_mux_offset		= 0xe000,
+		.pmu_mux_offset		= 0x0,
+		.grf_drv_offset		= 0xe100,
+		.pmu_drv_offset		= 0x80,
+		.pull_calc_reg		= rk3399_calc_pull_reg_and_bit,
+		.drv_calc_reg		= rk3399_calc_drv_reg_and_bit,
+};
 
 static const struct of_device_id rockchip_pinctrl_dt_match[] = {
 	{ .compatible = "rockchip,rk2928-pinctrl",
@@ -2224,6 +2532,8 @@ static const struct of_device_id rockchip_pinctrl_dt_match[] = {
 		.data = (void *)&rk3288_pin_ctrl },
 	{ .compatible = "rockchip,rk3368-pinctrl",
 		.data = (void *)&rk3368_pin_ctrl },
+	{ .compatible = "rockchip,rk3399-pinctrl",
+		.data = (void *)&rk3399_pin_ctrl },
 	{},
 };
 MODULE_DEVICE_TABLE(of, rockchip_pinctrl_dt_match);
-- 
1.9.1




From jean-michel.hautbois at veo-labs.com  Tue Jan  5 07:40:54 2016
From: jean-michel.hautbois at veo-labs.com (Jean-Michel Hautbois)
Date: Tue, 5 Jan 2016 16:40:54 +0100
Subject: [alsa-devel] [PATCH 0/9] dw-hdmi audio support
In-Reply-To: <1440664941.3233.38.camel@pengutronix.de>
References: <20150808160251.GM7557@n2100.arm.linux.org.uk>
 <20150808160936.GN7557@n2100.arm.linux.org.uk>
 <1440664941.3233.38.camel@pengutronix.de>
Message-ID: <CAH-u=810V863yYhMvvtaQ7jxNgrOXAL4bi1mS506a-yOTbuYhQ@mail.gmail.com>

Hi Russell,

2015-08-27 10:42 GMT+02:00 Philipp Zabel <p.zabel at pengutronix.de>:
>
> Am Samstag, den 08.08.2015, 17:09 +0100 schrieb Russell King - ARM
> Linux:
> > Following on from the previous sub-series, this sub-series adds audio
> > support to dw-hdmi.
> >
> > The two different variants are now in this patch: AHB audio support
> > found on iMX6 platforms, and I2S support found on Rockchip patches.
> > Thanks to Yakir Yang for contributing the I2S support.
> >
> > I suspect that there is still some discussion to be had on this
> > series, though I would like to see it moving forward so that we can
> > get something merged.
>
> Tested-by: Philipp Zabel <p.zabel at pengutronix.de>
> on i.MX6 GK802 via HDMI connected to a TV (stereo only).
>
> except for the i2s patch, which is broken in this series.
>
> regards
> Philipp


What is the status of this series ?
I would like to use audio output in HDMI on my i.MX6 board, but I
don't know if you have some pending WIP on this ?

Thanks,
JM


From festevam at gmail.com  Tue Jan  5 07:54:26 2016
From: festevam at gmail.com (Fabio Estevam)
Date: Tue, 5 Jan 2016 13:54:26 -0200
Subject: [alsa-devel] [PATCH 0/9] dw-hdmi audio support
In-Reply-To: <CAH-u=810V863yYhMvvtaQ7jxNgrOXAL4bi1mS506a-yOTbuYhQ@mail.gmail.com>
References: <20150808160251.GM7557@n2100.arm.linux.org.uk>
 <20150808160936.GN7557@n2100.arm.linux.org.uk>
 <1440664941.3233.38.camel@pengutronix.de>
 <CAH-u=810V863yYhMvvtaQ7jxNgrOXAL4bi1mS506a-yOTbuYhQ@mail.gmail.com>
Message-ID: <CAOMZO5Ax5-uEHLCKmc7qHWSK9mBmzMOOB=rCTxHGbQ_Xv89KrQ@mail.gmail.com>

On Tue, Jan 5, 2016 at 1:40 PM, Jean-Michel Hautbois
<jean-michel.hautbois at veo-labs.com> wrote:

> What is the status of this series ?
> I would like to use audio output in HDMI on my i.MX6 board, but I
> don't know if you have some pending WIP on this ?

This series is in mainline since 4.4-rc1.


From robh at kernel.org  Tue Jan  5 08:06:04 2016
From: robh at kernel.org (Rob Herring)
Date: Tue, 5 Jan 2016 10:06:04 -0600
Subject: [PATCH v2 3/3] dt-bindings: add document for rk3229-vop
In-Reply-To: <1451966436-12733-1-git-send-email-ykk@rock-chips.com>
References: <1451965688-12286-1-git-send-email-ykk@rock-chips.com>
 <1451966436-12733-1-git-send-email-ykk@rock-chips.com>
Message-ID: <CAL_JsqKnyYJvgh05OTr6S2pG5+OqFHg-SZtSvpahb0pg64tmkQ@mail.gmail.com>

On Mon, Jan 4, 2016 at 10:00 PM, Yakir Yang <ykk at rock-chips.com> wrote:
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
> Acked-by: Rob Herring <robh at kernel.org>
> ---
> Changes in v2:
> - acked by Rob

It doesn't hurt, but there is no point to send a new version just to
add acks. The maintainer should add any for the current version. If
you do send a new version with changes, then it is your responsibility
to add acks. Maintainers are not going to look at older versions.

Rob


From linux at arm.linux.org.uk  Tue Jan  5 08:04:21 2016
From: linux at arm.linux.org.uk (Russell King - ARM Linux)
Date: Tue, 5 Jan 2016 16:04:21 +0000
Subject: [alsa-devel] [PATCH 0/9] dw-hdmi audio support
In-Reply-To: <CAH-u=810V863yYhMvvtaQ7jxNgrOXAL4bi1mS506a-yOTbuYhQ@mail.gmail.com>
References: <20150808160251.GM7557@n2100.arm.linux.org.uk>
 <20150808160936.GN7557@n2100.arm.linux.org.uk>
 <1440664941.3233.38.camel@pengutronix.de>
 <CAH-u=810V863yYhMvvtaQ7jxNgrOXAL4bi1mS506a-yOTbuYhQ@mail.gmail.com>
Message-ID: <20160105160421.GX19062@n2100.arm.linux.org.uk>

On Tue, Jan 05, 2016 at 04:40:54PM +0100, Jean-Michel Hautbois wrote:
> Hi Russell,
> 
> 2015-08-27 10:42 GMT+02:00 Philipp Zabel <p.zabel at pengutronix.de>:
> >
> > Am Samstag, den 08.08.2015, 17:09 +0100 schrieb Russell King - ARM
> > Linux:
> > > Following on from the previous sub-series, this sub-series adds audio
> > > support to dw-hdmi.
> > >
> > > The two different variants are now in this patch: AHB audio support
> > > found on iMX6 platforms, and I2S support found on Rockchip patches.
> > > Thanks to Yakir Yang for contributing the I2S support.
> > >
> > > I suspect that there is still some discussion to be had on this
> > > series, though I would like to see it moving forward so that we can
> > > get something merged.
> >
> > Tested-by: Philipp Zabel <p.zabel at pengutronix.de>
> > on i.MX6 GK802 via HDMI connected to a TV (stereo only).
> >
> > except for the i2s patch, which is broken in this series.
> >
> > regards
> > Philipp
> 
> 
> What is the status of this series ?
> I would like to use audio output in HDMI on my i.MX6 board, but I
> don't know if you have some pending WIP on this ?

The I2S part has been dropped.  The DesignWare HDMI block is configurable
when it is synthesized - it can contain either the AHB audio interface or
an I2S interface.  Freescale chose to synthesize it with the AHB audio
interface, and this is what my patches are geared up to provide.

On Rockchip devices, they chose to synthesize it with the I2S audio
block, and so they need a different driver for it.  Yakir Yang has been
working on that, but I've not seen anything recently.  After merging
his I2S patch, I found some problems and decided with Yakir that the
best thing to do was to drop it.

So, the result is we support HDMI audio on iMX6.

The changes were merged into mainline during the 4.4 merge window, so
Linux 4.4 will support iMX6 HDMI audio.

-- 
RMK's Patch system: http://www.arm.linux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.


From ykk at rock-chips.com  Tue Jan  5 16:46:27 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Wed, 6 Jan 2016 08:46:27 +0800
Subject: [PATCH v2 3/3] dt-bindings: add document for rk3229-vop
In-Reply-To: <CAL_JsqKnyYJvgh05OTr6S2pG5+OqFHg-SZtSvpahb0pg64tmkQ@mail.gmail.com>
References: <1451965688-12286-1-git-send-email-ykk@rock-chips.com>
 <1451966436-12733-1-git-send-email-ykk@rock-chips.com>
 <CAL_JsqKnyYJvgh05OTr6S2pG5+OqFHg-SZtSvpahb0pg64tmkQ@mail.gmail.com>
Message-ID: <568C63E3.1010908@rock-chips.com>



On 01/06/2016 12:06 AM, Rob Herring wrote:
> On Mon, Jan 4, 2016 at 10:00 PM, Yakir Yang <ykk at rock-chips.com> wrote:
>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
>> Acked-by: Rob Herring <robh at kernel.org>
>> ---
>> Changes in v2:
>> - acked by Rob
> It doesn't hurt, but there is no point to send a new version just to
> add acks. The maintainer should add any for the current version. If
> you do send a new version with changes, then it is your responsibility
> to add acks. Maintainers are not going to look at older versions.

Got it  :)

- Yakir

> Rob
>
>
>




From mark.yao at rock-chips.com  Tue Jan  5 16:56:48 2016
From: mark.yao at rock-chips.com (Mark yao)
Date: Wed, 06 Jan 2016 08:56:48 +0800
Subject: [PATCH v2 2/3] drm/rockchip: vop: add rk3229 vop support
In-Reply-To: <1451966307-12642-1-git-send-email-ykk@rock-chips.com>
References: <1451965688-12286-1-git-send-email-ykk@rock-chips.com>
 <1451966307-12642-1-git-send-email-ykk@rock-chips.com>
Message-ID: <568C6650.5040708@rock-chips.com>

On 2016?01?05? 11:58, Yakir Yang wrote:
> RK3229 registers layout is simalar to RK3288 layout, only the
> interruput registers is different to RK3288.
>
> RK3229 support two overlay plane and one hwc plane, max output
> resolution is 4K. it support IOMMU, and its IOMMU same as rk3288's.
>
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
> ---
> Changes in v2:
> - Separate the write-mask changes out, and remove the DUMMY_INTR marcos (Heiko)
>
>   drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 110 ++++++++++++++++++++++++++++
>   drivers/gpu/drm/rockchip/rockchip_vop_reg.h |  90 +++++++++++++++++++++++
>   2 files changed, 200 insertions(+)
>
> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> index 7fbaf76..f1358f9 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> @@ -191,6 +191,114 @@ static const struct vop_data rk3288_vop = {
>   	.win_size = ARRAY_SIZE(rk3288_vop_win_data),
>   };
>   
> +static const struct vop_scl_extension rk3229_win_full_scl_ext = {
> +	.cbcr_vsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 31),
> +	.cbcr_vsu_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 30),
> +	.cbcr_hsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 28),
> +	.cbcr_ver_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 26),
> +	.cbcr_hor_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 24),
> +	.yrgb_vsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 23),
> +	.yrgb_vsu_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 22),
> +	.yrgb_hsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 20),
> +	.yrgb_ver_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 18),
> +	.yrgb_hor_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 16),
> +	.line_load_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 15),
> +	.cbcr_axi_gather_num = VOP_REG(RK3229_WIN0_CTRL1, 0x7, 12),
> +	.yrgb_axi_gather_num = VOP_REG(RK3229_WIN0_CTRL1, 0xf, 8),
> +	.vsd_cbcr_gt2 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 7),
> +	.vsd_cbcr_gt4 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 6),
> +	.vsd_yrgb_gt2 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 5),
> +	.vsd_yrgb_gt4 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 4),
> +	.bic_coe_sel = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 2),
> +	.cbcr_axi_gather_en = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 1),
> +	.yrgb_axi_gather_en = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 0),
> +	.lb_mode = VOP_REG(RK3229_WIN0_CTRL0, 0x7, 5),
> +};
> +
> +static const struct vop_scl_regs rk3229_win_full_scl = {
> +	.ext = &rk3229_win_full_scl_ext,
> +	.scale_yrgb_x = VOP_REG(RK3229_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
> +	.scale_yrgb_y = VOP_REG(RK3229_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
> +	.scale_cbcr_x = VOP_REG(RK3229_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0),
> +	.scale_cbcr_y = VOP_REG(RK3229_WIN0_SCL_FACTOR_CBR, 0xffff, 16),
> +};
> +
> +static const struct vop_win_phy rk3229_win01_data = {
> +	.scl = &rk3229_win_full_scl,
> +	.data_formats = formats_win_full,
> +	.nformats = ARRAY_SIZE(formats_win_full),
> +	.enable = VOP_REG(RK3229_WIN0_CTRL0, 0x1, 0),
> +	.format = VOP_REG(RK3229_WIN0_CTRL0, 0x7, 1),
> +	.rb_swap = VOP_REG(RK3229_WIN0_CTRL0, 0x1, 12),
> +	.act_info = VOP_REG(RK3229_WIN0_ACT_INFO, 0x1fff1fff, 0),
> +	.dsp_info = VOP_REG(RK3229_WIN0_DSP_INFO, 0x0fff0fff, 0),
> +	.dsp_st = VOP_REG(RK3229_WIN0_DSP_ST, 0x1fff1fff, 0),
> +	.yrgb_mst = VOP_REG(RK3229_WIN0_YRGB_MST, 0xffffffff, 0),
> +	.uv_mst = VOP_REG(RK3229_WIN0_CBR_MST, 0xffffffff, 0),
> +	.yrgb_vir = VOP_REG(RK3229_WIN0_VIR, 0x3fff, 0),
> +	.uv_vir = VOP_REG(RK3229_WIN0_VIR, 0x3fff, 16),
> +	.src_alpha_ctl = VOP_REG(RK3229_WIN0_SRC_ALPHA_CTRL, 0xff, 0),
> +	.dst_alpha_ctl = VOP_REG(RK3229_WIN0_DST_ALPHA_CTRL, 0xff, 0),
> +};
> +
> +static const struct vop_win_data rk3229_vop_win_data[] = {
> +	{ .base = 0x00, .phy = &rk3229_win01_data,
> +	  .type = DRM_PLANE_TYPE_PRIMARY },
> +	{ .base = 0x40, .phy = &rk3229_win01_data,
> +	  .type = DRM_PLANE_TYPE_CURSOR },
> +};
> +
> +static const struct vop_ctrl rk3229_ctrl_data = {
> +	.cfg_done = VOP_REG(RK3229_REG_CFG_DONE, 0x1, 0),
> +	.standby = VOP_REG(RK3229_SYS_CTRL, 0x1, 22),
> +	.gate_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 23),
> +	.mmu_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 20),
> +	.rgb_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 12),
> +	.hdmi_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 13),
> +	.edp_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 14),
> +	.mipi_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 15),
> +	.data_blank = VOP_REG(RK3229_DSP_CTRL0, 0x1, 19),
> +	.out_mode = VOP_REG(RK3229_DSP_CTRL0, 0xf, 0),
> +	.pin_pol = VOP_REG(RK3229_DSP_CTRL1, 0xf, 20),
> +	.dither_up = VOP_REG(RK3229_DSP_CTRL1, 0x1, 6),
> +	.htotal_pw = VOP_REG(RK3229_DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
> +	.hact_st_end = VOP_REG(RK3229_DSP_HACT_ST_END, 0x1fff1fff, 0),
> +	.vtotal_pw = VOP_REG(RK3229_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
> +	.vact_st_end = VOP_REG(RK3229_DSP_VACT_ST_END, 0x1fff1fff, 0),
> +	.hpost_st_end = VOP_REG(RK3229_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
> +	.vpost_st_end = VOP_REG(RK3229_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
> +};

Hi Yakir

RK3229 registers layout is simalar to RK3288 layout, is possible to reuse rk3288 register table?

rk3229_ctrl_data & rk3229_win01_data seems same as rk3288_ctrl_data & rk3288_win01_data.

Thanks.

-- 
?ark Yao




From mark.yao at rock-chips.com  Tue Jan  5 17:48:08 2016
From: mark.yao at rock-chips.com (Mark yao)
Date: Wed, 06 Jan 2016 09:48:08 +0800
Subject: [PATCH v6.3 4/6] drm: rockchip: Support Synopsys DW MIPI DSI
In-Reply-To: <1450842215-10883-1-git-send-email-zyw@rock-chips.com>
References: <1450260616-21160-5-git-send-email-zyw@rock-chips.com>
 <1450842215-10883-1-git-send-email-zyw@rock-chips.com>
Message-ID: <568C7258.3010104@rock-chips.com>

On 2015?12?23? 11:43, Chris Zhong wrote:
> +static int dw_mipi_dsi_register(struct drm_device *drm,
> +				      struct dw_mipi_dsi *dsi)
> +{
> +	struct drm_encoder *encoder = &dsi->encoder;
> +	struct drm_connector *connector = &dsi->connector;
> +	struct device *dev = dsi->dev;
> +	int ret;
> +
> +	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm,
> +							     dev->of_node);
> +	/*
> +	 * If we failed to find the CRTC(s) which this encoder is
> +	 * supposed to be connected to, it's because the CRTC has
> +	 * not been registered yet.  Defer probing, and hope that
> +	 * the required CRTC is added later.
> +	 */
> +	if (encoder->possible_crtcs == 0)
> +		return -EPROBE_DEFER;
> +
> +	drm_encoder_helper_add(&dsi->encoder,
> +			       &dw_mipi_dsi_encoder_helper_funcs);
> +	ret = drm_encoder_init(drm, &dsi->encoder, &dw_mipi_dsi_encoder_funcs,
> +			 DRM_MODE_ENCODER_DSI);
> +	if (ret) {
> +		dev_err(dev, "Failed to initialize encoder with drm\n");
> +		return ret;
> +	}
> +
> +	drm_connector_helper_add(connector,
> +			&dw_mipi_dsi_connector_helper_funcs);
> +
> +	drm_connector_init(drm, &dsi->connector,
> +			   &dw_mipi_dsi_atomic_connector_funcs,
> +			   DRM_MODE_CONNECTOR_DSI);
> +
Hi Chris

I try to pick your patches, but found following issues:

drivers/gpu/drm/rockchip/dw-mipi-dsi.c: ????dw_mipi_dsi_register??:
drivers/gpu/drm/rockchip/dw-mipi-dsi.c:1024:5: ??? ????? 
?drm_encoder_init??????
include/drm/drm_crtc.h:2220:5: ??? ????
make[4]: *** [drivers/gpu/drm/rockchip/dw-mipi-dsi.o] ?? 1

Can you rebase to newest Dave's drm-next and fix this problem?

Thanks.

-- 
?ark Yao




From mark.yao at rock-chips.com  Tue Jan  5 18:04:43 2016
From: mark.yao at rock-chips.com (Mark yao)
Date: Wed, 06 Jan 2016 10:04:43 +0800
Subject: [PATCH v6.3 4/6] drm: rockchip: Support Synopsys DW MIPI DSI
In-Reply-To: <1450842215-10883-1-git-send-email-zyw@rock-chips.com>
References: <1450260616-21160-5-git-send-email-zyw@rock-chips.com>
 <1450842215-10883-1-git-send-email-zyw@rock-chips.com>
Message-ID: <568C763B.1030900@rock-chips.com>

On 2015?12?23? 11:43, Chris Zhong wrote:
> @@ -34,3 +34,13 @@ config ROCKCHIP_ANALOGIX_DP
>   	  This selects support for Rockchip SoC specific extensions
>   	  for the Analogix Core DP driver. If you want to enable DP
>   	  on RK3288 based SoC, you should selet this option.
> +
> +config ROCKCHIP_DW_MIPI_DSI
> +	bool "Rockchip specific extensions for Synopsys DW MIPI DSI"

I think "bool" should be "tristate", so dw mipi can build as modules,  
since ROCKCHIP_DW_HDMI use tristate.

Thanks.

> +	depends on DRM_ROCKCHIP
> +	select DRM_MIPI_DSI
> +	help
> +	 This selects support for Rockchip SoC specific extensions
> +	 for the Synopsys DesignWare HDMI driver. If you want to
> +	 enable MIPI DSI on RK3288 based SoC, you should selet this
> +	 option.


-- 
?ark Yao




From zyw at rock-chips.com  Tue Jan  5 20:03:52 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Wed,  6 Jan 2016 12:03:52 +0800
Subject: [PATCH v7 0/5] Add mipi dsi support for rk3288
Message-ID: <1452053038-32098-1-git-send-email-zyw@rock-chips.com>


The rk3288 MIPI DSI is a Synopsys DesignWare MIPI DSI host controller
IP. This series adds support for a Synopsys DesignWare MIPI DSI host
controller DRM driver.

The MIPI DSI feature is tested on rk3288 evb board, backport them to
chrome os kernel chrome_v3.14, and it can display normally.

This patchset is base on the patchset from Ying.liu at freescale.com.
<http://www.spinics.net/lists/dri-devel/msg77181.html>

According to the suggestion from Thierry, I have get rid of the bridge,
and register the encoder & connecter in drm/rockchip/dw-mipi-dsi.c.

After backport below patches from linux-next, I can merge this serial into
Dave's drm-next branch.

94d5d6a0fbf3 clk: rockchip: add an id for rk3288 crypto clk
c6d49fbcfcc4 clk: rockchip: add id for mipidsi sclk on rk3288
a2f4c560f18e clk: rockchip: add mipidsi clock on rk3288
69c923743640 ARM: dts: rockchip: correct the name of REG8 for rk3288-evb-act8846
662513a14c9d ARM: dts: rockchip: add 2 regulators for rk3288-evb-act8846
b04061e6599b ARM: dts: rockchip: move the public part to rk3288-evb common


Changes in v7:
- modify the config to tristate for modules build (Mark Yao)
- Pass NULL 'name' to drm_encoder_init() to fix compile err (Mark Yao)
- Move the lcd_en control to act8846 dts

Changes in v6:
- update the document, since the bridge device has been deleted.
- move the mipi_en gate to ockchip_drm_crtc_mode_config
- Remove the atomic feature check (Mark Yao)
- Add atomic API support (Heiko St?bne)
- Do not use bridge driver (Thierry Reding)
- Optimization the phy init sequence

Changes in v5:
- modify the clk name to SCLK_MIPIDSI_24M
- add a blank line befor lcd_en

Changes in v4:
- use clk_round_rate to check the clock rate in vop_crtc_mode_fixup

Changes in v3:
- move dw_mipi_dsi_rockchip.txt to bindings/display/rockchip/

Chris Zhong (5):
  drm/rockchip: return a true clock rate to adjusted_mode
  Documentation: dt-bindings: Add bindings for rk3288 DW MIPI DSI driver
  drm: rockchip: Support Synopsys DW MIPI DSI
  ARM: dts: rockchip: add rk3288 mipi_dsi nodes
  ARM: dts: rockchip: add support mipi panel tv080wum-nl0

 .../display/rockchip/dw_mipi_dsi_rockchip.txt      |   60 +
 arch/arm/boot/dts/rk3288-evb-act8846.dts           |    2 +
 arch/arm/boot/dts/rk3288-evb.dtsi                  |   12 +-
 arch/arm/boot/dts/rk3288.dtsi                      |   39 +
 drivers/gpu/drm/rockchip/Kconfig                   |   10 +
 drivers/gpu/drm/rockchip/Makefile                  |    1 +
 drivers/gpu/drm/rockchip/dw-mipi-dsi.c             | 1196 ++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c        |    8 +
 8 files changed, 1327 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt
 create mode 100644 drivers/gpu/drm/rockchip/dw-mipi-dsi.c

-- 
2.6.3



From zyw at rock-chips.com  Tue Jan  5 20:03:53 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Wed,  6 Jan 2016 12:03:53 +0800
Subject: [PATCH v7 1/5] drm/rockchip: return a true clock rate to adjusted_mode
In-Reply-To: <1452053038-32098-1-git-send-email-zyw@rock-chips.com>
References: <1452053038-32098-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1452053038-32098-2-git-send-email-zyw@rock-chips.com>

Since the mipi dsi driver need to use the clock of vop to make the
calculation of Blanking. But sometimes the clock driver can not set a
accurate clock_rate for vop, get it by clk_round_rate before mode_set,
so we can get the true value.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
Acked-by: Mark Yao <mark.yao at rock-chips.com>

---

Changes in v7: None
Changes in v6: None
Changes in v5: None
Changes in v4:
- use clk_round_rate to check the clock rate in vop_crtc_mode_fixup

Changes in v3: None

 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index f5b3da2..056cac3 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -878,9 +878,14 @@ static bool vop_crtc_mode_fixup(struct drm_crtc *crtc,
 				const struct drm_display_mode *mode,
 				struct drm_display_mode *adjusted_mode)
 {
+	struct vop *vop = to_vop(crtc);
+
 	if (adjusted_mode->htotal == 0 || adjusted_mode->vtotal == 0)
 		return false;
 
+	adjusted_mode->clock =
+		clk_round_rate(vop->dclk, mode->clock * 1000) / 1000;
+
 	return true;
 }
 
-- 
2.6.3



From zyw at rock-chips.com  Tue Jan  5 20:03:54 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Wed,  6 Jan 2016 12:03:54 +0800
Subject: [PATCH v7 2/5] Documentation: dt-bindings: Add bindings for rk3288 DW
 MIPI DSI driver
In-Reply-To: <1452053038-32098-1-git-send-email-zyw@rock-chips.com>
References: <1452053038-32098-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1452053038-32098-3-git-send-email-zyw@rock-chips.com>

add device tree bindings for rk3288 specific Synopsys DW MIPI DSI driver

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
Acked-by: Rob Herring <robh at kernel.org>

---

Changes in v7: None
Changes in v6:
- update the document, since the bridge device has been deleted.

Changes in v5: None
Changes in v4: None
Changes in v3:
- move dw_mipi_dsi_rockchip.txt to bindings/display/rockchip/

 .../display/rockchip/dw_mipi_dsi_rockchip.txt      | 60 ++++++++++++++++++++++
 1 file changed, 60 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt

diff --git a/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt
new file mode 100644
index 0000000..1753f0c
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt
@@ -0,0 +1,60 @@
+Rockchip specific extensions to the Synopsys Designware MIPI DSI
+================================
+
+Required properties:
+- #address-cells: Should be <1>.
+- #size-cells: Should be <0>.
+- compatible: "rockchip,rk3288-mipi-dsi", "snps,dw-mipi-dsi".
+- reg: Represent the physical address range of the controller.
+- interrupts: Represent the controller's interrupt to the CPU(s).
+- clocks, clock-names: Phandles to the controller's pll reference
+  clock(ref) and APB clock(pclk), as described in [1].
+- rockchip,grf: this soc should set GRF regs to mux vopl/vopb.
+- ports: contain a port node with endpoint definitions as defined in [2].
+  For vopb,set the reg = <0> and set the reg = <1> for vopl.
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+[2] Documentation/devicetree/bindings/media/video-interfaces.txt
+
+Example:
+	mipi_dsi: mipi at ff960000 {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		compatible = "rockchip,rk3288-mipi-dsi", "snps,dw-mipi-dsi";
+		reg = <0xff960000 0x4000>;
+		interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru SCLK_MIPI_24M>, <&cru PCLK_MIPI_DSI0>;
+		clock-names = "ref", "pclk";
+		rockchip,grf = <&grf>;
+		status = "okay";
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <1>;
+
+			mipi_in: port {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				mipi_in_vopb: endpoint at 0 {
+					reg = <0>;
+					remote-endpoint = <&vopb_out_mipi>;
+				};
+				mipi_in_vopl: endpoint at 1 {
+					reg = <1>;
+					remote-endpoint = <&vopl_out_mipi>;
+				};
+			};
+		};
+
+		panel {
+			compatible ="boe,tv080wum-nl0";
+			reg = <0>;
+
+			enable-gpios = <&gpio7 3 GPIO_ACTIVE_HIGH>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&lcd_en>;
+			backlight = <&backlight>;
+			status = "okay";
+		};
+	};
-- 
2.6.3



From zyw at rock-chips.com  Tue Jan  5 20:03:55 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Wed,  6 Jan 2016 12:03:55 +0800
Subject: [PATCH v7 3/5] drm: rockchip: Support Synopsys DW MIPI DSI
In-Reply-To: <1452053038-32098-1-git-send-email-zyw@rock-chips.com>
References: <1452053038-32098-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1452053038-32098-4-git-send-email-zyw@rock-chips.com>

Add support for Synopsys DesignWare MIPI DSI controller which is
embedded in the rk3288 SoCs.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
Acked-by: Mark Yao <mark.yao at rock-chips.com>
---

Changes in v7:
- modify the config to tristate for modules build (Mark Yao)
- Pass NULL 'name' to drm_encoder_init() to fix compile err (Mark Yao)

Changes in v6:
- move the mipi_en gate to ockchip_drm_crtc_mode_config
- Remove the atomic feature check (Mark Yao)
- Add atomic API support (Heiko St?bne)
- Do not use bridge driver (Thierry Reding)
- Optimization the phy init sequence

Changes in v5: None
Changes in v4: None
Changes in v3: None

 drivers/gpu/drm/rockchip/Kconfig            |   10 +
 drivers/gpu/drm/rockchip/Makefile           |    1 +
 drivers/gpu/drm/rockchip/dw-mipi-dsi.c      | 1196 +++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c |    3 +
 4 files changed, 1210 insertions(+)
 create mode 100644 drivers/gpu/drm/rockchip/dw-mipi-dsi.c

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 35215f6..8573985 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -25,3 +25,13 @@ config ROCKCHIP_DW_HDMI
 	  for the Synopsys DesignWare HDMI driver. If you want to
 	  enable HDMI on RK3288 based SoC, you should selet this
 	  option.
+
+config ROCKCHIP_DW_MIPI_DSI
+	tristate "Rockchip specific extensions for Synopsys DW MIPI DSI"
+	depends on DRM_ROCKCHIP
+	select DRM_MIPI_DSI
+	help
+	 This selects support for Rockchip SoC specific extensions
+	 for the Synopsys DesignWare HDMI driver. If you want to
+	 enable MIPI DSI on RK3288 based SoC, you should selet this
+	 option.
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index a9d380f..d1dc0f7 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -6,6 +6,7 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
 		rockchip_drm_gem.o
 
 obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
+obj-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
 
 obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_drm_vop.o \
 				rockchip_vop_reg.o
diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
new file mode 100644
index 0000000..b170f9d
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
@@ -0,0 +1,1196 @@
+/*
+ * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/iopoll.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drmP.h>
+#include <video/mipi_display.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_vop.h"
+
+#define DRIVER_NAME    "dw-mipi-dsi"
+
+#define GRF_SOC_CON6                    0x025c
+#define DSI0_SEL_VOP_LIT                (1 << 6)
+#define DSI1_SEL_VOP_LIT                (1 << 9)
+
+#define DSI_VERSION			0x00
+#define DSI_PWR_UP			0x04
+#define RESET				0
+#define POWERUP				BIT(0)
+
+#define DSI_CLKMGR_CFG			0x08
+#define TO_CLK_DIVIDSION(div)		(((div) & 0xff) << 8)
+#define TX_ESC_CLK_DIVIDSION(div)	(((div) & 0xff) << 0)
+
+#define DSI_DPI_VCID			0x0c
+#define DPI_VID(vid)			(((vid) & 0x3) << 0)
+
+#define DSI_DPI_COLOR_CODING		0x10
+#define EN18_LOOSELY			BIT(8)
+#define DPI_COLOR_CODING_16BIT_1	0x0
+#define DPI_COLOR_CODING_16BIT_2	0x1
+#define DPI_COLOR_CODING_16BIT_3	0x2
+#define DPI_COLOR_CODING_18BIT_1	0x3
+#define DPI_COLOR_CODING_18BIT_2	0x4
+#define DPI_COLOR_CODING_24BIT		0x5
+
+#define DSI_DPI_CFG_POL			0x14
+#define COLORM_ACTIVE_LOW		BIT(4)
+#define SHUTD_ACTIVE_LOW		BIT(3)
+#define HSYNC_ACTIVE_LOW		BIT(2)
+#define VSYNC_ACTIVE_LOW		BIT(1)
+#define DATAEN_ACTIVE_LOW		BIT(0)
+
+#define DSI_DPI_LP_CMD_TIM		0x18
+#define OUTVACT_LPCMD_TIME(p)		(((p) & 0xff) << 16)
+#define INVACT_LPCMD_TIME(p)		((p) & 0xff)
+
+#define DSI_DBI_CFG			0x20
+#define DSI_DBI_CMDSIZE			0x28
+
+#define DSI_PCKHDL_CFG			0x2c
+#define EN_CRC_RX			BIT(4)
+#define EN_ECC_RX			BIT(3)
+#define EN_BTA				BIT(2)
+#define EN_EOTP_RX			BIT(1)
+#define EN_EOTP_TX			BIT(0)
+
+#define DSI_MODE_CFG			0x34
+#define ENABLE_VIDEO_MODE		0
+#define ENABLE_CMD_MODE			BIT(0)
+
+#define DSI_VID_MODE_CFG		0x38
+#define FRAME_BTA_ACK			BIT(14)
+#define ENABLE_LOW_POWER		(0x3f << 8)
+#define ENABLE_LOW_POWER_MASK		(0x3f << 8)
+#define VID_MODE_TYPE_BURST_SYNC_PULSES		0x2
+#define VID_MODE_TYPE_MASK			0x3
+
+#define DSI_VID_PKT_SIZE		0x3c
+#define VID_PKT_SIZE(p)			(((p) & 0x3fff) << 0)
+#define VID_PKT_MAX_SIZE		0x3fff
+
+#define DSI_VID_HSA_TIME		0x48
+#define DSI_VID_HBP_TIME		0x4c
+#define DSI_VID_HLINE_TIME		0x50
+#define DSI_VID_VSA_LINES		0x54
+#define DSI_VID_VBP_LINES		0x58
+#define DSI_VID_VFP_LINES		0x5c
+#define DSI_VID_VACTIVE_LINES		0x60
+#define DSI_CMD_MODE_CFG		0x68
+#define MAX_RD_PKT_SIZE_LP		BIT(24)
+#define DCS_LW_TX_LP			BIT(19)
+#define DCS_SR_0P_TX_LP			BIT(18)
+#define DCS_SW_1P_TX_LP			BIT(17)
+#define DCS_SW_0P_TX_LP			BIT(16)
+#define GEN_LW_TX_LP			BIT(14)
+#define GEN_SR_2P_TX_LP			BIT(13)
+#define GEN_SR_1P_TX_LP			BIT(12)
+#define GEN_SR_0P_TX_LP			BIT(11)
+#define GEN_SW_2P_TX_LP			BIT(10)
+#define GEN_SW_1P_TX_LP			BIT(9)
+#define GEN_SW_0P_TX_LP			BIT(8)
+#define EN_ACK_RQST			BIT(1)
+#define EN_TEAR_FX			BIT(0)
+
+#define CMD_MODE_ALL_LP			(MAX_RD_PKT_SIZE_LP | \
+					 DCS_LW_TX_LP | \
+					 DCS_SR_0P_TX_LP | \
+					 DCS_SW_1P_TX_LP | \
+					 DCS_SW_0P_TX_LP | \
+					 GEN_LW_TX_LP | \
+					 GEN_SR_2P_TX_LP | \
+					 GEN_SR_1P_TX_LP | \
+					 GEN_SR_0P_TX_LP | \
+					 GEN_SW_2P_TX_LP | \
+					 GEN_SW_1P_TX_LP | \
+					 GEN_SW_0P_TX_LP)
+
+#define DSI_GEN_HDR			0x6c
+#define GEN_HDATA(data)			(((data) & 0xffff) << 8)
+#define GEN_HDATA_MASK			(0xffff << 8)
+#define GEN_HTYPE(type)			(((type) & 0xff) << 0)
+#define GEN_HTYPE_MASK			0xff
+
+#define DSI_GEN_PLD_DATA		0x70
+
+#define DSI_CMD_PKT_STATUS		0x74
+#define GEN_CMD_EMPTY			BIT(0)
+#define GEN_CMD_FULL			BIT(1)
+#define GEN_PLD_W_EMPTY			BIT(2)
+#define GEN_PLD_W_FULL			BIT(3)
+#define GEN_PLD_R_EMPTY			BIT(4)
+#define GEN_PLD_R_FULL			BIT(5)
+#define GEN_RD_CMD_BUSY			BIT(6)
+
+#define DSI_TO_CNT_CFG			0x78
+#define HSTX_TO_CNT(p)			(((p) & 0xffff) << 16)
+#define LPRX_TO_CNT(p)			((p) & 0xffff)
+
+#define DSI_BTA_TO_CNT			0x8c
+
+#define DSI_LPCLK_CTRL			0x94
+#define AUTO_CLKLANE_CTRL		BIT(1)
+#define PHY_TXREQUESTCLKHS		BIT(0)
+
+#define DSI_PHY_TMR_LPCLK_CFG		0x98
+#define PHY_CLKHS2LP_TIME(lbcc)		(((lbcc) & 0x3ff) << 16)
+#define PHY_CLKLP2HS_TIME(lbcc)		((lbcc) & 0x3ff)
+
+#define DSI_PHY_TMR_CFG			0x9c
+#define PHY_HS2LP_TIME(lbcc)		(((lbcc) & 0xff) << 24)
+#define PHY_LP2HS_TIME(lbcc)		(((lbcc) & 0xff) << 16)
+#define MAX_RD_TIME(lbcc)		((lbcc) & 0x7fff)
+
+#define DSI_PHY_RSTZ			0xa0
+#define PHY_DISFORCEPLL			0
+#define PHY_ENFORCEPLL			BIT(3)
+#define PHY_DISABLECLK			0
+#define PHY_ENABLECLK			BIT(2)
+#define PHY_RSTZ			0
+#define PHY_UNRSTZ			BIT(1)
+#define PHY_SHUTDOWNZ			0
+#define PHY_UNSHUTDOWNZ			BIT(0)
+
+#define DSI_PHY_IF_CFG			0xa4
+#define N_LANES(n)			((((n) - 1) & 0x3) << 0)
+#define PHY_STOP_WAIT_TIME(cycle)	(((cycle) & 0xff) << 8)
+
+#define DSI_PHY_STATUS			0xb0
+#define LOCK				BIT(0)
+#define STOP_STATE_CLK_LANE		BIT(2)
+
+#define DSI_PHY_TST_CTRL0		0xb4
+#define PHY_TESTCLK			BIT(1)
+#define PHY_UNTESTCLK			0
+#define PHY_TESTCLR			BIT(0)
+#define PHY_UNTESTCLR			0
+
+#define DSI_PHY_TST_CTRL1		0xb8
+#define PHY_TESTEN			BIT(16)
+#define PHY_UNTESTEN			0
+#define PHY_TESTDOUT(n)			(((n) & 0xff) << 8)
+#define PHY_TESTDIN(n)			(((n) & 0xff) << 0)
+
+#define DSI_INT_ST0			0xbc
+#define DSI_INT_ST1			0xc0
+#define DSI_INT_MSK0			0xc4
+#define DSI_INT_MSK1			0xc8
+
+#define PHY_STATUS_TIMEOUT_US		10000
+#define CMD_PKT_STATUS_TIMEOUT_US	20000
+
+#define BYPASS_VCO_RANGE	BIT(7)
+#define VCO_RANGE_CON_SEL(val)	(((val) & 0x7) << 3)
+#define VCO_IN_CAP_CON_DEFAULT	(0x0 << 1)
+#define VCO_IN_CAP_CON_LOW	(0x1 << 1)
+#define VCO_IN_CAP_CON_HIGH	(0x2 << 1)
+#define REF_BIAS_CUR_SEL	BIT(0)
+
+#define CP_CURRENT_3MA		BIT(3)
+#define CP_PROGRAM_EN		BIT(7)
+#define LPF_PROGRAM_EN		BIT(6)
+#define LPF_RESISTORS_20_KOHM	0
+
+#define HSFREQRANGE_SEL(val)	(((val) & 0x3f) << 1)
+
+#define INPUT_DIVIDER(val)	((val - 1) & 0x7f)
+#define LOW_PROGRAM_EN		0
+#define HIGH_PROGRAM_EN		BIT(7)
+#define LOOP_DIV_LOW_SEL(val)	((val - 1) & 0x1f)
+#define LOOP_DIV_HIGH_SEL(val)	(((val - 1) >> 5) & 0x1f)
+#define PLL_LOOP_DIV_EN		BIT(5)
+#define PLL_INPUT_DIV_EN	BIT(4)
+
+#define POWER_CONTROL		BIT(6)
+#define INTERNAL_REG_CURRENT	BIT(3)
+#define BIAS_BLOCK_ON		BIT(2)
+#define BANDGAP_ON		BIT(0)
+
+#define TER_RESISTOR_HIGH	BIT(7)
+#define	TER_RESISTOR_LOW	0
+#define LEVEL_SHIFTERS_ON	BIT(6)
+#define TER_CAL_DONE		BIT(5)
+#define SETRD_MAX		(0x7 << 2)
+#define POWER_MANAGE		BIT(1)
+#define TER_RESISTORS_ON	BIT(0)
+
+#define BIASEXTR_SEL(val)	((val) & 0x7)
+#define BANDGAP_SEL(val)	((val) & 0x7)
+#define TLP_PROGRAM_EN		BIT(7)
+#define THS_PRE_PROGRAM_EN	BIT(7)
+#define THS_ZERO_PROGRAM_EN	BIT(6)
+
+enum {
+	BANDGAP_97_07,
+	BANDGAP_98_05,
+	BANDGAP_99_02,
+	BANDGAP_100_00,
+	BANDGAP_93_17,
+	BANDGAP_94_15,
+	BANDGAP_95_12,
+	BANDGAP_96_10,
+};
+
+enum {
+	BIASEXTR_87_1,
+	BIASEXTR_91_5,
+	BIASEXTR_95_9,
+	BIASEXTR_100,
+	BIASEXTR_105_94,
+	BIASEXTR_111_88,
+	BIASEXTR_118_8,
+	BIASEXTR_127_7,
+};
+
+struct dw_mipi_dsi_plat_data {
+	unsigned int max_data_lanes;
+	enum drm_mode_status (*mode_valid)(struct drm_connector *connector,
+					   struct drm_display_mode *mode);
+};
+
+struct dw_mipi_dsi {
+	struct drm_encoder encoder;
+	struct drm_connector connector;
+	struct mipi_dsi_host dsi_host;
+	struct drm_panel *panel;
+	struct device *dev;
+	struct regmap *grf_regmap;
+	void __iomem *base;
+
+	struct clk *pllref_clk;
+	struct clk *pclk;
+
+	unsigned int lane_mbps; /* per lane */
+	u32 channel;
+	u32 lanes;
+	u32 format;
+	u16 input_div;
+	u16 feedback_div;
+	struct drm_display_mode *mode;
+
+	const struct dw_mipi_dsi_plat_data *pdata;
+
+};
+
+enum dw_mipi_dsi_mode {
+	DW_MIPI_DSI_CMD_MODE,
+	DW_MIPI_DSI_VID_MODE,
+};
+
+struct dphy_pll_testdin_map {
+	unsigned int max_mbps;
+	u8 testdin;
+};
+
+/* The table is based on 27MHz DPHY pll reference clock. */
+static const struct dphy_pll_testdin_map dptdin_map[] = {
+	{  90, 0x00}, { 100, 0x10}, { 110, 0x20}, { 130, 0x01},
+	{ 140, 0x11}, { 150, 0x21}, { 170, 0x02}, { 180, 0x12},
+	{ 200, 0x22}, { 220, 0x03}, { 240, 0x13}, { 250, 0x23},
+	{ 270, 0x04}, { 300, 0x14}, { 330, 0x05}, { 360, 0x15},
+	{ 400, 0x25}, { 450, 0x06}, { 500, 0x16}, { 550, 0x07},
+	{ 600, 0x17}, { 650, 0x08}, { 700, 0x18}, { 750, 0x09},
+	{ 800, 0x19}, { 850, 0x29}, { 900, 0x39}, { 950, 0x0a},
+	{1000, 0x1a}, {1050, 0x2a}, {1100, 0x3a}, {1150, 0x0b},
+	{1200, 0x1b}, {1250, 0x2b}, {1300, 0x3b}, {1350, 0x0c},
+	{1400, 0x1c}, {1450, 0x2c}, {1500, 0x3c}
+};
+
+static int max_mbps_to_testdin(unsigned int max_mbps)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(dptdin_map); i++)
+		if (dptdin_map[i].max_mbps > max_mbps)
+			return dptdin_map[i].testdin;
+
+	return -EINVAL;
+}
+
+/*
+ * The controller should generate 2 frames before
+ * preparing the peripheral.
+ */
+static void dw_mipi_dsi_wait_for_two_frames(struct dw_mipi_dsi *dsi)
+{
+	int refresh, two_frames;
+
+	refresh = drm_mode_vrefresh(dsi->mode);
+	two_frames = DIV_ROUND_UP(MSEC_PER_SEC, refresh) * 2;
+	msleep(two_frames);
+}
+
+static inline struct dw_mipi_dsi *host_to_dsi(struct mipi_dsi_host *host)
+{
+	return container_of(host, struct dw_mipi_dsi, dsi_host);
+}
+
+static inline struct dw_mipi_dsi *con_to_dsi(struct drm_connector *con)
+{
+	return container_of(con, struct dw_mipi_dsi, connector);
+}
+
+static inline struct dw_mipi_dsi *encoder_to_dsi(struct drm_encoder *encoder)
+{
+	return container_of(encoder, struct dw_mipi_dsi, encoder);
+}
+static inline void dsi_write(struct dw_mipi_dsi *dsi, u32 reg, u32 val)
+{
+	writel(val, dsi->base + reg);
+}
+
+static inline u32 dsi_read(struct dw_mipi_dsi *dsi, u32 reg)
+{
+	return readl(dsi->base + reg);
+}
+
+static void dw_mipi_dsi_phy_write(struct dw_mipi_dsi *dsi, u8 test_code,
+				 u8 test_data)
+{
+	/*
+	 * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content
+	 * is latched internally as the current test code. Test data is
+	 * programmed internally by rising edge on TESTCLK.
+	 */
+	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR);
+
+	dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_TESTEN | PHY_TESTDOUT(0) |
+					  PHY_TESTDIN(test_code));
+
+	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLK | PHY_UNTESTCLR);
+
+	dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_UNTESTEN | PHY_TESTDOUT(0) |
+					  PHY_TESTDIN(test_data));
+
+	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR);
+}
+
+static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi)
+{
+	int ret, testdin, vco, val;
+
+	vco = (dsi->lane_mbps < 200) ? 0 : (dsi->lane_mbps + 100) / 200;
+
+	testdin = max_mbps_to_testdin(dsi->lane_mbps);
+	if (testdin < 0) {
+		dev_err(dsi->dev,
+			"failed to get testdin for %dmbps lane clock\n",
+			dsi->lane_mbps);
+		return testdin;
+	}
+
+	dsi_write(dsi, DSI_PWR_UP, POWERUP);
+
+	dw_mipi_dsi_phy_write(dsi, 0x10, BYPASS_VCO_RANGE |
+					 VCO_RANGE_CON_SEL(vco) |
+					 VCO_IN_CAP_CON_LOW |
+					 REF_BIAS_CUR_SEL);
+
+	dw_mipi_dsi_phy_write(dsi, 0x11, CP_CURRENT_3MA);
+	dw_mipi_dsi_phy_write(dsi, 0x12, CP_PROGRAM_EN | LPF_PROGRAM_EN |
+					 LPF_RESISTORS_20_KOHM);
+
+	dw_mipi_dsi_phy_write(dsi, 0x44, HSFREQRANGE_SEL(testdin));
+
+	dw_mipi_dsi_phy_write(dsi, 0x19, PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN);
+	dw_mipi_dsi_phy_write(dsi, 0x17, INPUT_DIVIDER(dsi->input_div));
+	dw_mipi_dsi_phy_write(dsi, 0x18, LOOP_DIV_LOW_SEL(dsi->feedback_div) |
+					 LOW_PROGRAM_EN);
+	dw_mipi_dsi_phy_write(dsi, 0x18, LOOP_DIV_HIGH_SEL(dsi->feedback_div) |
+					 HIGH_PROGRAM_EN);
+
+	dw_mipi_dsi_phy_write(dsi, 0x20, POWER_CONTROL | INTERNAL_REG_CURRENT |
+					 BIAS_BLOCK_ON | BANDGAP_ON);
+
+	dw_mipi_dsi_phy_write(dsi, 0x21, TER_RESISTOR_LOW | TER_CAL_DONE |
+					 SETRD_MAX | TER_RESISTORS_ON);
+	dw_mipi_dsi_phy_write(dsi, 0x21, TER_RESISTOR_HIGH | LEVEL_SHIFTERS_ON |
+					 SETRD_MAX | POWER_MANAGE |
+					 TER_RESISTORS_ON);
+
+	dw_mipi_dsi_phy_write(dsi, 0x22, LOW_PROGRAM_EN |
+					 BIASEXTR_SEL(BIASEXTR_127_7));
+	dw_mipi_dsi_phy_write(dsi, 0x22, HIGH_PROGRAM_EN |
+					 BANDGAP_SEL(BANDGAP_96_10));
+
+	dw_mipi_dsi_phy_write(dsi, 0x70, TLP_PROGRAM_EN | 0xf);
+	dw_mipi_dsi_phy_write(dsi, 0x71, THS_PRE_PROGRAM_EN | 0x55);
+	dw_mipi_dsi_phy_write(dsi, 0x72, THS_ZERO_PROGRAM_EN | 0xa);
+
+	dsi_write(dsi, DSI_PHY_RSTZ, PHY_ENFORCEPLL | PHY_ENABLECLK |
+				     PHY_UNRSTZ | PHY_UNSHUTDOWNZ);
+
+
+	ret = readx_poll_timeout(readl, dsi->base + DSI_PHY_STATUS,
+				 val, val & LOCK, 1000, PHY_STATUS_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dsi->dev, "failed to wait for phy lock state\n");
+		return ret;
+	}
+
+	ret = readx_poll_timeout(readl, dsi->base + DSI_PHY_STATUS,
+				 val, val & STOP_STATE_CLK_LANE, 1000,
+				 PHY_STATUS_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dsi->dev,
+			"failed to wait for phy clk lane stop state\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static int dw_mipi_dsi_get_lane_bps(struct dw_mipi_dsi *dsi)
+{
+	unsigned int bpp, i, pre;
+	unsigned long mpclk, pllref, tmp;
+	unsigned int m = 1, n = 1, target_mbps = 1000;
+	unsigned int max_mbps = dptdin_map[ARRAY_SIZE(dptdin_map) - 1].max_mbps;
+
+	bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
+	if (bpp < 0) {
+		dev_err(dsi->dev, "failed to get bpp for pixel format %d\n",
+			dsi->format);
+		return bpp;
+	}
+
+	mpclk = DIV_ROUND_UP(dsi->mode->clock, MSEC_PER_SEC);
+	if (mpclk) {
+		/* take 1 / 0.9, since mbps must big than bandwidth of RGB */
+		tmp = mpclk * (bpp / dsi->lanes) * 10 / 9;
+		if (tmp < max_mbps)
+			target_mbps = tmp;
+		else
+			dev_err(dsi->dev, "DPHY clock frequency is out of range\n");
+	}
+
+	pllref = DIV_ROUND_UP(clk_get_rate(dsi->pllref_clk), USEC_PER_SEC);
+	tmp = pllref;
+
+	for (i = 1; i < 6; i++) {
+		pre = pllref / i;
+		if ((tmp > (target_mbps % pre)) && (target_mbps / pre < 512)) {
+			tmp = target_mbps % pre;
+			n = i;
+			m = target_mbps / pre;
+		}
+		if (tmp == 0)
+			break;
+	}
+
+	dsi->lane_mbps = pllref / n * m;
+	dsi->input_div = n;
+	dsi->feedback_div = m;
+
+	return 0;
+}
+
+static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host,
+				   struct mipi_dsi_device *device)
+{
+	struct dw_mipi_dsi *dsi = host_to_dsi(host);
+
+	if (device->lanes > dsi->pdata->max_data_lanes) {
+		dev_err(dsi->dev, "the number of data lanes(%u) is too many\n",
+				device->lanes);
+		return -EINVAL;
+	}
+
+	if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) ||
+	    !(device->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)) {
+		dev_err(dsi->dev, "device mode is unsupported\n");
+		return -EINVAL;
+	}
+
+	dsi->lanes = device->lanes;
+	dsi->channel = device->channel;
+	dsi->format = device->format;
+	dsi->panel = of_drm_find_panel(device->dev.of_node);
+	if (dsi->panel)
+		return drm_panel_attach(dsi->panel, &dsi->connector);
+
+	return -EINVAL;
+}
+
+static int dw_mipi_dsi_host_detach(struct mipi_dsi_host *host,
+				   struct mipi_dsi_device *device)
+{
+	struct dw_mipi_dsi *dsi = host_to_dsi(host);
+
+	drm_panel_detach(dsi->panel);
+
+	return 0;
+}
+
+static int dw_mipi_dsi_gen_pkt_hdr_write(struct dw_mipi_dsi *dsi, u32 val)
+{
+	int ret;
+
+	ret = readx_poll_timeout(readl, dsi->base + DSI_CMD_PKT_STATUS,
+				 val, !(val & GEN_CMD_FULL), 1000,
+				 CMD_PKT_STATUS_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dsi->dev, "failed to get available command FIFO\n");
+		return ret;
+	}
+
+	dsi_write(dsi, DSI_GEN_HDR, val);
+
+	ret = readx_poll_timeout(readl, dsi->base + DSI_CMD_PKT_STATUS,
+				 val, val & (GEN_CMD_EMPTY | GEN_PLD_W_EMPTY),
+				 1000, CMD_PKT_STATUS_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dsi->dev, "failed to write command FIFO\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int dw_mipi_dsi_dcs_short_write(struct dw_mipi_dsi *dsi,
+				       const struct mipi_dsi_msg *msg)
+{
+	const u16 *tx_buf = msg->tx_buf;
+	u32 val = GEN_HDATA(*tx_buf) | GEN_HTYPE(msg->type);
+
+	if (msg->tx_len > 2) {
+		dev_err(dsi->dev, "too long tx buf length %zu for short write\n",
+			msg->tx_len);
+		return -EINVAL;
+	}
+
+	return dw_mipi_dsi_gen_pkt_hdr_write(dsi, val);
+}
+
+static int dw_mipi_dsi_dcs_long_write(struct dw_mipi_dsi *dsi,
+				      const struct mipi_dsi_msg *msg)
+{
+	const u32 *tx_buf = msg->tx_buf;
+	int len = msg->tx_len, pld_data_bytes = sizeof(*tx_buf), ret;
+	u32 val = GEN_HDATA(msg->tx_len) | GEN_HTYPE(msg->type);
+	u32 remainder = 0;
+
+	if (msg->tx_len < 3) {
+		dev_err(dsi->dev, "wrong tx buf length %zu for long write\n",
+			msg->tx_len);
+		return -EINVAL;
+	}
+
+	while (DIV_ROUND_UP(len, pld_data_bytes)) {
+		if (len < pld_data_bytes) {
+			memcpy(&remainder, tx_buf, len);
+			dsi_write(dsi, DSI_GEN_PLD_DATA, remainder);
+			len = 0;
+		} else {
+			dsi_write(dsi, DSI_GEN_PLD_DATA, *tx_buf);
+			tx_buf++;
+			len -= pld_data_bytes;
+		}
+
+		ret = readx_poll_timeout(readl, dsi->base + DSI_CMD_PKT_STATUS,
+					 val, !(val & GEN_PLD_W_FULL), 1000,
+					 CMD_PKT_STATUS_TIMEOUT_US);
+		if (ret < 0) {
+			dev_err(dsi->dev,
+				"failed to get available write payload FIFO\n");
+			return ret;
+		}
+	}
+
+	return dw_mipi_dsi_gen_pkt_hdr_write(dsi, val);
+}
+
+static ssize_t dw_mipi_dsi_host_transfer(struct mipi_dsi_host *host,
+					 const struct mipi_dsi_msg *msg)
+{
+	struct dw_mipi_dsi *dsi = host_to_dsi(host);
+	int ret;
+
+	switch (msg->type) {
+	case MIPI_DSI_DCS_SHORT_WRITE:
+	case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
+	case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
+		ret = dw_mipi_dsi_dcs_short_write(dsi, msg);
+		break;
+	case MIPI_DSI_DCS_LONG_WRITE:
+		ret = dw_mipi_dsi_dcs_long_write(dsi, msg);
+		break;
+	default:
+		dev_err(dsi->dev, "unsupported message type\n");
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static const struct mipi_dsi_host_ops dw_mipi_dsi_host_ops = {
+	.attach = dw_mipi_dsi_host_attach,
+	.detach = dw_mipi_dsi_host_detach,
+	.transfer = dw_mipi_dsi_host_transfer,
+};
+
+static void dw_mipi_dsi_video_mode_config(struct dw_mipi_dsi *dsi)
+{
+	u32 val;
+
+	val = VID_MODE_TYPE_BURST_SYNC_PULSES | ENABLE_LOW_POWER;
+
+	dsi_write(dsi, DSI_VID_MODE_CFG, val);
+}
+
+static void dw_mipi_dsi_set_mode(struct dw_mipi_dsi *dsi,
+				 enum dw_mipi_dsi_mode mode)
+{
+	if (mode == DW_MIPI_DSI_CMD_MODE) {
+		dsi_write(dsi, DSI_PWR_UP, RESET);
+		dsi_write(dsi, DSI_MODE_CFG, ENABLE_CMD_MODE);
+		dsi_write(dsi, DSI_PWR_UP, POWERUP);
+	} else {
+		dsi_write(dsi, DSI_PWR_UP, RESET);
+		dsi_write(dsi, DSI_MODE_CFG, ENABLE_VIDEO_MODE);
+		dw_mipi_dsi_video_mode_config(dsi);
+		dsi_write(dsi, DSI_PWR_UP, POWERUP);
+	}
+}
+
+static void dw_mipi_dsi_disable(struct dw_mipi_dsi *dsi)
+{
+	dsi_write(dsi, DSI_PWR_UP, RESET);
+	dsi_write(dsi, DSI_PHY_RSTZ, PHY_RSTZ);
+}
+
+static void dw_mipi_dsi_init(struct dw_mipi_dsi *dsi)
+{
+	dsi_write(dsi, DSI_PWR_UP, RESET);
+	dsi_write(dsi, DSI_PHY_RSTZ, PHY_DISFORCEPLL | PHY_DISABLECLK
+		  | PHY_RSTZ | PHY_SHUTDOWNZ);
+	dsi_write(dsi, DSI_CLKMGR_CFG, TO_CLK_DIVIDSION(10) |
+		  TX_ESC_CLK_DIVIDSION(7));
+	dsi_write(dsi, DSI_LPCLK_CTRL, PHY_TXREQUESTCLKHS);
+}
+
+static void dw_mipi_dsi_dpi_config(struct dw_mipi_dsi *dsi,
+				   struct drm_display_mode *mode)
+{
+	u32 val = 0, color = 0;
+
+	switch (dsi->format) {
+	case MIPI_DSI_FMT_RGB888:
+		color = DPI_COLOR_CODING_24BIT;
+		break;
+	case MIPI_DSI_FMT_RGB666:
+		color = DPI_COLOR_CODING_18BIT_2 | EN18_LOOSELY;
+		break;
+	case MIPI_DSI_FMT_RGB666_PACKED:
+		color = DPI_COLOR_CODING_18BIT_1;
+		break;
+	case MIPI_DSI_FMT_RGB565:
+		color = DPI_COLOR_CODING_16BIT_1;
+		break;
+	}
+
+	if (!(mode->flags & DRM_MODE_FLAG_PVSYNC))
+		val |= VSYNC_ACTIVE_LOW;
+	if (!(mode->flags & DRM_MODE_FLAG_PHSYNC))
+		val |= HSYNC_ACTIVE_LOW;
+
+	dsi_write(dsi, DSI_DPI_VCID, DPI_VID(dsi->channel));
+	dsi_write(dsi, DSI_DPI_COLOR_CODING, color);
+	dsi_write(dsi, DSI_DPI_CFG_POL, val);
+	dsi_write(dsi, DSI_DPI_LP_CMD_TIM, OUTVACT_LPCMD_TIME(4)
+		  | INVACT_LPCMD_TIME(4));
+}
+
+static void dw_mipi_dsi_packet_handler_config(struct dw_mipi_dsi *dsi)
+{
+	dsi_write(dsi, DSI_PCKHDL_CFG, EN_CRC_RX | EN_ECC_RX | EN_BTA);
+}
+
+static void dw_mipi_dsi_video_packet_config(struct dw_mipi_dsi *dsi,
+					    struct drm_display_mode *mode)
+{
+	dsi_write(dsi, DSI_VID_PKT_SIZE, VID_PKT_SIZE(mode->hdisplay));
+}
+
+static void dw_mipi_dsi_command_mode_config(struct dw_mipi_dsi *dsi)
+{
+	dsi_write(dsi, DSI_TO_CNT_CFG, HSTX_TO_CNT(1000) | LPRX_TO_CNT(1000));
+	dsi_write(dsi, DSI_BTA_TO_CNT, 0xd00);
+	dsi_write(dsi, DSI_CMD_MODE_CFG, CMD_MODE_ALL_LP);
+	dsi_write(dsi, DSI_MODE_CFG, ENABLE_CMD_MODE);
+}
+
+/* Get lane byte clock cycles. */
+static u32 dw_mipi_dsi_get_hcomponent_lbcc(struct dw_mipi_dsi *dsi,
+					   u32 hcomponent)
+{
+	u32 frac, lbcc;
+
+	lbcc = hcomponent * dsi->lane_mbps * MSEC_PER_SEC / 8;
+
+	frac = lbcc % dsi->mode->clock;
+	lbcc = lbcc / dsi->mode->clock;
+	if (frac)
+		lbcc++;
+
+	return lbcc;
+}
+
+static void dw_mipi_dsi_line_timer_config(struct dw_mipi_dsi *dsi)
+{
+	u32 htotal, hsa, hbp, lbcc;
+	struct drm_display_mode *mode = dsi->mode;
+
+	htotal = mode->htotal;
+	hsa = mode->hsync_end - mode->hsync_start;
+	hbp = mode->htotal - mode->hsync_end;
+
+	lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, htotal);
+	dsi_write(dsi, DSI_VID_HLINE_TIME, lbcc);
+
+	lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, hsa);
+	dsi_write(dsi, DSI_VID_HSA_TIME, lbcc);
+
+	lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, hbp);
+	dsi_write(dsi, DSI_VID_HBP_TIME, lbcc);
+}
+
+static void dw_mipi_dsi_vertical_timing_config(struct dw_mipi_dsi *dsi)
+{
+	u32 vactive, vsa, vfp, vbp;
+	struct drm_display_mode *mode = dsi->mode;
+
+	vactive = mode->vdisplay;
+	vsa = mode->vsync_end - mode->vsync_start;
+	vfp = mode->vsync_start - mode->vdisplay;
+	vbp = mode->vtotal - mode->vsync_end;
+
+	dsi_write(dsi, DSI_VID_VACTIVE_LINES, vactive);
+	dsi_write(dsi, DSI_VID_VSA_LINES, vsa);
+	dsi_write(dsi, DSI_VID_VFP_LINES, vfp);
+	dsi_write(dsi, DSI_VID_VBP_LINES, vbp);
+}
+
+static void dw_mipi_dsi_dphy_timing_config(struct dw_mipi_dsi *dsi)
+{
+	dsi_write(dsi, DSI_PHY_TMR_CFG, PHY_HS2LP_TIME(0x40)
+		  | PHY_LP2HS_TIME(0x40) | MAX_RD_TIME(10000));
+
+	dsi_write(dsi, DSI_PHY_TMR_LPCLK_CFG, PHY_CLKHS2LP_TIME(0x40)
+		  | PHY_CLKLP2HS_TIME(0x40));
+}
+
+static void dw_mipi_dsi_dphy_interface_config(struct dw_mipi_dsi *dsi)
+{
+	dsi_write(dsi, DSI_PHY_IF_CFG, PHY_STOP_WAIT_TIME(0x20) |
+		  N_LANES(dsi->lanes));
+}
+
+static void dw_mipi_dsi_clear_err(struct dw_mipi_dsi *dsi)
+{
+	dsi_read(dsi, DSI_INT_ST0);
+	dsi_read(dsi, DSI_INT_ST1);
+	dsi_write(dsi, DSI_INT_MSK0, 0);
+	dsi_write(dsi, DSI_INT_MSK1, 0);
+}
+
+static void dw_mipi_dsi_encoder_mode_set(struct drm_encoder *encoder,
+					struct drm_display_mode *mode,
+					struct drm_display_mode *adjusted_mode)
+{
+	struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
+	int ret;
+
+	dsi->mode = adjusted_mode;
+
+	ret = dw_mipi_dsi_get_lane_bps(dsi);
+	if (ret < 0)
+		return;
+
+	if (clk_prepare_enable(dsi->pclk)) {
+		dev_err(dsi->dev, "%s: Failed to enable pclk\n", __func__);
+		return;
+	}
+
+	dw_mipi_dsi_init(dsi);
+	dw_mipi_dsi_dpi_config(dsi, mode);
+	dw_mipi_dsi_packet_handler_config(dsi);
+	dw_mipi_dsi_video_mode_config(dsi);
+	dw_mipi_dsi_video_packet_config(dsi, mode);
+	dw_mipi_dsi_command_mode_config(dsi);
+	dw_mipi_dsi_line_timer_config(dsi);
+	dw_mipi_dsi_vertical_timing_config(dsi);
+	dw_mipi_dsi_dphy_timing_config(dsi);
+	dw_mipi_dsi_dphy_interface_config(dsi);
+	dw_mipi_dsi_clear_err(dsi);
+	if (drm_panel_prepare(dsi->panel)) {
+		dev_err(dsi->dev, "failed to prepare panel\n");
+		return;
+	}
+	clk_disable_unprepare(dsi->pclk);
+}
+
+static void dw_mipi_dsi_encoder_disable(struct drm_encoder *encoder)
+{
+	struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
+
+	drm_panel_disable(dsi->panel);
+
+	if (clk_prepare_enable(dsi->pclk)) {
+		dev_err(dsi->dev, "%s: Failed to enable pclk\n", __func__);
+		return;
+	}
+
+	dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_CMD_MODE);
+	drm_panel_unprepare(dsi->panel);
+	dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_VID_MODE);
+
+	/*
+	 * This is necessary to make sure the peripheral will be driven
+	 * normally when the display is enabled again later.
+	 */
+	msleep(120);
+
+	dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_CMD_MODE);
+	dw_mipi_dsi_disable(dsi);
+	clk_disable_unprepare(dsi->pclk);
+}
+
+static bool dw_mipi_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
+					const struct drm_display_mode *mode,
+					struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static void dw_mipi_dsi_encoder_commit(struct drm_encoder *encoder)
+{
+	struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
+	int mux  = rockchip_drm_encoder_get_mux_id(dsi->dev->of_node, encoder);
+	u32 interface_pix_fmt;
+	u32 val;
+
+	if (clk_prepare_enable(dsi->pclk)) {
+		dev_err(dsi->dev, "%s: Failed to enable pclk\n", __func__);
+		return;
+	}
+
+	dw_mipi_dsi_phy_init(dsi);
+	dw_mipi_dsi_wait_for_two_frames(dsi);
+
+	dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_VID_MODE);
+	drm_panel_enable(dsi->panel);
+
+	clk_disable_unprepare(dsi->pclk);
+
+	switch (dsi->format) {
+	case MIPI_DSI_FMT_RGB888:
+		interface_pix_fmt = ROCKCHIP_OUT_MODE_P888;
+		break;
+	case MIPI_DSI_FMT_RGB666:
+		interface_pix_fmt = ROCKCHIP_OUT_MODE_P666;
+		break;
+	case MIPI_DSI_FMT_RGB565:
+		interface_pix_fmt = ROCKCHIP_OUT_MODE_P565;
+		break;
+	default:
+		WARN_ON(1);
+		return;
+	}
+
+	rockchip_drm_crtc_mode_config(encoder->crtc, DRM_MODE_CONNECTOR_DSI,
+				      interface_pix_fmt);
+
+	if (mux)
+		val = DSI0_SEL_VOP_LIT | (DSI0_SEL_VOP_LIT << 16);
+	else
+		val = DSI0_SEL_VOP_LIT << 16;
+
+	regmap_write(dsi->grf_regmap, GRF_SOC_CON6, val);
+	dev_dbg(dsi->dev, "vop %s output to dsi0\n", (mux) ? "LIT" : "BIG");
+}
+
+static struct drm_encoder_helper_funcs
+dw_mipi_dsi_encoder_helper_funcs = {
+	.mode_fixup = dw_mipi_dsi_encoder_mode_fixup,
+	.commit = dw_mipi_dsi_encoder_commit,
+	.mode_set = dw_mipi_dsi_encoder_mode_set,
+	.disable = dw_mipi_dsi_encoder_disable,
+};
+
+static struct drm_encoder_funcs dw_mipi_dsi_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static int dw_mipi_dsi_connector_get_modes(struct drm_connector *connector)
+{
+	struct dw_mipi_dsi *dsi = con_to_dsi(connector);
+
+	return drm_panel_get_modes(dsi->panel);
+}
+
+static enum drm_mode_status dw_mipi_dsi_mode_valid(
+					struct drm_connector *connector,
+					struct drm_display_mode *mode)
+{
+	struct dw_mipi_dsi *dsi = con_to_dsi(connector);
+
+	enum drm_mode_status mode_status = MODE_OK;
+
+	if (dsi->pdata->mode_valid)
+		mode_status = dsi->pdata->mode_valid(connector, mode);
+
+	return mode_status;
+}
+
+static struct drm_encoder *dw_mipi_dsi_connector_best_encoder(
+					struct drm_connector *connector)
+{
+	struct dw_mipi_dsi *dsi = con_to_dsi(connector);
+
+	return &dsi->encoder;
+}
+
+static struct drm_connector_helper_funcs dw_mipi_dsi_connector_helper_funcs = {
+	.get_modes = dw_mipi_dsi_connector_get_modes,
+	.mode_valid = dw_mipi_dsi_mode_valid,
+	.best_encoder = dw_mipi_dsi_connector_best_encoder,
+};
+
+static enum drm_connector_status
+dw_mipi_dsi_detect(struct drm_connector *connector, bool force)
+{
+	return connector_status_connected;
+}
+
+static void dw_mipi_dsi_drm_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs dw_mipi_dsi_atomic_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.detect = dw_mipi_dsi_detect,
+	.destroy = dw_mipi_dsi_drm_connector_destroy,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int dw_mipi_dsi_register(struct drm_device *drm,
+				      struct dw_mipi_dsi *dsi)
+{
+	struct drm_encoder *encoder = &dsi->encoder;
+	struct drm_connector *connector = &dsi->connector;
+	struct device *dev = dsi->dev;
+	int ret;
+
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm,
+							     dev->of_node);
+	/*
+	 * If we failed to find the CRTC(s) which this encoder is
+	 * supposed to be connected to, it's because the CRTC has
+	 * not been registered yet.  Defer probing, and hope that
+	 * the required CRTC is added later.
+	 */
+	if (encoder->possible_crtcs == 0)
+		return -EPROBE_DEFER;
+
+	drm_encoder_helper_add(&dsi->encoder,
+			       &dw_mipi_dsi_encoder_helper_funcs);
+	ret = drm_encoder_init(drm, &dsi->encoder, &dw_mipi_dsi_encoder_funcs,
+			 DRM_MODE_ENCODER_DSI, NULL);
+	if (ret) {
+		dev_err(dev, "Failed to initialize encoder with drm\n");
+		return ret;
+	}
+
+	drm_connector_helper_add(connector,
+			&dw_mipi_dsi_connector_helper_funcs);
+
+	drm_connector_init(drm, &dsi->connector,
+			   &dw_mipi_dsi_atomic_connector_funcs,
+			   DRM_MODE_CONNECTOR_DSI);
+
+	drm_mode_connector_attach_encoder(connector, encoder);
+
+	return 0;
+}
+
+static int rockchip_mipi_parse_dt(struct dw_mipi_dsi *dsi)
+{
+	struct device_node *np = dsi->dev->of_node;
+
+	dsi->grf_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+	if (IS_ERR(dsi->grf_regmap)) {
+		dev_err(dsi->dev, "Unable to get rockchip,grf\n");
+		return PTR_ERR(dsi->grf_regmap);
+	}
+
+	return 0;
+}
+
+static enum drm_mode_status rk3288_mipi_dsi_mode_valid(
+					struct drm_connector *connector,
+					struct drm_display_mode *mode)
+{
+	/*
+	 * The VID_PKT_SIZE field in the DSI_VID_PKT_CFG
+	 * register is 11-bit.
+	 */
+	if (mode->hdisplay > 0x7ff)
+		return MODE_BAD_HVALUE;
+
+	/*
+	 * The V_ACTIVE_LINES field in the DSI_VTIMING_CFG
+	 * register is 11-bit.
+	 */
+	if (mode->vdisplay > 0x7ff)
+		return MODE_BAD_VVALUE;
+
+	return MODE_OK;
+}
+
+static struct dw_mipi_dsi_plat_data rk3288_mipi_dsi_drv_data = {
+	.max_data_lanes = 4,
+	.mode_valid = rk3288_mipi_dsi_mode_valid,
+};
+
+static const struct of_device_id dw_mipi_dsi_dt_ids[] = {
+	{
+	 .compatible = "rockchip,rk3288-mipi-dsi",
+	 .data = &rk3288_mipi_dsi_drv_data,
+	},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, dw_mipi_dsi_dt_ids);
+
+static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
+			     void *data)
+{
+	const struct of_device_id *of_id =
+			of_match_device(dw_mipi_dsi_dt_ids, dev);
+	const struct dw_mipi_dsi_plat_data *pdata = of_id->data;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = data;
+	struct dw_mipi_dsi *dsi;
+	struct resource *res;
+	int ret;
+
+	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
+	if (!dsi)
+		return -ENOMEM;
+
+	dsi->dev = dev;
+	dsi->pdata = pdata;
+
+	ret = rockchip_mipi_parse_dt(dsi);
+	if (ret)
+		return ret;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	dsi->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(dsi->base))
+		return PTR_ERR(dsi->base);
+
+	dsi->pllref_clk = devm_clk_get(dev, "ref");
+	if (IS_ERR(dsi->pllref_clk)) {
+		ret = PTR_ERR(dsi->pllref_clk);
+		dev_err(dev, "Unable to get pll reference clock: %d\n", ret);
+		return ret;
+	}
+
+	dsi->pclk = devm_clk_get(dev, "pclk");
+	if (IS_ERR(dsi->pclk)) {
+		ret = PTR_ERR(dsi->pclk);
+		dev_err(dev, "Unable to get pclk: %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(dsi->pllref_clk);
+	if (ret) {
+		dev_err(dev, "%s: Failed to enable pllref_clk\n", __func__);
+		return ret;
+	}
+
+	ret = dw_mipi_dsi_register(drm, dsi);
+	if (ret) {
+		dev_err(dev, "Failed to register mipi_dsi: %d\n", ret);
+		goto err_pllref;
+	}
+
+	dev_set_drvdata(dev, dsi);
+
+	dsi->dsi_host.ops = &dw_mipi_dsi_host_ops;
+	dsi->dsi_host.dev = dev;
+	return mipi_dsi_host_register(&dsi->dsi_host);
+
+err_pllref:
+	clk_disable_unprepare(dsi->pllref_clk);
+	return ret;
+}
+
+static void dw_mipi_dsi_unbind(struct device *dev, struct device *master,
+	void *data)
+{
+	struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
+
+	mipi_dsi_host_unregister(&dsi->dsi_host);
+	clk_disable_unprepare(dsi->pllref_clk);
+}
+
+static const struct component_ops dw_mipi_dsi_ops = {
+	.bind	= dw_mipi_dsi_bind,
+	.unbind	= dw_mipi_dsi_unbind,
+};
+
+static int dw_mipi_dsi_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &dw_mipi_dsi_ops);
+}
+
+static int dw_mipi_dsi_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &dw_mipi_dsi_ops);
+	return 0;
+}
+
+static struct platform_driver dw_mipi_dsi_driver = {
+	.probe		= dw_mipi_dsi_probe,
+	.remove		= dw_mipi_dsi_remove,
+	.driver		= {
+		.of_match_table = dw_mipi_dsi_dt_ids,
+		.name	= DRIVER_NAME,
+	},
+};
+module_platform_driver(dw_mipi_dsi_driver);
+
+MODULE_DESCRIPTION("ROCKCHIP MIPI DSI host controller driver");
+MODULE_AUTHOR("Chris Zhong <zyw at rock-chips.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 056cac3..46c2a8d 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -818,6 +818,9 @@ int rockchip_drm_crtc_mode_config(struct drm_crtc *crtc,
 	case DRM_MODE_CONNECTOR_HDMIA:
 		VOP_CTRL_SET(vop, hdmi_en, 1);
 		break;
+	case DRM_MODE_CONNECTOR_DSI:
+		VOP_CTRL_SET(vop, mipi_en, 1);
+		break;
 	default:
 		DRM_ERROR("unsupport connector_type[%d]\n", connector_type);
 		return -EINVAL;
-- 
2.6.3



From zyw at rock-chips.com  Tue Jan  5 20:16:52 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Wed, 6 Jan 2016 12:16:52 +0800
Subject: [PATCH v6.3 4/6] drm: rockchip: Support Synopsys DW MIPI DSI
In-Reply-To: <568C7258.3010104@rock-chips.com>
References: <1450260616-21160-5-git-send-email-zyw@rock-chips.com>
 <1450842215-10883-1-git-send-email-zyw@rock-chips.com>
 <568C7258.3010104@rock-chips.com>
Message-ID: <568C9534.3050700@rock-chips.com>

Hi Mark


On 01/06/2016 09:48 AM, Mark yao wrote:
> On 2015?12?23? 11:43, Chris Zhong wrote:
>> +static int dw_mipi_dsi_register(struct drm_device *drm,
>> +                      struct dw_mipi_dsi *dsi)
>> +{
>> +    struct drm_encoder *encoder = &dsi->encoder;
>> +    struct drm_connector *connector = &dsi->connector;
>> +    struct device *dev = dsi->dev;
>> +    int ret;
>> +
>> +    encoder->possible_crtcs = drm_of_find_possible_crtcs(drm,
>> +                                 dev->of_node);
>> +    /*
>> +     * If we failed to find the CRTC(s) which this encoder is
>> +     * supposed to be connected to, it's because the CRTC has
>> +     * not been registered yet.  Defer probing, and hope that
>> +     * the required CRTC is added later.
>> +     */
>> +    if (encoder->possible_crtcs == 0)
>> +        return -EPROBE_DEFER;
>> +
>> +    drm_encoder_helper_add(&dsi->encoder,
>> +                   &dw_mipi_dsi_encoder_helper_funcs);
>> +    ret = drm_encoder_init(drm, &dsi->encoder, 
>> &dw_mipi_dsi_encoder_funcs,
>> +             DRM_MODE_ENCODER_DSI);
>> +    if (ret) {
>> +        dev_err(dev, "Failed to initialize encoder with drm\n");
>> +        return ret;
>> +    }
>> +
>> +    drm_connector_helper_add(connector,
>> +            &dw_mipi_dsi_connector_helper_funcs);
>> +
>> +    drm_connector_init(drm, &dsi->connector,
>> +               &dw_mipi_dsi_atomic_connector_funcs,
>> +               DRM_MODE_CONNECTOR_DSI);
>> +
> Hi Chris
>
> I try to pick your patches, but found following issues:
>
> drivers/gpu/drm/rockchip/dw-mipi-dsi.c: ????dw_mipi_dsi_register??:
> drivers/gpu/drm/rockchip/dw-mipi-dsi.c:1024:5: ??? ????? 
> ?drm_encoder_init??????
> include/drm/drm_crtc.h:2220:5: ??? ????
> make[4]: *** [drivers/gpu/drm/rockchip/dw-mipi-dsi.o] ?? 1
>
> Can you rebase to newest Dave's drm-next and fix this problem?
Thanks Mark.
I have post the v7 serial, fix this this compile err and modify the 
config to tristate for modules build
>
> Thanks.
>



From zyw at rock-chips.com  Tue Jan  5 20:03:57 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Wed,  6 Jan 2016 12:03:57 +0800
Subject: [PATCH v7 5/5] ARM: dts: rockchip: add support mipi panel tv080wum-nl0
In-Reply-To: <1452053038-32098-1-git-send-email-zyw@rock-chips.com>
References: <1452053038-32098-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1452053038-32098-6-git-send-email-zyw@rock-chips.com>

This tv080wum-nl0 panel is a mipi panel, it can use in MIPI_TX socket
of rk3288 evb board.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>

---

Changes in v7:
- Move the lcd_en control to act8846 dts

Changes in v6: None
Changes in v5:
- add a blank line befor lcd_en

Changes in v4: None
Changes in v3: None

 arch/arm/boot/dts/rk3288-evb-act8846.dts |  2 ++
 arch/arm/boot/dts/rk3288-evb.dtsi        | 12 +++++++++++-
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/rk3288-evb-act8846.dts b/arch/arm/boot/dts/rk3288-evb-act8846.dts
index e662147..7d5475b 100644
--- a/arch/arm/boot/dts/rk3288-evb-act8846.dts
+++ b/arch/arm/boot/dts/rk3288-evb-act8846.dts
@@ -50,6 +50,8 @@
 		gpio = <&gpio7 3 GPIO_ACTIVE_HIGH>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&lcd_en>;
+		regulator-always-on;
+		regulator-boot-on;
 		regulator-name = "vcc_lcd";
 		vin-supply = <&vcc_io>;
 	};
diff --git a/arch/arm/boot/dts/rk3288-evb.dtsi b/arch/arm/boot/dts/rk3288-evb.dtsi
index 4faabdb..0c91858 100644
--- a/arch/arm/boot/dts/rk3288-evb.dtsi
+++ b/arch/arm/boot/dts/rk3288-evb.dtsi
@@ -47,7 +47,7 @@
 		reg = <0x0 0x80000000>;
 	};
 
-	backlight {
+	backlight: backlight {
 		compatible = "pwm-backlight";
 		brightness-levels = <
 			  0   1   2   3   4   5   6   7
@@ -183,6 +183,16 @@
 	status = "okay";
 };
 
+&mipi_dsi {
+	status = "okay";
+	panel {
+		compatible ="boe,tv080wum-nl0";
+		reg = <0>;
+		backlight = <&backlight>;
+		status = "okay";
+	};
+};
+
 &sdmmc {
 	bus-width = <4>;
 	cap-mmc-highspeed;
-- 
2.6.3



From zyw at rock-chips.com  Tue Jan  5 20:03:56 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Wed,  6 Jan 2016 12:03:56 +0800
Subject: [PATCH v7 4/5] ARM: dts: rockchip: add rk3288 mipi_dsi nodes
In-Reply-To: <1452053038-32098-1-git-send-email-zyw@rock-chips.com>
References: <1452053038-32098-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1452053038-32098-5-git-send-email-zyw@rock-chips.com>

Add a mipi_dsi node, and also add mipi_dsi endpoints to vopb and vopl
output port nodes.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>

---

Changes in v7: None
Changes in v6: None
Changes in v5:
- modify the clk name to SCLK_MIPIDSI_24M

Changes in v4: None
Changes in v3: None

 arch/arm/boot/dts/rk3288.dtsi | 39 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
index 04ea209..6b21a87 100644
--- a/arch/arm/boot/dts/rk3288.dtsi
+++ b/arch/arm/boot/dts/rk3288.dtsi
@@ -800,6 +800,10 @@
 				reg = <0>;
 				remote-endpoint = <&hdmi_in_vopb>;
 			};
+			vopb_out_mipi: endpoint at 2 {
+				reg = <2>;
+				remote-endpoint = <&mipi_in_vopb>;
+			};
 		};
 	};
 
@@ -833,6 +837,10 @@
 				reg = <0>;
 				remote-endpoint = <&hdmi_in_vopl>;
 			};
+			vopl_out_mipi: endpoint at 2 {
+				reg = <2>;
+				remote-endpoint = <&mipi_in_vopl>;
+			};
 		};
 	};
 
@@ -873,6 +881,37 @@
 		};
 	};
 
+	mipi_dsi: mipi at ff960000 {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		compatible = "rockchip,rk3288-mipi-dsi", "snps,dw-mipi-dsi";
+		reg = <0xff960000 0x4000>;
+		interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru SCLK_MIPIDSI_24M>, <&cru PCLK_MIPI_DSI0>;
+		clock-names = "ref", "pclk";
+		rockchip,grf = <&grf>;
+		status = "disabled";
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <1>;
+
+			mipi_in: port {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				mipi_in_vopb: endpoint at 0 {
+					reg = <0>;
+					remote-endpoint = <&vopb_out_mipi>;
+				};
+				mipi_in_vopl: endpoint at 1 {
+					reg = <1>;
+					remote-endpoint = <&vopl_out_mipi>;
+				};
+			};
+		};
+	};
+
 	gic: interrupt-controller at ffc01000 {
 		compatible = "arm,gic-400";
 		interrupt-controller;
-- 
2.6.3



From ykk at rock-chips.com  Tue Jan  5 22:13:26 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Wed, 6 Jan 2016 14:13:26 +0800
Subject: [PATCH v2 2/3] drm/rockchip: vop: add rk3229 vop support
In-Reply-To: <568C6650.5040708@rock-chips.com>
References: <1451965688-12286-1-git-send-email-ykk@rock-chips.com>
 <1451966307-12642-1-git-send-email-ykk@rock-chips.com>
 <568C6650.5040708@rock-chips.com>
Message-ID: <568CB086.3000609@rock-chips.com>

Hi Mark,

On 01/06/2016 08:56 AM, Mark yao wrote:
> On 2016?01?05? 11:58, Yakir Yang wrote:
>> RK3229 registers layout is simalar to RK3288 layout, only the
>> interruput registers is different to RK3288.
>>
>> RK3229 support two overlay plane and one hwc plane, max output
>> resolution is 4K. it support IOMMU, and its IOMMU same as rk3288's.
>>
>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
>> ---
>> Changes in v2:
>> - Separate the write-mask changes out, and remove the DUMMY_INTR 
>> marcos (Heiko)
>>
>>   drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 110 
>> ++++++++++++++++++++++++++++
>>   drivers/gpu/drm/rockchip/rockchip_vop_reg.h |  90 
>> +++++++++++++++++++++++
>>   2 files changed, 200 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c 
>> b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>> index 7fbaf76..f1358f9 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>> @@ -191,6 +191,114 @@ static const struct vop_data rk3288_vop = {
>>       .win_size = ARRAY_SIZE(rk3288_vop_win_data),
>>   };
>>   +static const struct vop_scl_extension rk3229_win_full_scl_ext = {
>> +    .cbcr_vsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 31),
>> +    .cbcr_vsu_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 30),
>> +    .cbcr_hsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 28),
>> +    .cbcr_ver_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 26),
>> +    .cbcr_hor_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 24),
>> +    .yrgb_vsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 23),
>> +    .yrgb_vsu_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 22),
>> +    .yrgb_hsd_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 20),
>> +    .yrgb_ver_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 18),
>> +    .yrgb_hor_scl_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 16),
>> +    .line_load_mode = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 15),
>> +    .cbcr_axi_gather_num = VOP_REG(RK3229_WIN0_CTRL1, 0x7, 12),
>> +    .yrgb_axi_gather_num = VOP_REG(RK3229_WIN0_CTRL1, 0xf, 8),
>> +    .vsd_cbcr_gt2 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 7),
>> +    .vsd_cbcr_gt4 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 6),
>> +    .vsd_yrgb_gt2 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 5),
>> +    .vsd_yrgb_gt4 = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 4),
>> +    .bic_coe_sel = VOP_REG(RK3229_WIN0_CTRL1, 0x3, 2),
>> +    .cbcr_axi_gather_en = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 1),
>> +    .yrgb_axi_gather_en = VOP_REG(RK3229_WIN0_CTRL1, 0x1, 0),
>> +    .lb_mode = VOP_REG(RK3229_WIN0_CTRL0, 0x7, 5),
>> +};
>> +
>> +static const struct vop_scl_regs rk3229_win_full_scl = {
>> +    .ext = &rk3229_win_full_scl_ext,
>> +    .scale_yrgb_x = VOP_REG(RK3229_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
>> +    .scale_yrgb_y = VOP_REG(RK3229_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
>> +    .scale_cbcr_x = VOP_REG(RK3229_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0),
>> +    .scale_cbcr_y = VOP_REG(RK3229_WIN0_SCL_FACTOR_CBR, 0xffff, 16),
>> +};
>> +
>> +static const struct vop_win_phy rk3229_win01_data = {
>> +    .scl = &rk3229_win_full_scl,
>> +    .data_formats = formats_win_full,
>> +    .nformats = ARRAY_SIZE(formats_win_full),
>> +    .enable = VOP_REG(RK3229_WIN0_CTRL0, 0x1, 0),
>> +    .format = VOP_REG(RK3229_WIN0_CTRL0, 0x7, 1),
>> +    .rb_swap = VOP_REG(RK3229_WIN0_CTRL0, 0x1, 12),
>> +    .act_info = VOP_REG(RK3229_WIN0_ACT_INFO, 0x1fff1fff, 0),
>> +    .dsp_info = VOP_REG(RK3229_WIN0_DSP_INFO, 0x0fff0fff, 0),
>> +    .dsp_st = VOP_REG(RK3229_WIN0_DSP_ST, 0x1fff1fff, 0),
>> +    .yrgb_mst = VOP_REG(RK3229_WIN0_YRGB_MST, 0xffffffff, 0),
>> +    .uv_mst = VOP_REG(RK3229_WIN0_CBR_MST, 0xffffffff, 0),
>> +    .yrgb_vir = VOP_REG(RK3229_WIN0_VIR, 0x3fff, 0),
>> +    .uv_vir = VOP_REG(RK3229_WIN0_VIR, 0x3fff, 16),
>> +    .src_alpha_ctl = VOP_REG(RK3229_WIN0_SRC_ALPHA_CTRL, 0xff, 0),
>> +    .dst_alpha_ctl = VOP_REG(RK3229_WIN0_DST_ALPHA_CTRL, 0xff, 0),
>> +};
>> +
>> +static const struct vop_win_data rk3229_vop_win_data[] = {
>> +    { .base = 0x00, .phy = &rk3229_win01_data,
>> +      .type = DRM_PLANE_TYPE_PRIMARY },
>> +    { .base = 0x40, .phy = &rk3229_win01_data,
>> +      .type = DRM_PLANE_TYPE_CURSOR },
>> +};
>> +
>> +static const struct vop_ctrl rk3229_ctrl_data = {
>> +    .cfg_done = VOP_REG(RK3229_REG_CFG_DONE, 0x1, 0),
>> +    .standby = VOP_REG(RK3229_SYS_CTRL, 0x1, 22),
>> +    .gate_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 23),
>> +    .mmu_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 20),
>> +    .rgb_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 12),
>> +    .hdmi_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 13),
>> +    .edp_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 14),
>> +    .mipi_en = VOP_REG(RK3229_SYS_CTRL, 0x1, 15),
>> +    .data_blank = VOP_REG(RK3229_DSP_CTRL0, 0x1, 19),
>> +    .out_mode = VOP_REG(RK3229_DSP_CTRL0, 0xf, 0),
>> +    .pin_pol = VOP_REG(RK3229_DSP_CTRL1, 0xf, 20),
>> +    .dither_up = VOP_REG(RK3229_DSP_CTRL1, 0x1, 6),
>> +    .htotal_pw = VOP_REG(RK3229_DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
>> +    .hact_st_end = VOP_REG(RK3229_DSP_HACT_ST_END, 0x1fff1fff, 0),
>> +    .vtotal_pw = VOP_REG(RK3229_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
>> +    .vact_st_end = VOP_REG(RK3229_DSP_VACT_ST_END, 0x1fff1fff, 0),
>> +    .hpost_st_end = VOP_REG(RK3229_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>> +    .vpost_st_end = VOP_REG(RK3229_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
>> +};
>
> Hi Yakir
>
> RK3229 registers layout is simalar to RK3288 layout, is possible to 
> reuse rk3288 register table?
>
> rk3229_ctrl_data & rk3229_win01_data seems same as rk3288_ctrl_data & 
> rk3288_win01_data.
>

Great, glad to do that :)

- Yakir

> Thanks.
>




From mark.yao at rock-chips.com  Tue Jan  5 22:30:19 2016
From: mark.yao at rock-chips.com (Mark yao)
Date: Wed, 06 Jan 2016 14:30:19 +0800
Subject: [PATCH v7 3/5] drm: rockchip: Support Synopsys DW MIPI DSI
In-Reply-To: <1452053038-32098-4-git-send-email-zyw@rock-chips.com>
References: <1452053038-32098-1-git-send-email-zyw@rock-chips.com>
 <1452053038-32098-4-git-send-email-zyw@rock-chips.com>
Message-ID: <568CB47B.1010205@rock-chips.com>

Hi Chris

Looks good for me except some tiny issues.


On 2016?01?06? 12:03, Chris Zhong wrote:
> +struct dw_mipi_dsi {
> +	struct drm_encoder encoder;
> +	struct drm_connector connector;
> +	struct mipi_dsi_host dsi_host;
> +	struct drm_panel *panel;
> +	struct device *dev;
> +	struct regmap *grf_regmap;
> +	void __iomem *base;
> +
> +	struct clk *pllref_clk;
> +	struct clk *pclk;
> +
> +	unsigned int lane_mbps; /* per lane */
> +	u32 channel;
> +	u32 lanes;
> +	u32 format;
> +	u16 input_div;
> +	u16 feedback_div;
> +	struct drm_display_mode *mode;
> +
> +	const struct dw_mipi_dsi_plat_data *pdata;
> +

remove this blank line.

> +};
> +

[...]

> +static void dw_mipi_dsi_encoder_mode_set(struct drm_encoder *encoder,
> +					struct drm_display_mode *mode,
> +					struct drm_display_mode *adjusted_mode)
> +{
> +	struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
> +	int ret;
> +
> +	dsi->mode = adjusted_mode;
> +
> +	ret = dw_mipi_dsi_get_lane_bps(dsi);
> +	if (ret < 0)
> +		return;
> +
> +	if (clk_prepare_enable(dsi->pclk)) {
> +		dev_err(dsi->dev, "%s: Failed to enable pclk\n", __func__);
> +		return;
> +	}
> +
> +	dw_mipi_dsi_init(dsi);
> +	dw_mipi_dsi_dpi_config(dsi, mode);
> +	dw_mipi_dsi_packet_handler_config(dsi);
> +	dw_mipi_dsi_video_mode_config(dsi);
> +	dw_mipi_dsi_video_packet_config(dsi, mode);
> +	dw_mipi_dsi_command_mode_config(dsi);
> +	dw_mipi_dsi_line_timer_config(dsi);
> +	dw_mipi_dsi_vertical_timing_config(dsi);
> +	dw_mipi_dsi_dphy_timing_config(dsi);
> +	dw_mipi_dsi_dphy_interface_config(dsi);
> +	dw_mipi_dsi_clear_err(dsi);
> +	if (drm_panel_prepare(dsi->panel)) {
> +		dev_err(dsi->dev, "failed to prepare panel\n");

Don't forget doing clk_disable_unprepare when run into error path.

> +		return;
> +	}
> +	clk_disable_unprepare(dsi->pclk);
> +}
> +


-- 
?ark Yao




From zyw at rock-chips.com  Tue Jan  5 23:12:09 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Wed,  6 Jan 2016 15:12:09 +0800
Subject: [PATCH v7.1 3/5] drm: rockchip: Support Synopsys DW MIPI DSI
In-Reply-To: <568CB47B.1010205@rock-chips.com>
References: <568CB47B.1010205@rock-chips.com>
Message-ID: <1452064329-25897-1-git-send-email-zyw@rock-chips.com>

Add support for Synopsys DesignWare MIPI DSI controller which is
embedded in the rk3288 SoCs.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
Acked-by: Mark Yao <mark.yao at rock-chips.com>
---

Changes in v7.1:
- make sure disable clk when drm_panel_prepare err (Mark Yao)

Changes in v7:
- modify the config to tristate for modules build (Mark Yao)
- Pass NULL 'name' to drm_encoder_init() to fix compile err (Mark Yao)

Changes in v6:
- move the mipi_en gate to ockchip_drm_crtc_mode_config
- Remove the atomic feature check (Mark Yao)
- Add atomic API support (Heiko St?bne)
- Do not use bridge driver (Thierry Reding)
- Optimization the phy init sequence

Changes in v5: None
Changes in v4: None
Changes in v3: None

 drivers/gpu/drm/rockchip/Kconfig            |   10 +
 drivers/gpu/drm/rockchip/Makefile           |    1 +
 drivers/gpu/drm/rockchip/dw-mipi-dsi.c      | 1197 +++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c |    3 +
 4 files changed, 1211 insertions(+)
 create mode 100644 drivers/gpu/drm/rockchip/dw-mipi-dsi.c

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 35215f6..8573985 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -25,3 +25,13 @@ config ROCKCHIP_DW_HDMI
 	  for the Synopsys DesignWare HDMI driver. If you want to
 	  enable HDMI on RK3288 based SoC, you should selet this
 	  option.
+
+config ROCKCHIP_DW_MIPI_DSI
+	tristate "Rockchip specific extensions for Synopsys DW MIPI DSI"
+	depends on DRM_ROCKCHIP
+	select DRM_MIPI_DSI
+	help
+	 This selects support for Rockchip SoC specific extensions
+	 for the Synopsys DesignWare HDMI driver. If you want to
+	 enable MIPI DSI on RK3288 based SoC, you should selet this
+	 option.
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index a9d380f..d1dc0f7 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -6,6 +6,7 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
 		rockchip_drm_gem.o
 
 obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
+obj-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
 
 obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_drm_vop.o \
 				rockchip_vop_reg.o
diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
new file mode 100644
index 0000000..a36cf60
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
@@ -0,0 +1,1197 @@
+/*
+ * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/iopoll.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drmP.h>
+#include <video/mipi_display.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_vop.h"
+
+#define DRIVER_NAME    "dw-mipi-dsi"
+
+#define GRF_SOC_CON6                    0x025c
+#define DSI0_SEL_VOP_LIT                (1 << 6)
+#define DSI1_SEL_VOP_LIT                (1 << 9)
+
+#define DSI_VERSION			0x00
+#define DSI_PWR_UP			0x04
+#define RESET				0
+#define POWERUP				BIT(0)
+
+#define DSI_CLKMGR_CFG			0x08
+#define TO_CLK_DIVIDSION(div)		(((div) & 0xff) << 8)
+#define TX_ESC_CLK_DIVIDSION(div)	(((div) & 0xff) << 0)
+
+#define DSI_DPI_VCID			0x0c
+#define DPI_VID(vid)			(((vid) & 0x3) << 0)
+
+#define DSI_DPI_COLOR_CODING		0x10
+#define EN18_LOOSELY			BIT(8)
+#define DPI_COLOR_CODING_16BIT_1	0x0
+#define DPI_COLOR_CODING_16BIT_2	0x1
+#define DPI_COLOR_CODING_16BIT_3	0x2
+#define DPI_COLOR_CODING_18BIT_1	0x3
+#define DPI_COLOR_CODING_18BIT_2	0x4
+#define DPI_COLOR_CODING_24BIT		0x5
+
+#define DSI_DPI_CFG_POL			0x14
+#define COLORM_ACTIVE_LOW		BIT(4)
+#define SHUTD_ACTIVE_LOW		BIT(3)
+#define HSYNC_ACTIVE_LOW		BIT(2)
+#define VSYNC_ACTIVE_LOW		BIT(1)
+#define DATAEN_ACTIVE_LOW		BIT(0)
+
+#define DSI_DPI_LP_CMD_TIM		0x18
+#define OUTVACT_LPCMD_TIME(p)		(((p) & 0xff) << 16)
+#define INVACT_LPCMD_TIME(p)		((p) & 0xff)
+
+#define DSI_DBI_CFG			0x20
+#define DSI_DBI_CMDSIZE			0x28
+
+#define DSI_PCKHDL_CFG			0x2c
+#define EN_CRC_RX			BIT(4)
+#define EN_ECC_RX			BIT(3)
+#define EN_BTA				BIT(2)
+#define EN_EOTP_RX			BIT(1)
+#define EN_EOTP_TX			BIT(0)
+
+#define DSI_MODE_CFG			0x34
+#define ENABLE_VIDEO_MODE		0
+#define ENABLE_CMD_MODE			BIT(0)
+
+#define DSI_VID_MODE_CFG		0x38
+#define FRAME_BTA_ACK			BIT(14)
+#define ENABLE_LOW_POWER		(0x3f << 8)
+#define ENABLE_LOW_POWER_MASK		(0x3f << 8)
+#define VID_MODE_TYPE_BURST_SYNC_PULSES		0x2
+#define VID_MODE_TYPE_MASK			0x3
+
+#define DSI_VID_PKT_SIZE		0x3c
+#define VID_PKT_SIZE(p)			(((p) & 0x3fff) << 0)
+#define VID_PKT_MAX_SIZE		0x3fff
+
+#define DSI_VID_HSA_TIME		0x48
+#define DSI_VID_HBP_TIME		0x4c
+#define DSI_VID_HLINE_TIME		0x50
+#define DSI_VID_VSA_LINES		0x54
+#define DSI_VID_VBP_LINES		0x58
+#define DSI_VID_VFP_LINES		0x5c
+#define DSI_VID_VACTIVE_LINES		0x60
+#define DSI_CMD_MODE_CFG		0x68
+#define MAX_RD_PKT_SIZE_LP		BIT(24)
+#define DCS_LW_TX_LP			BIT(19)
+#define DCS_SR_0P_TX_LP			BIT(18)
+#define DCS_SW_1P_TX_LP			BIT(17)
+#define DCS_SW_0P_TX_LP			BIT(16)
+#define GEN_LW_TX_LP			BIT(14)
+#define GEN_SR_2P_TX_LP			BIT(13)
+#define GEN_SR_1P_TX_LP			BIT(12)
+#define GEN_SR_0P_TX_LP			BIT(11)
+#define GEN_SW_2P_TX_LP			BIT(10)
+#define GEN_SW_1P_TX_LP			BIT(9)
+#define GEN_SW_0P_TX_LP			BIT(8)
+#define EN_ACK_RQST			BIT(1)
+#define EN_TEAR_FX			BIT(0)
+
+#define CMD_MODE_ALL_LP			(MAX_RD_PKT_SIZE_LP | \
+					 DCS_LW_TX_LP | \
+					 DCS_SR_0P_TX_LP | \
+					 DCS_SW_1P_TX_LP | \
+					 DCS_SW_0P_TX_LP | \
+					 GEN_LW_TX_LP | \
+					 GEN_SR_2P_TX_LP | \
+					 GEN_SR_1P_TX_LP | \
+					 GEN_SR_0P_TX_LP | \
+					 GEN_SW_2P_TX_LP | \
+					 GEN_SW_1P_TX_LP | \
+					 GEN_SW_0P_TX_LP)
+
+#define DSI_GEN_HDR			0x6c
+#define GEN_HDATA(data)			(((data) & 0xffff) << 8)
+#define GEN_HDATA_MASK			(0xffff << 8)
+#define GEN_HTYPE(type)			(((type) & 0xff) << 0)
+#define GEN_HTYPE_MASK			0xff
+
+#define DSI_GEN_PLD_DATA		0x70
+
+#define DSI_CMD_PKT_STATUS		0x74
+#define GEN_CMD_EMPTY			BIT(0)
+#define GEN_CMD_FULL			BIT(1)
+#define GEN_PLD_W_EMPTY			BIT(2)
+#define GEN_PLD_W_FULL			BIT(3)
+#define GEN_PLD_R_EMPTY			BIT(4)
+#define GEN_PLD_R_FULL			BIT(5)
+#define GEN_RD_CMD_BUSY			BIT(6)
+
+#define DSI_TO_CNT_CFG			0x78
+#define HSTX_TO_CNT(p)			(((p) & 0xffff) << 16)
+#define LPRX_TO_CNT(p)			((p) & 0xffff)
+
+#define DSI_BTA_TO_CNT			0x8c
+
+#define DSI_LPCLK_CTRL			0x94
+#define AUTO_CLKLANE_CTRL		BIT(1)
+#define PHY_TXREQUESTCLKHS		BIT(0)
+
+#define DSI_PHY_TMR_LPCLK_CFG		0x98
+#define PHY_CLKHS2LP_TIME(lbcc)		(((lbcc) & 0x3ff) << 16)
+#define PHY_CLKLP2HS_TIME(lbcc)		((lbcc) & 0x3ff)
+
+#define DSI_PHY_TMR_CFG			0x9c
+#define PHY_HS2LP_TIME(lbcc)		(((lbcc) & 0xff) << 24)
+#define PHY_LP2HS_TIME(lbcc)		(((lbcc) & 0xff) << 16)
+#define MAX_RD_TIME(lbcc)		((lbcc) & 0x7fff)
+
+#define DSI_PHY_RSTZ			0xa0
+#define PHY_DISFORCEPLL			0
+#define PHY_ENFORCEPLL			BIT(3)
+#define PHY_DISABLECLK			0
+#define PHY_ENABLECLK			BIT(2)
+#define PHY_RSTZ			0
+#define PHY_UNRSTZ			BIT(1)
+#define PHY_SHUTDOWNZ			0
+#define PHY_UNSHUTDOWNZ			BIT(0)
+
+#define DSI_PHY_IF_CFG			0xa4
+#define N_LANES(n)			((((n) - 1) & 0x3) << 0)
+#define PHY_STOP_WAIT_TIME(cycle)	(((cycle) & 0xff) << 8)
+
+#define DSI_PHY_STATUS			0xb0
+#define LOCK				BIT(0)
+#define STOP_STATE_CLK_LANE		BIT(2)
+
+#define DSI_PHY_TST_CTRL0		0xb4
+#define PHY_TESTCLK			BIT(1)
+#define PHY_UNTESTCLK			0
+#define PHY_TESTCLR			BIT(0)
+#define PHY_UNTESTCLR			0
+
+#define DSI_PHY_TST_CTRL1		0xb8
+#define PHY_TESTEN			BIT(16)
+#define PHY_UNTESTEN			0
+#define PHY_TESTDOUT(n)			(((n) & 0xff) << 8)
+#define PHY_TESTDIN(n)			(((n) & 0xff) << 0)
+
+#define DSI_INT_ST0			0xbc
+#define DSI_INT_ST1			0xc0
+#define DSI_INT_MSK0			0xc4
+#define DSI_INT_MSK1			0xc8
+
+#define PHY_STATUS_TIMEOUT_US		10000
+#define CMD_PKT_STATUS_TIMEOUT_US	20000
+
+#define BYPASS_VCO_RANGE	BIT(7)
+#define VCO_RANGE_CON_SEL(val)	(((val) & 0x7) << 3)
+#define VCO_IN_CAP_CON_DEFAULT	(0x0 << 1)
+#define VCO_IN_CAP_CON_LOW	(0x1 << 1)
+#define VCO_IN_CAP_CON_HIGH	(0x2 << 1)
+#define REF_BIAS_CUR_SEL	BIT(0)
+
+#define CP_CURRENT_3MA		BIT(3)
+#define CP_PROGRAM_EN		BIT(7)
+#define LPF_PROGRAM_EN		BIT(6)
+#define LPF_RESISTORS_20_KOHM	0
+
+#define HSFREQRANGE_SEL(val)	(((val) & 0x3f) << 1)
+
+#define INPUT_DIVIDER(val)	((val - 1) & 0x7f)
+#define LOW_PROGRAM_EN		0
+#define HIGH_PROGRAM_EN		BIT(7)
+#define LOOP_DIV_LOW_SEL(val)	((val - 1) & 0x1f)
+#define LOOP_DIV_HIGH_SEL(val)	(((val - 1) >> 5) & 0x1f)
+#define PLL_LOOP_DIV_EN		BIT(5)
+#define PLL_INPUT_DIV_EN	BIT(4)
+
+#define POWER_CONTROL		BIT(6)
+#define INTERNAL_REG_CURRENT	BIT(3)
+#define BIAS_BLOCK_ON		BIT(2)
+#define BANDGAP_ON		BIT(0)
+
+#define TER_RESISTOR_HIGH	BIT(7)
+#define	TER_RESISTOR_LOW	0
+#define LEVEL_SHIFTERS_ON	BIT(6)
+#define TER_CAL_DONE		BIT(5)
+#define SETRD_MAX		(0x7 << 2)
+#define POWER_MANAGE		BIT(1)
+#define TER_RESISTORS_ON	BIT(0)
+
+#define BIASEXTR_SEL(val)	((val) & 0x7)
+#define BANDGAP_SEL(val)	((val) & 0x7)
+#define TLP_PROGRAM_EN		BIT(7)
+#define THS_PRE_PROGRAM_EN	BIT(7)
+#define THS_ZERO_PROGRAM_EN	BIT(6)
+
+enum {
+	BANDGAP_97_07,
+	BANDGAP_98_05,
+	BANDGAP_99_02,
+	BANDGAP_100_00,
+	BANDGAP_93_17,
+	BANDGAP_94_15,
+	BANDGAP_95_12,
+	BANDGAP_96_10,
+};
+
+enum {
+	BIASEXTR_87_1,
+	BIASEXTR_91_5,
+	BIASEXTR_95_9,
+	BIASEXTR_100,
+	BIASEXTR_105_94,
+	BIASEXTR_111_88,
+	BIASEXTR_118_8,
+	BIASEXTR_127_7,
+};
+
+struct dw_mipi_dsi_plat_data {
+	unsigned int max_data_lanes;
+	enum drm_mode_status (*mode_valid)(struct drm_connector *connector,
+					   struct drm_display_mode *mode);
+};
+
+struct dw_mipi_dsi {
+	struct drm_encoder encoder;
+	struct drm_connector connector;
+	struct mipi_dsi_host dsi_host;
+	struct drm_panel *panel;
+	struct device *dev;
+	struct regmap *grf_regmap;
+	void __iomem *base;
+
+	struct clk *pllref_clk;
+	struct clk *pclk;
+
+	unsigned int lane_mbps; /* per lane */
+	u32 channel;
+	u32 lanes;
+	u32 format;
+	u16 input_div;
+	u16 feedback_div;
+	struct drm_display_mode *mode;
+
+	const struct dw_mipi_dsi_plat_data *pdata;
+};
+
+enum dw_mipi_dsi_mode {
+	DW_MIPI_DSI_CMD_MODE,
+	DW_MIPI_DSI_VID_MODE,
+};
+
+struct dphy_pll_testdin_map {
+	unsigned int max_mbps;
+	u8 testdin;
+};
+
+/* The table is based on 27MHz DPHY pll reference clock. */
+static const struct dphy_pll_testdin_map dptdin_map[] = {
+	{  90, 0x00}, { 100, 0x10}, { 110, 0x20}, { 130, 0x01},
+	{ 140, 0x11}, { 150, 0x21}, { 170, 0x02}, { 180, 0x12},
+	{ 200, 0x22}, { 220, 0x03}, { 240, 0x13}, { 250, 0x23},
+	{ 270, 0x04}, { 300, 0x14}, { 330, 0x05}, { 360, 0x15},
+	{ 400, 0x25}, { 450, 0x06}, { 500, 0x16}, { 550, 0x07},
+	{ 600, 0x17}, { 650, 0x08}, { 700, 0x18}, { 750, 0x09},
+	{ 800, 0x19}, { 850, 0x29}, { 900, 0x39}, { 950, 0x0a},
+	{1000, 0x1a}, {1050, 0x2a}, {1100, 0x3a}, {1150, 0x0b},
+	{1200, 0x1b}, {1250, 0x2b}, {1300, 0x3b}, {1350, 0x0c},
+	{1400, 0x1c}, {1450, 0x2c}, {1500, 0x3c}
+};
+
+static int max_mbps_to_testdin(unsigned int max_mbps)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(dptdin_map); i++)
+		if (dptdin_map[i].max_mbps > max_mbps)
+			return dptdin_map[i].testdin;
+
+	return -EINVAL;
+}
+
+/*
+ * The controller should generate 2 frames before
+ * preparing the peripheral.
+ */
+static void dw_mipi_dsi_wait_for_two_frames(struct dw_mipi_dsi *dsi)
+{
+	int refresh, two_frames;
+
+	refresh = drm_mode_vrefresh(dsi->mode);
+	two_frames = DIV_ROUND_UP(MSEC_PER_SEC, refresh) * 2;
+	msleep(two_frames);
+}
+
+static inline struct dw_mipi_dsi *host_to_dsi(struct mipi_dsi_host *host)
+{
+	return container_of(host, struct dw_mipi_dsi, dsi_host);
+}
+
+static inline struct dw_mipi_dsi *con_to_dsi(struct drm_connector *con)
+{
+	return container_of(con, struct dw_mipi_dsi, connector);
+}
+
+static inline struct dw_mipi_dsi *encoder_to_dsi(struct drm_encoder *encoder)
+{
+	return container_of(encoder, struct dw_mipi_dsi, encoder);
+}
+static inline void dsi_write(struct dw_mipi_dsi *dsi, u32 reg, u32 val)
+{
+	writel(val, dsi->base + reg);
+}
+
+static inline u32 dsi_read(struct dw_mipi_dsi *dsi, u32 reg)
+{
+	return readl(dsi->base + reg);
+}
+
+static void dw_mipi_dsi_phy_write(struct dw_mipi_dsi *dsi, u8 test_code,
+				 u8 test_data)
+{
+	/*
+	 * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content
+	 * is latched internally as the current test code. Test data is
+	 * programmed internally by rising edge on TESTCLK.
+	 */
+	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR);
+
+	dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_TESTEN | PHY_TESTDOUT(0) |
+					  PHY_TESTDIN(test_code));
+
+	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLK | PHY_UNTESTCLR);
+
+	dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_UNTESTEN | PHY_TESTDOUT(0) |
+					  PHY_TESTDIN(test_data));
+
+	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR);
+}
+
+static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi)
+{
+	int ret, testdin, vco, val;
+
+	vco = (dsi->lane_mbps < 200) ? 0 : (dsi->lane_mbps + 100) / 200;
+
+	testdin = max_mbps_to_testdin(dsi->lane_mbps);
+	if (testdin < 0) {
+		dev_err(dsi->dev,
+			"failed to get testdin for %dmbps lane clock\n",
+			dsi->lane_mbps);
+		return testdin;
+	}
+
+	dsi_write(dsi, DSI_PWR_UP, POWERUP);
+
+	dw_mipi_dsi_phy_write(dsi, 0x10, BYPASS_VCO_RANGE |
+					 VCO_RANGE_CON_SEL(vco) |
+					 VCO_IN_CAP_CON_LOW |
+					 REF_BIAS_CUR_SEL);
+
+	dw_mipi_dsi_phy_write(dsi, 0x11, CP_CURRENT_3MA);
+	dw_mipi_dsi_phy_write(dsi, 0x12, CP_PROGRAM_EN | LPF_PROGRAM_EN |
+					 LPF_RESISTORS_20_KOHM);
+
+	dw_mipi_dsi_phy_write(dsi, 0x44, HSFREQRANGE_SEL(testdin));
+
+	dw_mipi_dsi_phy_write(dsi, 0x19, PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN);
+	dw_mipi_dsi_phy_write(dsi, 0x17, INPUT_DIVIDER(dsi->input_div));
+	dw_mipi_dsi_phy_write(dsi, 0x18, LOOP_DIV_LOW_SEL(dsi->feedback_div) |
+					 LOW_PROGRAM_EN);
+	dw_mipi_dsi_phy_write(dsi, 0x18, LOOP_DIV_HIGH_SEL(dsi->feedback_div) |
+					 HIGH_PROGRAM_EN);
+
+	dw_mipi_dsi_phy_write(dsi, 0x20, POWER_CONTROL | INTERNAL_REG_CURRENT |
+					 BIAS_BLOCK_ON | BANDGAP_ON);
+
+	dw_mipi_dsi_phy_write(dsi, 0x21, TER_RESISTOR_LOW | TER_CAL_DONE |
+					 SETRD_MAX | TER_RESISTORS_ON);
+	dw_mipi_dsi_phy_write(dsi, 0x21, TER_RESISTOR_HIGH | LEVEL_SHIFTERS_ON |
+					 SETRD_MAX | POWER_MANAGE |
+					 TER_RESISTORS_ON);
+
+	dw_mipi_dsi_phy_write(dsi, 0x22, LOW_PROGRAM_EN |
+					 BIASEXTR_SEL(BIASEXTR_127_7));
+	dw_mipi_dsi_phy_write(dsi, 0x22, HIGH_PROGRAM_EN |
+					 BANDGAP_SEL(BANDGAP_96_10));
+
+	dw_mipi_dsi_phy_write(dsi, 0x70, TLP_PROGRAM_EN | 0xf);
+	dw_mipi_dsi_phy_write(dsi, 0x71, THS_PRE_PROGRAM_EN | 0x55);
+	dw_mipi_dsi_phy_write(dsi, 0x72, THS_ZERO_PROGRAM_EN | 0xa);
+
+	dsi_write(dsi, DSI_PHY_RSTZ, PHY_ENFORCEPLL | PHY_ENABLECLK |
+				     PHY_UNRSTZ | PHY_UNSHUTDOWNZ);
+
+
+	ret = readx_poll_timeout(readl, dsi->base + DSI_PHY_STATUS,
+				 val, val & LOCK, 1000, PHY_STATUS_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dsi->dev, "failed to wait for phy lock state\n");
+		return ret;
+	}
+
+	ret = readx_poll_timeout(readl, dsi->base + DSI_PHY_STATUS,
+				 val, val & STOP_STATE_CLK_LANE, 1000,
+				 PHY_STATUS_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dsi->dev,
+			"failed to wait for phy clk lane stop state\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static int dw_mipi_dsi_get_lane_bps(struct dw_mipi_dsi *dsi)
+{
+	unsigned int bpp, i, pre;
+	unsigned long mpclk, pllref, tmp;
+	unsigned int m = 1, n = 1, target_mbps = 1000;
+	unsigned int max_mbps = dptdin_map[ARRAY_SIZE(dptdin_map) - 1].max_mbps;
+
+	bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
+	if (bpp < 0) {
+		dev_err(dsi->dev, "failed to get bpp for pixel format %d\n",
+			dsi->format);
+		return bpp;
+	}
+
+	mpclk = DIV_ROUND_UP(dsi->mode->clock, MSEC_PER_SEC);
+	if (mpclk) {
+		/* take 1 / 0.9, since mbps must big than bandwidth of RGB */
+		tmp = mpclk * (bpp / dsi->lanes) * 10 / 9;
+		if (tmp < max_mbps)
+			target_mbps = tmp;
+		else
+			dev_err(dsi->dev, "DPHY clock frequency is out of range\n");
+	}
+
+	pllref = DIV_ROUND_UP(clk_get_rate(dsi->pllref_clk), USEC_PER_SEC);
+	tmp = pllref;
+
+	for (i = 1; i < 6; i++) {
+		pre = pllref / i;
+		if ((tmp > (target_mbps % pre)) && (target_mbps / pre < 512)) {
+			tmp = target_mbps % pre;
+			n = i;
+			m = target_mbps / pre;
+		}
+		if (tmp == 0)
+			break;
+	}
+
+	dsi->lane_mbps = pllref / n * m;
+	dsi->input_div = n;
+	dsi->feedback_div = m;
+
+	return 0;
+}
+
+static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host,
+				   struct mipi_dsi_device *device)
+{
+	struct dw_mipi_dsi *dsi = host_to_dsi(host);
+
+	if (device->lanes > dsi->pdata->max_data_lanes) {
+		dev_err(dsi->dev, "the number of data lanes(%u) is too many\n",
+				device->lanes);
+		return -EINVAL;
+	}
+
+	if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) ||
+	    !(device->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)) {
+		dev_err(dsi->dev, "device mode is unsupported\n");
+		return -EINVAL;
+	}
+
+	dsi->lanes = device->lanes;
+	dsi->channel = device->channel;
+	dsi->format = device->format;
+	dsi->panel = of_drm_find_panel(device->dev.of_node);
+	if (dsi->panel)
+		return drm_panel_attach(dsi->panel, &dsi->connector);
+
+	return -EINVAL;
+}
+
+static int dw_mipi_dsi_host_detach(struct mipi_dsi_host *host,
+				   struct mipi_dsi_device *device)
+{
+	struct dw_mipi_dsi *dsi = host_to_dsi(host);
+
+	drm_panel_detach(dsi->panel);
+
+	return 0;
+}
+
+static int dw_mipi_dsi_gen_pkt_hdr_write(struct dw_mipi_dsi *dsi, u32 val)
+{
+	int ret;
+
+	ret = readx_poll_timeout(readl, dsi->base + DSI_CMD_PKT_STATUS,
+				 val, !(val & GEN_CMD_FULL), 1000,
+				 CMD_PKT_STATUS_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dsi->dev, "failed to get available command FIFO\n");
+		return ret;
+	}
+
+	dsi_write(dsi, DSI_GEN_HDR, val);
+
+	ret = readx_poll_timeout(readl, dsi->base + DSI_CMD_PKT_STATUS,
+				 val, val & (GEN_CMD_EMPTY | GEN_PLD_W_EMPTY),
+				 1000, CMD_PKT_STATUS_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dsi->dev, "failed to write command FIFO\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int dw_mipi_dsi_dcs_short_write(struct dw_mipi_dsi *dsi,
+				       const struct mipi_dsi_msg *msg)
+{
+	const u16 *tx_buf = msg->tx_buf;
+	u32 val = GEN_HDATA(*tx_buf) | GEN_HTYPE(msg->type);
+
+	if (msg->tx_len > 2) {
+		dev_err(dsi->dev, "too long tx buf length %zu for short write\n",
+			msg->tx_len);
+		return -EINVAL;
+	}
+
+	return dw_mipi_dsi_gen_pkt_hdr_write(dsi, val);
+}
+
+static int dw_mipi_dsi_dcs_long_write(struct dw_mipi_dsi *dsi,
+				      const struct mipi_dsi_msg *msg)
+{
+	const u32 *tx_buf = msg->tx_buf;
+	int len = msg->tx_len, pld_data_bytes = sizeof(*tx_buf), ret;
+	u32 val = GEN_HDATA(msg->tx_len) | GEN_HTYPE(msg->type);
+	u32 remainder = 0;
+
+	if (msg->tx_len < 3) {
+		dev_err(dsi->dev, "wrong tx buf length %zu for long write\n",
+			msg->tx_len);
+		return -EINVAL;
+	}
+
+	while (DIV_ROUND_UP(len, pld_data_bytes)) {
+		if (len < pld_data_bytes) {
+			memcpy(&remainder, tx_buf, len);
+			dsi_write(dsi, DSI_GEN_PLD_DATA, remainder);
+			len = 0;
+		} else {
+			dsi_write(dsi, DSI_GEN_PLD_DATA, *tx_buf);
+			tx_buf++;
+			len -= pld_data_bytes;
+		}
+
+		ret = readx_poll_timeout(readl, dsi->base + DSI_CMD_PKT_STATUS,
+					 val, !(val & GEN_PLD_W_FULL), 1000,
+					 CMD_PKT_STATUS_TIMEOUT_US);
+		if (ret < 0) {
+			dev_err(dsi->dev,
+				"failed to get available write payload FIFO\n");
+			return ret;
+		}
+	}
+
+	return dw_mipi_dsi_gen_pkt_hdr_write(dsi, val);
+}
+
+static ssize_t dw_mipi_dsi_host_transfer(struct mipi_dsi_host *host,
+					 const struct mipi_dsi_msg *msg)
+{
+	struct dw_mipi_dsi *dsi = host_to_dsi(host);
+	int ret;
+
+	switch (msg->type) {
+	case MIPI_DSI_DCS_SHORT_WRITE:
+	case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
+	case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
+		ret = dw_mipi_dsi_dcs_short_write(dsi, msg);
+		break;
+	case MIPI_DSI_DCS_LONG_WRITE:
+		ret = dw_mipi_dsi_dcs_long_write(dsi, msg);
+		break;
+	default:
+		dev_err(dsi->dev, "unsupported message type\n");
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static const struct mipi_dsi_host_ops dw_mipi_dsi_host_ops = {
+	.attach = dw_mipi_dsi_host_attach,
+	.detach = dw_mipi_dsi_host_detach,
+	.transfer = dw_mipi_dsi_host_transfer,
+};
+
+static void dw_mipi_dsi_video_mode_config(struct dw_mipi_dsi *dsi)
+{
+	u32 val;
+
+	val = VID_MODE_TYPE_BURST_SYNC_PULSES | ENABLE_LOW_POWER;
+
+	dsi_write(dsi, DSI_VID_MODE_CFG, val);
+}
+
+static void dw_mipi_dsi_set_mode(struct dw_mipi_dsi *dsi,
+				 enum dw_mipi_dsi_mode mode)
+{
+	if (mode == DW_MIPI_DSI_CMD_MODE) {
+		dsi_write(dsi, DSI_PWR_UP, RESET);
+		dsi_write(dsi, DSI_MODE_CFG, ENABLE_CMD_MODE);
+		dsi_write(dsi, DSI_PWR_UP, POWERUP);
+	} else {
+		dsi_write(dsi, DSI_PWR_UP, RESET);
+		dsi_write(dsi, DSI_MODE_CFG, ENABLE_VIDEO_MODE);
+		dw_mipi_dsi_video_mode_config(dsi);
+		dsi_write(dsi, DSI_PWR_UP, POWERUP);
+	}
+}
+
+static void dw_mipi_dsi_disable(struct dw_mipi_dsi *dsi)
+{
+	dsi_write(dsi, DSI_PWR_UP, RESET);
+	dsi_write(dsi, DSI_PHY_RSTZ, PHY_RSTZ);
+}
+
+static void dw_mipi_dsi_init(struct dw_mipi_dsi *dsi)
+{
+	dsi_write(dsi, DSI_PWR_UP, RESET);
+	dsi_write(dsi, DSI_PHY_RSTZ, PHY_DISFORCEPLL | PHY_DISABLECLK
+		  | PHY_RSTZ | PHY_SHUTDOWNZ);
+	dsi_write(dsi, DSI_CLKMGR_CFG, TO_CLK_DIVIDSION(10) |
+		  TX_ESC_CLK_DIVIDSION(7));
+	dsi_write(dsi, DSI_LPCLK_CTRL, PHY_TXREQUESTCLKHS);
+}
+
+static void dw_mipi_dsi_dpi_config(struct dw_mipi_dsi *dsi,
+				   struct drm_display_mode *mode)
+{
+	u32 val = 0, color = 0;
+
+	switch (dsi->format) {
+	case MIPI_DSI_FMT_RGB888:
+		color = DPI_COLOR_CODING_24BIT;
+		break;
+	case MIPI_DSI_FMT_RGB666:
+		color = DPI_COLOR_CODING_18BIT_2 | EN18_LOOSELY;
+		break;
+	case MIPI_DSI_FMT_RGB666_PACKED:
+		color = DPI_COLOR_CODING_18BIT_1;
+		break;
+	case MIPI_DSI_FMT_RGB565:
+		color = DPI_COLOR_CODING_16BIT_1;
+		break;
+	}
+
+	if (!(mode->flags & DRM_MODE_FLAG_PVSYNC))
+		val |= VSYNC_ACTIVE_LOW;
+	if (!(mode->flags & DRM_MODE_FLAG_PHSYNC))
+		val |= HSYNC_ACTIVE_LOW;
+
+	dsi_write(dsi, DSI_DPI_VCID, DPI_VID(dsi->channel));
+	dsi_write(dsi, DSI_DPI_COLOR_CODING, color);
+	dsi_write(dsi, DSI_DPI_CFG_POL, val);
+	dsi_write(dsi, DSI_DPI_LP_CMD_TIM, OUTVACT_LPCMD_TIME(4)
+		  | INVACT_LPCMD_TIME(4));
+}
+
+static void dw_mipi_dsi_packet_handler_config(struct dw_mipi_dsi *dsi)
+{
+	dsi_write(dsi, DSI_PCKHDL_CFG, EN_CRC_RX | EN_ECC_RX | EN_BTA);
+}
+
+static void dw_mipi_dsi_video_packet_config(struct dw_mipi_dsi *dsi,
+					    struct drm_display_mode *mode)
+{
+	dsi_write(dsi, DSI_VID_PKT_SIZE, VID_PKT_SIZE(mode->hdisplay));
+}
+
+static void dw_mipi_dsi_command_mode_config(struct dw_mipi_dsi *dsi)
+{
+	dsi_write(dsi, DSI_TO_CNT_CFG, HSTX_TO_CNT(1000) | LPRX_TO_CNT(1000));
+	dsi_write(dsi, DSI_BTA_TO_CNT, 0xd00);
+	dsi_write(dsi, DSI_CMD_MODE_CFG, CMD_MODE_ALL_LP);
+	dsi_write(dsi, DSI_MODE_CFG, ENABLE_CMD_MODE);
+}
+
+/* Get lane byte clock cycles. */
+static u32 dw_mipi_dsi_get_hcomponent_lbcc(struct dw_mipi_dsi *dsi,
+					   u32 hcomponent)
+{
+	u32 frac, lbcc;
+
+	lbcc = hcomponent * dsi->lane_mbps * MSEC_PER_SEC / 8;
+
+	frac = lbcc % dsi->mode->clock;
+	lbcc = lbcc / dsi->mode->clock;
+	if (frac)
+		lbcc++;
+
+	return lbcc;
+}
+
+static void dw_mipi_dsi_line_timer_config(struct dw_mipi_dsi *dsi)
+{
+	u32 htotal, hsa, hbp, lbcc;
+	struct drm_display_mode *mode = dsi->mode;
+
+	htotal = mode->htotal;
+	hsa = mode->hsync_end - mode->hsync_start;
+	hbp = mode->htotal - mode->hsync_end;
+
+	lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, htotal);
+	dsi_write(dsi, DSI_VID_HLINE_TIME, lbcc);
+
+	lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, hsa);
+	dsi_write(dsi, DSI_VID_HSA_TIME, lbcc);
+
+	lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, hbp);
+	dsi_write(dsi, DSI_VID_HBP_TIME, lbcc);
+}
+
+static void dw_mipi_dsi_vertical_timing_config(struct dw_mipi_dsi *dsi)
+{
+	u32 vactive, vsa, vfp, vbp;
+	struct drm_display_mode *mode = dsi->mode;
+
+	vactive = mode->vdisplay;
+	vsa = mode->vsync_end - mode->vsync_start;
+	vfp = mode->vsync_start - mode->vdisplay;
+	vbp = mode->vtotal - mode->vsync_end;
+
+	dsi_write(dsi, DSI_VID_VACTIVE_LINES, vactive);
+	dsi_write(dsi, DSI_VID_VSA_LINES, vsa);
+	dsi_write(dsi, DSI_VID_VFP_LINES, vfp);
+	dsi_write(dsi, DSI_VID_VBP_LINES, vbp);
+}
+
+static void dw_mipi_dsi_dphy_timing_config(struct dw_mipi_dsi *dsi)
+{
+	dsi_write(dsi, DSI_PHY_TMR_CFG, PHY_HS2LP_TIME(0x40)
+		  | PHY_LP2HS_TIME(0x40) | MAX_RD_TIME(10000));
+
+	dsi_write(dsi, DSI_PHY_TMR_LPCLK_CFG, PHY_CLKHS2LP_TIME(0x40)
+		  | PHY_CLKLP2HS_TIME(0x40));
+}
+
+static void dw_mipi_dsi_dphy_interface_config(struct dw_mipi_dsi *dsi)
+{
+	dsi_write(dsi, DSI_PHY_IF_CFG, PHY_STOP_WAIT_TIME(0x20) |
+		  N_LANES(dsi->lanes));
+}
+
+static void dw_mipi_dsi_clear_err(struct dw_mipi_dsi *dsi)
+{
+	dsi_read(dsi, DSI_INT_ST0);
+	dsi_read(dsi, DSI_INT_ST1);
+	dsi_write(dsi, DSI_INT_MSK0, 0);
+	dsi_write(dsi, DSI_INT_MSK1, 0);
+}
+
+static void dw_mipi_dsi_encoder_mode_set(struct drm_encoder *encoder,
+					struct drm_display_mode *mode,
+					struct drm_display_mode *adjusted_mode)
+{
+	struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
+	int ret;
+
+	dsi->mode = adjusted_mode;
+
+	ret = dw_mipi_dsi_get_lane_bps(dsi);
+	if (ret < 0)
+		return;
+
+	if (clk_prepare_enable(dsi->pclk)) {
+		dev_err(dsi->dev, "%s: Failed to enable pclk\n", __func__);
+		return;
+	}
+
+	dw_mipi_dsi_init(dsi);
+	dw_mipi_dsi_dpi_config(dsi, mode);
+	dw_mipi_dsi_packet_handler_config(dsi);
+	dw_mipi_dsi_video_mode_config(dsi);
+	dw_mipi_dsi_video_packet_config(dsi, mode);
+	dw_mipi_dsi_command_mode_config(dsi);
+	dw_mipi_dsi_line_timer_config(dsi);
+	dw_mipi_dsi_vertical_timing_config(dsi);
+	dw_mipi_dsi_dphy_timing_config(dsi);
+	dw_mipi_dsi_dphy_interface_config(dsi);
+	dw_mipi_dsi_clear_err(dsi);
+	if (drm_panel_prepare(dsi->panel)) {
+		dev_err(dsi->dev, "failed to prepare panel\n");
+		goto mode_set_end;
+	}
+
+mode_set_end:
+	clk_disable_unprepare(dsi->pclk);
+}
+
+static void dw_mipi_dsi_encoder_disable(struct drm_encoder *encoder)
+{
+	struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
+
+	drm_panel_disable(dsi->panel);
+
+	if (clk_prepare_enable(dsi->pclk)) {
+		dev_err(dsi->dev, "%s: Failed to enable pclk\n", __func__);
+		return;
+	}
+
+	dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_CMD_MODE);
+	drm_panel_unprepare(dsi->panel);
+	dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_VID_MODE);
+
+	/*
+	 * This is necessary to make sure the peripheral will be driven
+	 * normally when the display is enabled again later.
+	 */
+	msleep(120);
+
+	dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_CMD_MODE);
+	dw_mipi_dsi_disable(dsi);
+	clk_disable_unprepare(dsi->pclk);
+}
+
+static bool dw_mipi_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
+					const struct drm_display_mode *mode,
+					struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static void dw_mipi_dsi_encoder_commit(struct drm_encoder *encoder)
+{
+	struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
+	int mux  = rockchip_drm_encoder_get_mux_id(dsi->dev->of_node, encoder);
+	u32 interface_pix_fmt;
+	u32 val;
+
+	if (clk_prepare_enable(dsi->pclk)) {
+		dev_err(dsi->dev, "%s: Failed to enable pclk\n", __func__);
+		return;
+	}
+
+	dw_mipi_dsi_phy_init(dsi);
+	dw_mipi_dsi_wait_for_two_frames(dsi);
+
+	dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_VID_MODE);
+	drm_panel_enable(dsi->panel);
+
+	clk_disable_unprepare(dsi->pclk);
+
+	switch (dsi->format) {
+	case MIPI_DSI_FMT_RGB888:
+		interface_pix_fmt = ROCKCHIP_OUT_MODE_P888;
+		break;
+	case MIPI_DSI_FMT_RGB666:
+		interface_pix_fmt = ROCKCHIP_OUT_MODE_P666;
+		break;
+	case MIPI_DSI_FMT_RGB565:
+		interface_pix_fmt = ROCKCHIP_OUT_MODE_P565;
+		break;
+	default:
+		WARN_ON(1);
+		return;
+	}
+
+	rockchip_drm_crtc_mode_config(encoder->crtc, DRM_MODE_CONNECTOR_DSI,
+				      interface_pix_fmt);
+
+	if (mux)
+		val = DSI0_SEL_VOP_LIT | (DSI0_SEL_VOP_LIT << 16);
+	else
+		val = DSI0_SEL_VOP_LIT << 16;
+
+	regmap_write(dsi->grf_regmap, GRF_SOC_CON6, val);
+	dev_dbg(dsi->dev, "vop %s output to dsi0\n", (mux) ? "LIT" : "BIG");
+}
+
+static struct drm_encoder_helper_funcs
+dw_mipi_dsi_encoder_helper_funcs = {
+	.mode_fixup = dw_mipi_dsi_encoder_mode_fixup,
+	.commit = dw_mipi_dsi_encoder_commit,
+	.mode_set = dw_mipi_dsi_encoder_mode_set,
+	.disable = dw_mipi_dsi_encoder_disable,
+};
+
+static struct drm_encoder_funcs dw_mipi_dsi_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static int dw_mipi_dsi_connector_get_modes(struct drm_connector *connector)
+{
+	struct dw_mipi_dsi *dsi = con_to_dsi(connector);
+
+	return drm_panel_get_modes(dsi->panel);
+}
+
+static enum drm_mode_status dw_mipi_dsi_mode_valid(
+					struct drm_connector *connector,
+					struct drm_display_mode *mode)
+{
+	struct dw_mipi_dsi *dsi = con_to_dsi(connector);
+
+	enum drm_mode_status mode_status = MODE_OK;
+
+	if (dsi->pdata->mode_valid)
+		mode_status = dsi->pdata->mode_valid(connector, mode);
+
+	return mode_status;
+}
+
+static struct drm_encoder *dw_mipi_dsi_connector_best_encoder(
+					struct drm_connector *connector)
+{
+	struct dw_mipi_dsi *dsi = con_to_dsi(connector);
+
+	return &dsi->encoder;
+}
+
+static struct drm_connector_helper_funcs dw_mipi_dsi_connector_helper_funcs = {
+	.get_modes = dw_mipi_dsi_connector_get_modes,
+	.mode_valid = dw_mipi_dsi_mode_valid,
+	.best_encoder = dw_mipi_dsi_connector_best_encoder,
+};
+
+static enum drm_connector_status
+dw_mipi_dsi_detect(struct drm_connector *connector, bool force)
+{
+	return connector_status_connected;
+}
+
+static void dw_mipi_dsi_drm_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs dw_mipi_dsi_atomic_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.detect = dw_mipi_dsi_detect,
+	.destroy = dw_mipi_dsi_drm_connector_destroy,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int dw_mipi_dsi_register(struct drm_device *drm,
+				      struct dw_mipi_dsi *dsi)
+{
+	struct drm_encoder *encoder = &dsi->encoder;
+	struct drm_connector *connector = &dsi->connector;
+	struct device *dev = dsi->dev;
+	int ret;
+
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm,
+							     dev->of_node);
+	/*
+	 * If we failed to find the CRTC(s) which this encoder is
+	 * supposed to be connected to, it's because the CRTC has
+	 * not been registered yet.  Defer probing, and hope that
+	 * the required CRTC is added later.
+	 */
+	if (encoder->possible_crtcs == 0)
+		return -EPROBE_DEFER;
+
+	drm_encoder_helper_add(&dsi->encoder,
+			       &dw_mipi_dsi_encoder_helper_funcs);
+	ret = drm_encoder_init(drm, &dsi->encoder, &dw_mipi_dsi_encoder_funcs,
+			 DRM_MODE_ENCODER_DSI, NULL);
+	if (ret) {
+		dev_err(dev, "Failed to initialize encoder with drm\n");
+		return ret;
+	}
+
+	drm_connector_helper_add(connector,
+			&dw_mipi_dsi_connector_helper_funcs);
+
+	drm_connector_init(drm, &dsi->connector,
+			   &dw_mipi_dsi_atomic_connector_funcs,
+			   DRM_MODE_CONNECTOR_DSI);
+
+	drm_mode_connector_attach_encoder(connector, encoder);
+
+	return 0;
+}
+
+static int rockchip_mipi_parse_dt(struct dw_mipi_dsi *dsi)
+{
+	struct device_node *np = dsi->dev->of_node;
+
+	dsi->grf_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+	if (IS_ERR(dsi->grf_regmap)) {
+		dev_err(dsi->dev, "Unable to get rockchip,grf\n");
+		return PTR_ERR(dsi->grf_regmap);
+	}
+
+	return 0;
+}
+
+static enum drm_mode_status rk3288_mipi_dsi_mode_valid(
+					struct drm_connector *connector,
+					struct drm_display_mode *mode)
+{
+	/*
+	 * The VID_PKT_SIZE field in the DSI_VID_PKT_CFG
+	 * register is 11-bit.
+	 */
+	if (mode->hdisplay > 0x7ff)
+		return MODE_BAD_HVALUE;
+
+	/*
+	 * The V_ACTIVE_LINES field in the DSI_VTIMING_CFG
+	 * register is 11-bit.
+	 */
+	if (mode->vdisplay > 0x7ff)
+		return MODE_BAD_VVALUE;
+
+	return MODE_OK;
+}
+
+static struct dw_mipi_dsi_plat_data rk3288_mipi_dsi_drv_data = {
+	.max_data_lanes = 4,
+	.mode_valid = rk3288_mipi_dsi_mode_valid,
+};
+
+static const struct of_device_id dw_mipi_dsi_dt_ids[] = {
+	{
+	 .compatible = "rockchip,rk3288-mipi-dsi",
+	 .data = &rk3288_mipi_dsi_drv_data,
+	},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, dw_mipi_dsi_dt_ids);
+
+static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
+			     void *data)
+{
+	const struct of_device_id *of_id =
+			of_match_device(dw_mipi_dsi_dt_ids, dev);
+	const struct dw_mipi_dsi_plat_data *pdata = of_id->data;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = data;
+	struct dw_mipi_dsi *dsi;
+	struct resource *res;
+	int ret;
+
+	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
+	if (!dsi)
+		return -ENOMEM;
+
+	dsi->dev = dev;
+	dsi->pdata = pdata;
+
+	ret = rockchip_mipi_parse_dt(dsi);
+	if (ret)
+		return ret;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	dsi->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(dsi->base))
+		return PTR_ERR(dsi->base);
+
+	dsi->pllref_clk = devm_clk_get(dev, "ref");
+	if (IS_ERR(dsi->pllref_clk)) {
+		ret = PTR_ERR(dsi->pllref_clk);
+		dev_err(dev, "Unable to get pll reference clock: %d\n", ret);
+		return ret;
+	}
+
+	dsi->pclk = devm_clk_get(dev, "pclk");
+	if (IS_ERR(dsi->pclk)) {
+		ret = PTR_ERR(dsi->pclk);
+		dev_err(dev, "Unable to get pclk: %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(dsi->pllref_clk);
+	if (ret) {
+		dev_err(dev, "%s: Failed to enable pllref_clk\n", __func__);
+		return ret;
+	}
+
+	ret = dw_mipi_dsi_register(drm, dsi);
+	if (ret) {
+		dev_err(dev, "Failed to register mipi_dsi: %d\n", ret);
+		goto err_pllref;
+	}
+
+	dev_set_drvdata(dev, dsi);
+
+	dsi->dsi_host.ops = &dw_mipi_dsi_host_ops;
+	dsi->dsi_host.dev = dev;
+	return mipi_dsi_host_register(&dsi->dsi_host);
+
+err_pllref:
+	clk_disable_unprepare(dsi->pllref_clk);
+	return ret;
+}
+
+static void dw_mipi_dsi_unbind(struct device *dev, struct device *master,
+	void *data)
+{
+	struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
+
+	mipi_dsi_host_unregister(&dsi->dsi_host);
+	clk_disable_unprepare(dsi->pllref_clk);
+}
+
+static const struct component_ops dw_mipi_dsi_ops = {
+	.bind	= dw_mipi_dsi_bind,
+	.unbind	= dw_mipi_dsi_unbind,
+};
+
+static int dw_mipi_dsi_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &dw_mipi_dsi_ops);
+}
+
+static int dw_mipi_dsi_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &dw_mipi_dsi_ops);
+	return 0;
+}
+
+static struct platform_driver dw_mipi_dsi_driver = {
+	.probe		= dw_mipi_dsi_probe,
+	.remove		= dw_mipi_dsi_remove,
+	.driver		= {
+		.of_match_table = dw_mipi_dsi_dt_ids,
+		.name	= DRIVER_NAME,
+	},
+};
+module_platform_driver(dw_mipi_dsi_driver);
+
+MODULE_DESCRIPTION("ROCKCHIP MIPI DSI host controller driver");
+MODULE_AUTHOR("Chris Zhong <zyw at rock-chips.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 056cac3..46c2a8d 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -818,6 +818,9 @@ int rockchip_drm_crtc_mode_config(struct drm_crtc *crtc,
 	case DRM_MODE_CONNECTOR_HDMIA:
 		VOP_CTRL_SET(vop, hdmi_en, 1);
 		break;
+	case DRM_MODE_CONNECTOR_DSI:
+		VOP_CTRL_SET(vop, mipi_en, 1);
+		break;
 	default:
 		DRM_ERROR("unsupport connector_type[%d]\n", connector_type);
 		return -EINVAL;
-- 
2.6.3



From zyw at rock-chips.com  Wed Jan  6 00:12:54 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Wed,  6 Jan 2016 16:12:54 +0800
Subject: [PATCH v7.2 3/5] drm: rockchip: Support Synopsys DW MIPI DSI
In-Reply-To: <568CB47B.1010205@rock-chips.com>
References: <568CB47B.1010205@rock-chips.com>
Message-ID: <1452067974-29942-1-git-send-email-zyw@rock-chips.com>

Add support for Synopsys DesignWare MIPI DSI controller which is
embedded in the rk3288 SoCs.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
Acked-by: Mark Yao <mark.yao at rock-chips.com>
---

Changes in v7.2:
- make sure disable clk when drm_panel_prepare err (Mark Yao)

Changes in v7:
- modify the config to tristate for modules build (Mark Yao)
- Pass NULL 'name' to drm_encoder_init() to fix compile err (Mark Yao)

Changes in v6:
- move the mipi_en gate to ockchip_drm_crtc_mode_config
- Remove the atomic feature check (Mark Yao)
- Add atomic API support (Heiko St?bne)
- Do not use bridge driver (Thierry Reding)
- Optimization the phy init sequence

Changes in v5: None
Changes in v4: None
Changes in v3: None

 drivers/gpu/drm/rockchip/Kconfig            |   10 +
 drivers/gpu/drm/rockchip/Makefile           |    1 +
 drivers/gpu/drm/rockchip/dw-mipi-dsi.c      | 1194 +++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c |    3 +
 4 files changed, 1208 insertions(+)
 create mode 100644 drivers/gpu/drm/rockchip/dw-mipi-dsi.c

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 35215f6..8573985 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -25,3 +25,13 @@ config ROCKCHIP_DW_HDMI
 	  for the Synopsys DesignWare HDMI driver. If you want to
 	  enable HDMI on RK3288 based SoC, you should selet this
 	  option.
+
+config ROCKCHIP_DW_MIPI_DSI
+	tristate "Rockchip specific extensions for Synopsys DW MIPI DSI"
+	depends on DRM_ROCKCHIP
+	select DRM_MIPI_DSI
+	help
+	 This selects support for Rockchip SoC specific extensions
+	 for the Synopsys DesignWare HDMI driver. If you want to
+	 enable MIPI DSI on RK3288 based SoC, you should selet this
+	 option.
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index a9d380f..d1dc0f7 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -6,6 +6,7 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
 		rockchip_drm_gem.o
 
 obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
+obj-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
 
 obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_drm_vop.o \
 				rockchip_vop_reg.o
diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
new file mode 100644
index 0000000..7bfe243
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
@@ -0,0 +1,1194 @@
+/*
+ * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/iopoll.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drmP.h>
+#include <video/mipi_display.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_vop.h"
+
+#define DRIVER_NAME    "dw-mipi-dsi"
+
+#define GRF_SOC_CON6                    0x025c
+#define DSI0_SEL_VOP_LIT                (1 << 6)
+#define DSI1_SEL_VOP_LIT                (1 << 9)
+
+#define DSI_VERSION			0x00
+#define DSI_PWR_UP			0x04
+#define RESET				0
+#define POWERUP				BIT(0)
+
+#define DSI_CLKMGR_CFG			0x08
+#define TO_CLK_DIVIDSION(div)		(((div) & 0xff) << 8)
+#define TX_ESC_CLK_DIVIDSION(div)	(((div) & 0xff) << 0)
+
+#define DSI_DPI_VCID			0x0c
+#define DPI_VID(vid)			(((vid) & 0x3) << 0)
+
+#define DSI_DPI_COLOR_CODING		0x10
+#define EN18_LOOSELY			BIT(8)
+#define DPI_COLOR_CODING_16BIT_1	0x0
+#define DPI_COLOR_CODING_16BIT_2	0x1
+#define DPI_COLOR_CODING_16BIT_3	0x2
+#define DPI_COLOR_CODING_18BIT_1	0x3
+#define DPI_COLOR_CODING_18BIT_2	0x4
+#define DPI_COLOR_CODING_24BIT		0x5
+
+#define DSI_DPI_CFG_POL			0x14
+#define COLORM_ACTIVE_LOW		BIT(4)
+#define SHUTD_ACTIVE_LOW		BIT(3)
+#define HSYNC_ACTIVE_LOW		BIT(2)
+#define VSYNC_ACTIVE_LOW		BIT(1)
+#define DATAEN_ACTIVE_LOW		BIT(0)
+
+#define DSI_DPI_LP_CMD_TIM		0x18
+#define OUTVACT_LPCMD_TIME(p)		(((p) & 0xff) << 16)
+#define INVACT_LPCMD_TIME(p)		((p) & 0xff)
+
+#define DSI_DBI_CFG			0x20
+#define DSI_DBI_CMDSIZE			0x28
+
+#define DSI_PCKHDL_CFG			0x2c
+#define EN_CRC_RX			BIT(4)
+#define EN_ECC_RX			BIT(3)
+#define EN_BTA				BIT(2)
+#define EN_EOTP_RX			BIT(1)
+#define EN_EOTP_TX			BIT(0)
+
+#define DSI_MODE_CFG			0x34
+#define ENABLE_VIDEO_MODE		0
+#define ENABLE_CMD_MODE			BIT(0)
+
+#define DSI_VID_MODE_CFG		0x38
+#define FRAME_BTA_ACK			BIT(14)
+#define ENABLE_LOW_POWER		(0x3f << 8)
+#define ENABLE_LOW_POWER_MASK		(0x3f << 8)
+#define VID_MODE_TYPE_BURST_SYNC_PULSES		0x2
+#define VID_MODE_TYPE_MASK			0x3
+
+#define DSI_VID_PKT_SIZE		0x3c
+#define VID_PKT_SIZE(p)			(((p) & 0x3fff) << 0)
+#define VID_PKT_MAX_SIZE		0x3fff
+
+#define DSI_VID_HSA_TIME		0x48
+#define DSI_VID_HBP_TIME		0x4c
+#define DSI_VID_HLINE_TIME		0x50
+#define DSI_VID_VSA_LINES		0x54
+#define DSI_VID_VBP_LINES		0x58
+#define DSI_VID_VFP_LINES		0x5c
+#define DSI_VID_VACTIVE_LINES		0x60
+#define DSI_CMD_MODE_CFG		0x68
+#define MAX_RD_PKT_SIZE_LP		BIT(24)
+#define DCS_LW_TX_LP			BIT(19)
+#define DCS_SR_0P_TX_LP			BIT(18)
+#define DCS_SW_1P_TX_LP			BIT(17)
+#define DCS_SW_0P_TX_LP			BIT(16)
+#define GEN_LW_TX_LP			BIT(14)
+#define GEN_SR_2P_TX_LP			BIT(13)
+#define GEN_SR_1P_TX_LP			BIT(12)
+#define GEN_SR_0P_TX_LP			BIT(11)
+#define GEN_SW_2P_TX_LP			BIT(10)
+#define GEN_SW_1P_TX_LP			BIT(9)
+#define GEN_SW_0P_TX_LP			BIT(8)
+#define EN_ACK_RQST			BIT(1)
+#define EN_TEAR_FX			BIT(0)
+
+#define CMD_MODE_ALL_LP			(MAX_RD_PKT_SIZE_LP | \
+					 DCS_LW_TX_LP | \
+					 DCS_SR_0P_TX_LP | \
+					 DCS_SW_1P_TX_LP | \
+					 DCS_SW_0P_TX_LP | \
+					 GEN_LW_TX_LP | \
+					 GEN_SR_2P_TX_LP | \
+					 GEN_SR_1P_TX_LP | \
+					 GEN_SR_0P_TX_LP | \
+					 GEN_SW_2P_TX_LP | \
+					 GEN_SW_1P_TX_LP | \
+					 GEN_SW_0P_TX_LP)
+
+#define DSI_GEN_HDR			0x6c
+#define GEN_HDATA(data)			(((data) & 0xffff) << 8)
+#define GEN_HDATA_MASK			(0xffff << 8)
+#define GEN_HTYPE(type)			(((type) & 0xff) << 0)
+#define GEN_HTYPE_MASK			0xff
+
+#define DSI_GEN_PLD_DATA		0x70
+
+#define DSI_CMD_PKT_STATUS		0x74
+#define GEN_CMD_EMPTY			BIT(0)
+#define GEN_CMD_FULL			BIT(1)
+#define GEN_PLD_W_EMPTY			BIT(2)
+#define GEN_PLD_W_FULL			BIT(3)
+#define GEN_PLD_R_EMPTY			BIT(4)
+#define GEN_PLD_R_FULL			BIT(5)
+#define GEN_RD_CMD_BUSY			BIT(6)
+
+#define DSI_TO_CNT_CFG			0x78
+#define HSTX_TO_CNT(p)			(((p) & 0xffff) << 16)
+#define LPRX_TO_CNT(p)			((p) & 0xffff)
+
+#define DSI_BTA_TO_CNT			0x8c
+
+#define DSI_LPCLK_CTRL			0x94
+#define AUTO_CLKLANE_CTRL		BIT(1)
+#define PHY_TXREQUESTCLKHS		BIT(0)
+
+#define DSI_PHY_TMR_LPCLK_CFG		0x98
+#define PHY_CLKHS2LP_TIME(lbcc)		(((lbcc) & 0x3ff) << 16)
+#define PHY_CLKLP2HS_TIME(lbcc)		((lbcc) & 0x3ff)
+
+#define DSI_PHY_TMR_CFG			0x9c
+#define PHY_HS2LP_TIME(lbcc)		(((lbcc) & 0xff) << 24)
+#define PHY_LP2HS_TIME(lbcc)		(((lbcc) & 0xff) << 16)
+#define MAX_RD_TIME(lbcc)		((lbcc) & 0x7fff)
+
+#define DSI_PHY_RSTZ			0xa0
+#define PHY_DISFORCEPLL			0
+#define PHY_ENFORCEPLL			BIT(3)
+#define PHY_DISABLECLK			0
+#define PHY_ENABLECLK			BIT(2)
+#define PHY_RSTZ			0
+#define PHY_UNRSTZ			BIT(1)
+#define PHY_SHUTDOWNZ			0
+#define PHY_UNSHUTDOWNZ			BIT(0)
+
+#define DSI_PHY_IF_CFG			0xa4
+#define N_LANES(n)			((((n) - 1) & 0x3) << 0)
+#define PHY_STOP_WAIT_TIME(cycle)	(((cycle) & 0xff) << 8)
+
+#define DSI_PHY_STATUS			0xb0
+#define LOCK				BIT(0)
+#define STOP_STATE_CLK_LANE		BIT(2)
+
+#define DSI_PHY_TST_CTRL0		0xb4
+#define PHY_TESTCLK			BIT(1)
+#define PHY_UNTESTCLK			0
+#define PHY_TESTCLR			BIT(0)
+#define PHY_UNTESTCLR			0
+
+#define DSI_PHY_TST_CTRL1		0xb8
+#define PHY_TESTEN			BIT(16)
+#define PHY_UNTESTEN			0
+#define PHY_TESTDOUT(n)			(((n) & 0xff) << 8)
+#define PHY_TESTDIN(n)			(((n) & 0xff) << 0)
+
+#define DSI_INT_ST0			0xbc
+#define DSI_INT_ST1			0xc0
+#define DSI_INT_MSK0			0xc4
+#define DSI_INT_MSK1			0xc8
+
+#define PHY_STATUS_TIMEOUT_US		10000
+#define CMD_PKT_STATUS_TIMEOUT_US	20000
+
+#define BYPASS_VCO_RANGE	BIT(7)
+#define VCO_RANGE_CON_SEL(val)	(((val) & 0x7) << 3)
+#define VCO_IN_CAP_CON_DEFAULT	(0x0 << 1)
+#define VCO_IN_CAP_CON_LOW	(0x1 << 1)
+#define VCO_IN_CAP_CON_HIGH	(0x2 << 1)
+#define REF_BIAS_CUR_SEL	BIT(0)
+
+#define CP_CURRENT_3MA		BIT(3)
+#define CP_PROGRAM_EN		BIT(7)
+#define LPF_PROGRAM_EN		BIT(6)
+#define LPF_RESISTORS_20_KOHM	0
+
+#define HSFREQRANGE_SEL(val)	(((val) & 0x3f) << 1)
+
+#define INPUT_DIVIDER(val)	((val - 1) & 0x7f)
+#define LOW_PROGRAM_EN		0
+#define HIGH_PROGRAM_EN		BIT(7)
+#define LOOP_DIV_LOW_SEL(val)	((val - 1) & 0x1f)
+#define LOOP_DIV_HIGH_SEL(val)	(((val - 1) >> 5) & 0x1f)
+#define PLL_LOOP_DIV_EN		BIT(5)
+#define PLL_INPUT_DIV_EN	BIT(4)
+
+#define POWER_CONTROL		BIT(6)
+#define INTERNAL_REG_CURRENT	BIT(3)
+#define BIAS_BLOCK_ON		BIT(2)
+#define BANDGAP_ON		BIT(0)
+
+#define TER_RESISTOR_HIGH	BIT(7)
+#define	TER_RESISTOR_LOW	0
+#define LEVEL_SHIFTERS_ON	BIT(6)
+#define TER_CAL_DONE		BIT(5)
+#define SETRD_MAX		(0x7 << 2)
+#define POWER_MANAGE		BIT(1)
+#define TER_RESISTORS_ON	BIT(0)
+
+#define BIASEXTR_SEL(val)	((val) & 0x7)
+#define BANDGAP_SEL(val)	((val) & 0x7)
+#define TLP_PROGRAM_EN		BIT(7)
+#define THS_PRE_PROGRAM_EN	BIT(7)
+#define THS_ZERO_PROGRAM_EN	BIT(6)
+
+enum {
+	BANDGAP_97_07,
+	BANDGAP_98_05,
+	BANDGAP_99_02,
+	BANDGAP_100_00,
+	BANDGAP_93_17,
+	BANDGAP_94_15,
+	BANDGAP_95_12,
+	BANDGAP_96_10,
+};
+
+enum {
+	BIASEXTR_87_1,
+	BIASEXTR_91_5,
+	BIASEXTR_95_9,
+	BIASEXTR_100,
+	BIASEXTR_105_94,
+	BIASEXTR_111_88,
+	BIASEXTR_118_8,
+	BIASEXTR_127_7,
+};
+
+struct dw_mipi_dsi_plat_data {
+	unsigned int max_data_lanes;
+	enum drm_mode_status (*mode_valid)(struct drm_connector *connector,
+					   struct drm_display_mode *mode);
+};
+
+struct dw_mipi_dsi {
+	struct drm_encoder encoder;
+	struct drm_connector connector;
+	struct mipi_dsi_host dsi_host;
+	struct drm_panel *panel;
+	struct device *dev;
+	struct regmap *grf_regmap;
+	void __iomem *base;
+
+	struct clk *pllref_clk;
+	struct clk *pclk;
+
+	unsigned int lane_mbps; /* per lane */
+	u32 channel;
+	u32 lanes;
+	u32 format;
+	u16 input_div;
+	u16 feedback_div;
+	struct drm_display_mode *mode;
+
+	const struct dw_mipi_dsi_plat_data *pdata;
+};
+
+enum dw_mipi_dsi_mode {
+	DW_MIPI_DSI_CMD_MODE,
+	DW_MIPI_DSI_VID_MODE,
+};
+
+struct dphy_pll_testdin_map {
+	unsigned int max_mbps;
+	u8 testdin;
+};
+
+/* The table is based on 27MHz DPHY pll reference clock. */
+static const struct dphy_pll_testdin_map dptdin_map[] = {
+	{  90, 0x00}, { 100, 0x10}, { 110, 0x20}, { 130, 0x01},
+	{ 140, 0x11}, { 150, 0x21}, { 170, 0x02}, { 180, 0x12},
+	{ 200, 0x22}, { 220, 0x03}, { 240, 0x13}, { 250, 0x23},
+	{ 270, 0x04}, { 300, 0x14}, { 330, 0x05}, { 360, 0x15},
+	{ 400, 0x25}, { 450, 0x06}, { 500, 0x16}, { 550, 0x07},
+	{ 600, 0x17}, { 650, 0x08}, { 700, 0x18}, { 750, 0x09},
+	{ 800, 0x19}, { 850, 0x29}, { 900, 0x39}, { 950, 0x0a},
+	{1000, 0x1a}, {1050, 0x2a}, {1100, 0x3a}, {1150, 0x0b},
+	{1200, 0x1b}, {1250, 0x2b}, {1300, 0x3b}, {1350, 0x0c},
+	{1400, 0x1c}, {1450, 0x2c}, {1500, 0x3c}
+};
+
+static int max_mbps_to_testdin(unsigned int max_mbps)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(dptdin_map); i++)
+		if (dptdin_map[i].max_mbps > max_mbps)
+			return dptdin_map[i].testdin;
+
+	return -EINVAL;
+}
+
+/*
+ * The controller should generate 2 frames before
+ * preparing the peripheral.
+ */
+static void dw_mipi_dsi_wait_for_two_frames(struct dw_mipi_dsi *dsi)
+{
+	int refresh, two_frames;
+
+	refresh = drm_mode_vrefresh(dsi->mode);
+	two_frames = DIV_ROUND_UP(MSEC_PER_SEC, refresh) * 2;
+	msleep(two_frames);
+}
+
+static inline struct dw_mipi_dsi *host_to_dsi(struct mipi_dsi_host *host)
+{
+	return container_of(host, struct dw_mipi_dsi, dsi_host);
+}
+
+static inline struct dw_mipi_dsi *con_to_dsi(struct drm_connector *con)
+{
+	return container_of(con, struct dw_mipi_dsi, connector);
+}
+
+static inline struct dw_mipi_dsi *encoder_to_dsi(struct drm_encoder *encoder)
+{
+	return container_of(encoder, struct dw_mipi_dsi, encoder);
+}
+static inline void dsi_write(struct dw_mipi_dsi *dsi, u32 reg, u32 val)
+{
+	writel(val, dsi->base + reg);
+}
+
+static inline u32 dsi_read(struct dw_mipi_dsi *dsi, u32 reg)
+{
+	return readl(dsi->base + reg);
+}
+
+static void dw_mipi_dsi_phy_write(struct dw_mipi_dsi *dsi, u8 test_code,
+				 u8 test_data)
+{
+	/*
+	 * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content
+	 * is latched internally as the current test code. Test data is
+	 * programmed internally by rising edge on TESTCLK.
+	 */
+	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR);
+
+	dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_TESTEN | PHY_TESTDOUT(0) |
+					  PHY_TESTDIN(test_code));
+
+	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLK | PHY_UNTESTCLR);
+
+	dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_UNTESTEN | PHY_TESTDOUT(0) |
+					  PHY_TESTDIN(test_data));
+
+	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR);
+}
+
+static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi)
+{
+	int ret, testdin, vco, val;
+
+	vco = (dsi->lane_mbps < 200) ? 0 : (dsi->lane_mbps + 100) / 200;
+
+	testdin = max_mbps_to_testdin(dsi->lane_mbps);
+	if (testdin < 0) {
+		dev_err(dsi->dev,
+			"failed to get testdin for %dmbps lane clock\n",
+			dsi->lane_mbps);
+		return testdin;
+	}
+
+	dsi_write(dsi, DSI_PWR_UP, POWERUP);
+
+	dw_mipi_dsi_phy_write(dsi, 0x10, BYPASS_VCO_RANGE |
+					 VCO_RANGE_CON_SEL(vco) |
+					 VCO_IN_CAP_CON_LOW |
+					 REF_BIAS_CUR_SEL);
+
+	dw_mipi_dsi_phy_write(dsi, 0x11, CP_CURRENT_3MA);
+	dw_mipi_dsi_phy_write(dsi, 0x12, CP_PROGRAM_EN | LPF_PROGRAM_EN |
+					 LPF_RESISTORS_20_KOHM);
+
+	dw_mipi_dsi_phy_write(dsi, 0x44, HSFREQRANGE_SEL(testdin));
+
+	dw_mipi_dsi_phy_write(dsi, 0x19, PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN);
+	dw_mipi_dsi_phy_write(dsi, 0x17, INPUT_DIVIDER(dsi->input_div));
+	dw_mipi_dsi_phy_write(dsi, 0x18, LOOP_DIV_LOW_SEL(dsi->feedback_div) |
+					 LOW_PROGRAM_EN);
+	dw_mipi_dsi_phy_write(dsi, 0x18, LOOP_DIV_HIGH_SEL(dsi->feedback_div) |
+					 HIGH_PROGRAM_EN);
+
+	dw_mipi_dsi_phy_write(dsi, 0x20, POWER_CONTROL | INTERNAL_REG_CURRENT |
+					 BIAS_BLOCK_ON | BANDGAP_ON);
+
+	dw_mipi_dsi_phy_write(dsi, 0x21, TER_RESISTOR_LOW | TER_CAL_DONE |
+					 SETRD_MAX | TER_RESISTORS_ON);
+	dw_mipi_dsi_phy_write(dsi, 0x21, TER_RESISTOR_HIGH | LEVEL_SHIFTERS_ON |
+					 SETRD_MAX | POWER_MANAGE |
+					 TER_RESISTORS_ON);
+
+	dw_mipi_dsi_phy_write(dsi, 0x22, LOW_PROGRAM_EN |
+					 BIASEXTR_SEL(BIASEXTR_127_7));
+	dw_mipi_dsi_phy_write(dsi, 0x22, HIGH_PROGRAM_EN |
+					 BANDGAP_SEL(BANDGAP_96_10));
+
+	dw_mipi_dsi_phy_write(dsi, 0x70, TLP_PROGRAM_EN | 0xf);
+	dw_mipi_dsi_phy_write(dsi, 0x71, THS_PRE_PROGRAM_EN | 0x55);
+	dw_mipi_dsi_phy_write(dsi, 0x72, THS_ZERO_PROGRAM_EN | 0xa);
+
+	dsi_write(dsi, DSI_PHY_RSTZ, PHY_ENFORCEPLL | PHY_ENABLECLK |
+				     PHY_UNRSTZ | PHY_UNSHUTDOWNZ);
+
+
+	ret = readx_poll_timeout(readl, dsi->base + DSI_PHY_STATUS,
+				 val, val & LOCK, 1000, PHY_STATUS_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dsi->dev, "failed to wait for phy lock state\n");
+		return ret;
+	}
+
+	ret = readx_poll_timeout(readl, dsi->base + DSI_PHY_STATUS,
+				 val, val & STOP_STATE_CLK_LANE, 1000,
+				 PHY_STATUS_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dsi->dev,
+			"failed to wait for phy clk lane stop state\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static int dw_mipi_dsi_get_lane_bps(struct dw_mipi_dsi *dsi)
+{
+	unsigned int bpp, i, pre;
+	unsigned long mpclk, pllref, tmp;
+	unsigned int m = 1, n = 1, target_mbps = 1000;
+	unsigned int max_mbps = dptdin_map[ARRAY_SIZE(dptdin_map) - 1].max_mbps;
+
+	bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
+	if (bpp < 0) {
+		dev_err(dsi->dev, "failed to get bpp for pixel format %d\n",
+			dsi->format);
+		return bpp;
+	}
+
+	mpclk = DIV_ROUND_UP(dsi->mode->clock, MSEC_PER_SEC);
+	if (mpclk) {
+		/* take 1 / 0.9, since mbps must big than bandwidth of RGB */
+		tmp = mpclk * (bpp / dsi->lanes) * 10 / 9;
+		if (tmp < max_mbps)
+			target_mbps = tmp;
+		else
+			dev_err(dsi->dev, "DPHY clock frequency is out of range\n");
+	}
+
+	pllref = DIV_ROUND_UP(clk_get_rate(dsi->pllref_clk), USEC_PER_SEC);
+	tmp = pllref;
+
+	for (i = 1; i < 6; i++) {
+		pre = pllref / i;
+		if ((tmp > (target_mbps % pre)) && (target_mbps / pre < 512)) {
+			tmp = target_mbps % pre;
+			n = i;
+			m = target_mbps / pre;
+		}
+		if (tmp == 0)
+			break;
+	}
+
+	dsi->lane_mbps = pllref / n * m;
+	dsi->input_div = n;
+	dsi->feedback_div = m;
+
+	return 0;
+}
+
+static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host,
+				   struct mipi_dsi_device *device)
+{
+	struct dw_mipi_dsi *dsi = host_to_dsi(host);
+
+	if (device->lanes > dsi->pdata->max_data_lanes) {
+		dev_err(dsi->dev, "the number of data lanes(%u) is too many\n",
+				device->lanes);
+		return -EINVAL;
+	}
+
+	if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) ||
+	    !(device->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)) {
+		dev_err(dsi->dev, "device mode is unsupported\n");
+		return -EINVAL;
+	}
+
+	dsi->lanes = device->lanes;
+	dsi->channel = device->channel;
+	dsi->format = device->format;
+	dsi->panel = of_drm_find_panel(device->dev.of_node);
+	if (dsi->panel)
+		return drm_panel_attach(dsi->panel, &dsi->connector);
+
+	return -EINVAL;
+}
+
+static int dw_mipi_dsi_host_detach(struct mipi_dsi_host *host,
+				   struct mipi_dsi_device *device)
+{
+	struct dw_mipi_dsi *dsi = host_to_dsi(host);
+
+	drm_panel_detach(dsi->panel);
+
+	return 0;
+}
+
+static int dw_mipi_dsi_gen_pkt_hdr_write(struct dw_mipi_dsi *dsi, u32 val)
+{
+	int ret;
+
+	ret = readx_poll_timeout(readl, dsi->base + DSI_CMD_PKT_STATUS,
+				 val, !(val & GEN_CMD_FULL), 1000,
+				 CMD_PKT_STATUS_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dsi->dev, "failed to get available command FIFO\n");
+		return ret;
+	}
+
+	dsi_write(dsi, DSI_GEN_HDR, val);
+
+	ret = readx_poll_timeout(readl, dsi->base + DSI_CMD_PKT_STATUS,
+				 val, val & (GEN_CMD_EMPTY | GEN_PLD_W_EMPTY),
+				 1000, CMD_PKT_STATUS_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dsi->dev, "failed to write command FIFO\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int dw_mipi_dsi_dcs_short_write(struct dw_mipi_dsi *dsi,
+				       const struct mipi_dsi_msg *msg)
+{
+	const u16 *tx_buf = msg->tx_buf;
+	u32 val = GEN_HDATA(*tx_buf) | GEN_HTYPE(msg->type);
+
+	if (msg->tx_len > 2) {
+		dev_err(dsi->dev, "too long tx buf length %zu for short write\n",
+			msg->tx_len);
+		return -EINVAL;
+	}
+
+	return dw_mipi_dsi_gen_pkt_hdr_write(dsi, val);
+}
+
+static int dw_mipi_dsi_dcs_long_write(struct dw_mipi_dsi *dsi,
+				      const struct mipi_dsi_msg *msg)
+{
+	const u32 *tx_buf = msg->tx_buf;
+	int len = msg->tx_len, pld_data_bytes = sizeof(*tx_buf), ret;
+	u32 val = GEN_HDATA(msg->tx_len) | GEN_HTYPE(msg->type);
+	u32 remainder = 0;
+
+	if (msg->tx_len < 3) {
+		dev_err(dsi->dev, "wrong tx buf length %zu for long write\n",
+			msg->tx_len);
+		return -EINVAL;
+	}
+
+	while (DIV_ROUND_UP(len, pld_data_bytes)) {
+		if (len < pld_data_bytes) {
+			memcpy(&remainder, tx_buf, len);
+			dsi_write(dsi, DSI_GEN_PLD_DATA, remainder);
+			len = 0;
+		} else {
+			dsi_write(dsi, DSI_GEN_PLD_DATA, *tx_buf);
+			tx_buf++;
+			len -= pld_data_bytes;
+		}
+
+		ret = readx_poll_timeout(readl, dsi->base + DSI_CMD_PKT_STATUS,
+					 val, !(val & GEN_PLD_W_FULL), 1000,
+					 CMD_PKT_STATUS_TIMEOUT_US);
+		if (ret < 0) {
+			dev_err(dsi->dev,
+				"failed to get available write payload FIFO\n");
+			return ret;
+		}
+	}
+
+	return dw_mipi_dsi_gen_pkt_hdr_write(dsi, val);
+}
+
+static ssize_t dw_mipi_dsi_host_transfer(struct mipi_dsi_host *host,
+					 const struct mipi_dsi_msg *msg)
+{
+	struct dw_mipi_dsi *dsi = host_to_dsi(host);
+	int ret;
+
+	switch (msg->type) {
+	case MIPI_DSI_DCS_SHORT_WRITE:
+	case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
+	case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
+		ret = dw_mipi_dsi_dcs_short_write(dsi, msg);
+		break;
+	case MIPI_DSI_DCS_LONG_WRITE:
+		ret = dw_mipi_dsi_dcs_long_write(dsi, msg);
+		break;
+	default:
+		dev_err(dsi->dev, "unsupported message type\n");
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static const struct mipi_dsi_host_ops dw_mipi_dsi_host_ops = {
+	.attach = dw_mipi_dsi_host_attach,
+	.detach = dw_mipi_dsi_host_detach,
+	.transfer = dw_mipi_dsi_host_transfer,
+};
+
+static void dw_mipi_dsi_video_mode_config(struct dw_mipi_dsi *dsi)
+{
+	u32 val;
+
+	val = VID_MODE_TYPE_BURST_SYNC_PULSES | ENABLE_LOW_POWER;
+
+	dsi_write(dsi, DSI_VID_MODE_CFG, val);
+}
+
+static void dw_mipi_dsi_set_mode(struct dw_mipi_dsi *dsi,
+				 enum dw_mipi_dsi_mode mode)
+{
+	if (mode == DW_MIPI_DSI_CMD_MODE) {
+		dsi_write(dsi, DSI_PWR_UP, RESET);
+		dsi_write(dsi, DSI_MODE_CFG, ENABLE_CMD_MODE);
+		dsi_write(dsi, DSI_PWR_UP, POWERUP);
+	} else {
+		dsi_write(dsi, DSI_PWR_UP, RESET);
+		dsi_write(dsi, DSI_MODE_CFG, ENABLE_VIDEO_MODE);
+		dw_mipi_dsi_video_mode_config(dsi);
+		dsi_write(dsi, DSI_PWR_UP, POWERUP);
+	}
+}
+
+static void dw_mipi_dsi_disable(struct dw_mipi_dsi *dsi)
+{
+	dsi_write(dsi, DSI_PWR_UP, RESET);
+	dsi_write(dsi, DSI_PHY_RSTZ, PHY_RSTZ);
+}
+
+static void dw_mipi_dsi_init(struct dw_mipi_dsi *dsi)
+{
+	dsi_write(dsi, DSI_PWR_UP, RESET);
+	dsi_write(dsi, DSI_PHY_RSTZ, PHY_DISFORCEPLL | PHY_DISABLECLK
+		  | PHY_RSTZ | PHY_SHUTDOWNZ);
+	dsi_write(dsi, DSI_CLKMGR_CFG, TO_CLK_DIVIDSION(10) |
+		  TX_ESC_CLK_DIVIDSION(7));
+	dsi_write(dsi, DSI_LPCLK_CTRL, PHY_TXREQUESTCLKHS);
+}
+
+static void dw_mipi_dsi_dpi_config(struct dw_mipi_dsi *dsi,
+				   struct drm_display_mode *mode)
+{
+	u32 val = 0, color = 0;
+
+	switch (dsi->format) {
+	case MIPI_DSI_FMT_RGB888:
+		color = DPI_COLOR_CODING_24BIT;
+		break;
+	case MIPI_DSI_FMT_RGB666:
+		color = DPI_COLOR_CODING_18BIT_2 | EN18_LOOSELY;
+		break;
+	case MIPI_DSI_FMT_RGB666_PACKED:
+		color = DPI_COLOR_CODING_18BIT_1;
+		break;
+	case MIPI_DSI_FMT_RGB565:
+		color = DPI_COLOR_CODING_16BIT_1;
+		break;
+	}
+
+	if (!(mode->flags & DRM_MODE_FLAG_PVSYNC))
+		val |= VSYNC_ACTIVE_LOW;
+	if (!(mode->flags & DRM_MODE_FLAG_PHSYNC))
+		val |= HSYNC_ACTIVE_LOW;
+
+	dsi_write(dsi, DSI_DPI_VCID, DPI_VID(dsi->channel));
+	dsi_write(dsi, DSI_DPI_COLOR_CODING, color);
+	dsi_write(dsi, DSI_DPI_CFG_POL, val);
+	dsi_write(dsi, DSI_DPI_LP_CMD_TIM, OUTVACT_LPCMD_TIME(4)
+		  | INVACT_LPCMD_TIME(4));
+}
+
+static void dw_mipi_dsi_packet_handler_config(struct dw_mipi_dsi *dsi)
+{
+	dsi_write(dsi, DSI_PCKHDL_CFG, EN_CRC_RX | EN_ECC_RX | EN_BTA);
+}
+
+static void dw_mipi_dsi_video_packet_config(struct dw_mipi_dsi *dsi,
+					    struct drm_display_mode *mode)
+{
+	dsi_write(dsi, DSI_VID_PKT_SIZE, VID_PKT_SIZE(mode->hdisplay));
+}
+
+static void dw_mipi_dsi_command_mode_config(struct dw_mipi_dsi *dsi)
+{
+	dsi_write(dsi, DSI_TO_CNT_CFG, HSTX_TO_CNT(1000) | LPRX_TO_CNT(1000));
+	dsi_write(dsi, DSI_BTA_TO_CNT, 0xd00);
+	dsi_write(dsi, DSI_CMD_MODE_CFG, CMD_MODE_ALL_LP);
+	dsi_write(dsi, DSI_MODE_CFG, ENABLE_CMD_MODE);
+}
+
+/* Get lane byte clock cycles. */
+static u32 dw_mipi_dsi_get_hcomponent_lbcc(struct dw_mipi_dsi *dsi,
+					   u32 hcomponent)
+{
+	u32 frac, lbcc;
+
+	lbcc = hcomponent * dsi->lane_mbps * MSEC_PER_SEC / 8;
+
+	frac = lbcc % dsi->mode->clock;
+	lbcc = lbcc / dsi->mode->clock;
+	if (frac)
+		lbcc++;
+
+	return lbcc;
+}
+
+static void dw_mipi_dsi_line_timer_config(struct dw_mipi_dsi *dsi)
+{
+	u32 htotal, hsa, hbp, lbcc;
+	struct drm_display_mode *mode = dsi->mode;
+
+	htotal = mode->htotal;
+	hsa = mode->hsync_end - mode->hsync_start;
+	hbp = mode->htotal - mode->hsync_end;
+
+	lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, htotal);
+	dsi_write(dsi, DSI_VID_HLINE_TIME, lbcc);
+
+	lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, hsa);
+	dsi_write(dsi, DSI_VID_HSA_TIME, lbcc);
+
+	lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, hbp);
+	dsi_write(dsi, DSI_VID_HBP_TIME, lbcc);
+}
+
+static void dw_mipi_dsi_vertical_timing_config(struct dw_mipi_dsi *dsi)
+{
+	u32 vactive, vsa, vfp, vbp;
+	struct drm_display_mode *mode = dsi->mode;
+
+	vactive = mode->vdisplay;
+	vsa = mode->vsync_end - mode->vsync_start;
+	vfp = mode->vsync_start - mode->vdisplay;
+	vbp = mode->vtotal - mode->vsync_end;
+
+	dsi_write(dsi, DSI_VID_VACTIVE_LINES, vactive);
+	dsi_write(dsi, DSI_VID_VSA_LINES, vsa);
+	dsi_write(dsi, DSI_VID_VFP_LINES, vfp);
+	dsi_write(dsi, DSI_VID_VBP_LINES, vbp);
+}
+
+static void dw_mipi_dsi_dphy_timing_config(struct dw_mipi_dsi *dsi)
+{
+	dsi_write(dsi, DSI_PHY_TMR_CFG, PHY_HS2LP_TIME(0x40)
+		  | PHY_LP2HS_TIME(0x40) | MAX_RD_TIME(10000));
+
+	dsi_write(dsi, DSI_PHY_TMR_LPCLK_CFG, PHY_CLKHS2LP_TIME(0x40)
+		  | PHY_CLKLP2HS_TIME(0x40));
+}
+
+static void dw_mipi_dsi_dphy_interface_config(struct dw_mipi_dsi *dsi)
+{
+	dsi_write(dsi, DSI_PHY_IF_CFG, PHY_STOP_WAIT_TIME(0x20) |
+		  N_LANES(dsi->lanes));
+}
+
+static void dw_mipi_dsi_clear_err(struct dw_mipi_dsi *dsi)
+{
+	dsi_read(dsi, DSI_INT_ST0);
+	dsi_read(dsi, DSI_INT_ST1);
+	dsi_write(dsi, DSI_INT_MSK0, 0);
+	dsi_write(dsi, DSI_INT_MSK1, 0);
+}
+
+static void dw_mipi_dsi_encoder_mode_set(struct drm_encoder *encoder,
+					struct drm_display_mode *mode,
+					struct drm_display_mode *adjusted_mode)
+{
+	struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
+	int ret;
+
+	dsi->mode = adjusted_mode;
+
+	ret = dw_mipi_dsi_get_lane_bps(dsi);
+	if (ret < 0)
+		return;
+
+	if (clk_prepare_enable(dsi->pclk)) {
+		dev_err(dsi->dev, "%s: Failed to enable pclk\n", __func__);
+		return;
+	}
+
+	dw_mipi_dsi_init(dsi);
+	dw_mipi_dsi_dpi_config(dsi, mode);
+	dw_mipi_dsi_packet_handler_config(dsi);
+	dw_mipi_dsi_video_mode_config(dsi);
+	dw_mipi_dsi_video_packet_config(dsi, mode);
+	dw_mipi_dsi_command_mode_config(dsi);
+	dw_mipi_dsi_line_timer_config(dsi);
+	dw_mipi_dsi_vertical_timing_config(dsi);
+	dw_mipi_dsi_dphy_timing_config(dsi);
+	dw_mipi_dsi_dphy_interface_config(dsi);
+	dw_mipi_dsi_clear_err(dsi);
+	if (drm_panel_prepare(dsi->panel))
+		dev_err(dsi->dev, "failed to prepare panel\n");
+
+	clk_disable_unprepare(dsi->pclk);
+}
+
+static void dw_mipi_dsi_encoder_disable(struct drm_encoder *encoder)
+{
+	struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
+
+	drm_panel_disable(dsi->panel);
+
+	if (clk_prepare_enable(dsi->pclk)) {
+		dev_err(dsi->dev, "%s: Failed to enable pclk\n", __func__);
+		return;
+	}
+
+	dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_CMD_MODE);
+	drm_panel_unprepare(dsi->panel);
+	dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_VID_MODE);
+
+	/*
+	 * This is necessary to make sure the peripheral will be driven
+	 * normally when the display is enabled again later.
+	 */
+	msleep(120);
+
+	dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_CMD_MODE);
+	dw_mipi_dsi_disable(dsi);
+	clk_disable_unprepare(dsi->pclk);
+}
+
+static bool dw_mipi_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
+					const struct drm_display_mode *mode,
+					struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static void dw_mipi_dsi_encoder_commit(struct drm_encoder *encoder)
+{
+	struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
+	int mux  = rockchip_drm_encoder_get_mux_id(dsi->dev->of_node, encoder);
+	u32 interface_pix_fmt;
+	u32 val;
+
+	if (clk_prepare_enable(dsi->pclk)) {
+		dev_err(dsi->dev, "%s: Failed to enable pclk\n", __func__);
+		return;
+	}
+
+	dw_mipi_dsi_phy_init(dsi);
+	dw_mipi_dsi_wait_for_two_frames(dsi);
+
+	dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_VID_MODE);
+	drm_panel_enable(dsi->panel);
+
+	clk_disable_unprepare(dsi->pclk);
+
+	switch (dsi->format) {
+	case MIPI_DSI_FMT_RGB888:
+		interface_pix_fmt = ROCKCHIP_OUT_MODE_P888;
+		break;
+	case MIPI_DSI_FMT_RGB666:
+		interface_pix_fmt = ROCKCHIP_OUT_MODE_P666;
+		break;
+	case MIPI_DSI_FMT_RGB565:
+		interface_pix_fmt = ROCKCHIP_OUT_MODE_P565;
+		break;
+	default:
+		WARN_ON(1);
+		return;
+	}
+
+	rockchip_drm_crtc_mode_config(encoder->crtc, DRM_MODE_CONNECTOR_DSI,
+				      interface_pix_fmt);
+
+	if (mux)
+		val = DSI0_SEL_VOP_LIT | (DSI0_SEL_VOP_LIT << 16);
+	else
+		val = DSI0_SEL_VOP_LIT << 16;
+
+	regmap_write(dsi->grf_regmap, GRF_SOC_CON6, val);
+	dev_dbg(dsi->dev, "vop %s output to dsi0\n", (mux) ? "LIT" : "BIG");
+}
+
+static struct drm_encoder_helper_funcs
+dw_mipi_dsi_encoder_helper_funcs = {
+	.mode_fixup = dw_mipi_dsi_encoder_mode_fixup,
+	.commit = dw_mipi_dsi_encoder_commit,
+	.mode_set = dw_mipi_dsi_encoder_mode_set,
+	.disable = dw_mipi_dsi_encoder_disable,
+};
+
+static struct drm_encoder_funcs dw_mipi_dsi_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static int dw_mipi_dsi_connector_get_modes(struct drm_connector *connector)
+{
+	struct dw_mipi_dsi *dsi = con_to_dsi(connector);
+
+	return drm_panel_get_modes(dsi->panel);
+}
+
+static enum drm_mode_status dw_mipi_dsi_mode_valid(
+					struct drm_connector *connector,
+					struct drm_display_mode *mode)
+{
+	struct dw_mipi_dsi *dsi = con_to_dsi(connector);
+
+	enum drm_mode_status mode_status = MODE_OK;
+
+	if (dsi->pdata->mode_valid)
+		mode_status = dsi->pdata->mode_valid(connector, mode);
+
+	return mode_status;
+}
+
+static struct drm_encoder *dw_mipi_dsi_connector_best_encoder(
+					struct drm_connector *connector)
+{
+	struct dw_mipi_dsi *dsi = con_to_dsi(connector);
+
+	return &dsi->encoder;
+}
+
+static struct drm_connector_helper_funcs dw_mipi_dsi_connector_helper_funcs = {
+	.get_modes = dw_mipi_dsi_connector_get_modes,
+	.mode_valid = dw_mipi_dsi_mode_valid,
+	.best_encoder = dw_mipi_dsi_connector_best_encoder,
+};
+
+static enum drm_connector_status
+dw_mipi_dsi_detect(struct drm_connector *connector, bool force)
+{
+	return connector_status_connected;
+}
+
+static void dw_mipi_dsi_drm_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs dw_mipi_dsi_atomic_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.detect = dw_mipi_dsi_detect,
+	.destroy = dw_mipi_dsi_drm_connector_destroy,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int dw_mipi_dsi_register(struct drm_device *drm,
+				      struct dw_mipi_dsi *dsi)
+{
+	struct drm_encoder *encoder = &dsi->encoder;
+	struct drm_connector *connector = &dsi->connector;
+	struct device *dev = dsi->dev;
+	int ret;
+
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm,
+							     dev->of_node);
+	/*
+	 * If we failed to find the CRTC(s) which this encoder is
+	 * supposed to be connected to, it's because the CRTC has
+	 * not been registered yet.  Defer probing, and hope that
+	 * the required CRTC is added later.
+	 */
+	if (encoder->possible_crtcs == 0)
+		return -EPROBE_DEFER;
+
+	drm_encoder_helper_add(&dsi->encoder,
+			       &dw_mipi_dsi_encoder_helper_funcs);
+	ret = drm_encoder_init(drm, &dsi->encoder, &dw_mipi_dsi_encoder_funcs,
+			 DRM_MODE_ENCODER_DSI, NULL);
+	if (ret) {
+		dev_err(dev, "Failed to initialize encoder with drm\n");
+		return ret;
+	}
+
+	drm_connector_helper_add(connector,
+			&dw_mipi_dsi_connector_helper_funcs);
+
+	drm_connector_init(drm, &dsi->connector,
+			   &dw_mipi_dsi_atomic_connector_funcs,
+			   DRM_MODE_CONNECTOR_DSI);
+
+	drm_mode_connector_attach_encoder(connector, encoder);
+
+	return 0;
+}
+
+static int rockchip_mipi_parse_dt(struct dw_mipi_dsi *dsi)
+{
+	struct device_node *np = dsi->dev->of_node;
+
+	dsi->grf_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+	if (IS_ERR(dsi->grf_regmap)) {
+		dev_err(dsi->dev, "Unable to get rockchip,grf\n");
+		return PTR_ERR(dsi->grf_regmap);
+	}
+
+	return 0;
+}
+
+static enum drm_mode_status rk3288_mipi_dsi_mode_valid(
+					struct drm_connector *connector,
+					struct drm_display_mode *mode)
+{
+	/*
+	 * The VID_PKT_SIZE field in the DSI_VID_PKT_CFG
+	 * register is 11-bit.
+	 */
+	if (mode->hdisplay > 0x7ff)
+		return MODE_BAD_HVALUE;
+
+	/*
+	 * The V_ACTIVE_LINES field in the DSI_VTIMING_CFG
+	 * register is 11-bit.
+	 */
+	if (mode->vdisplay > 0x7ff)
+		return MODE_BAD_VVALUE;
+
+	return MODE_OK;
+}
+
+static struct dw_mipi_dsi_plat_data rk3288_mipi_dsi_drv_data = {
+	.max_data_lanes = 4,
+	.mode_valid = rk3288_mipi_dsi_mode_valid,
+};
+
+static const struct of_device_id dw_mipi_dsi_dt_ids[] = {
+	{
+	 .compatible = "rockchip,rk3288-mipi-dsi",
+	 .data = &rk3288_mipi_dsi_drv_data,
+	},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, dw_mipi_dsi_dt_ids);
+
+static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
+			     void *data)
+{
+	const struct of_device_id *of_id =
+			of_match_device(dw_mipi_dsi_dt_ids, dev);
+	const struct dw_mipi_dsi_plat_data *pdata = of_id->data;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = data;
+	struct dw_mipi_dsi *dsi;
+	struct resource *res;
+	int ret;
+
+	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
+	if (!dsi)
+		return -ENOMEM;
+
+	dsi->dev = dev;
+	dsi->pdata = pdata;
+
+	ret = rockchip_mipi_parse_dt(dsi);
+	if (ret)
+		return ret;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	dsi->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(dsi->base))
+		return PTR_ERR(dsi->base);
+
+	dsi->pllref_clk = devm_clk_get(dev, "ref");
+	if (IS_ERR(dsi->pllref_clk)) {
+		ret = PTR_ERR(dsi->pllref_clk);
+		dev_err(dev, "Unable to get pll reference clock: %d\n", ret);
+		return ret;
+	}
+
+	dsi->pclk = devm_clk_get(dev, "pclk");
+	if (IS_ERR(dsi->pclk)) {
+		ret = PTR_ERR(dsi->pclk);
+		dev_err(dev, "Unable to get pclk: %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(dsi->pllref_clk);
+	if (ret) {
+		dev_err(dev, "%s: Failed to enable pllref_clk\n", __func__);
+		return ret;
+	}
+
+	ret = dw_mipi_dsi_register(drm, dsi);
+	if (ret) {
+		dev_err(dev, "Failed to register mipi_dsi: %d\n", ret);
+		goto err_pllref;
+	}
+
+	dev_set_drvdata(dev, dsi);
+
+	dsi->dsi_host.ops = &dw_mipi_dsi_host_ops;
+	dsi->dsi_host.dev = dev;
+	return mipi_dsi_host_register(&dsi->dsi_host);
+
+err_pllref:
+	clk_disable_unprepare(dsi->pllref_clk);
+	return ret;
+}
+
+static void dw_mipi_dsi_unbind(struct device *dev, struct device *master,
+	void *data)
+{
+	struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
+
+	mipi_dsi_host_unregister(&dsi->dsi_host);
+	clk_disable_unprepare(dsi->pllref_clk);
+}
+
+static const struct component_ops dw_mipi_dsi_ops = {
+	.bind	= dw_mipi_dsi_bind,
+	.unbind	= dw_mipi_dsi_unbind,
+};
+
+static int dw_mipi_dsi_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &dw_mipi_dsi_ops);
+}
+
+static int dw_mipi_dsi_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &dw_mipi_dsi_ops);
+	return 0;
+}
+
+static struct platform_driver dw_mipi_dsi_driver = {
+	.probe		= dw_mipi_dsi_probe,
+	.remove		= dw_mipi_dsi_remove,
+	.driver		= {
+		.of_match_table = dw_mipi_dsi_dt_ids,
+		.name	= DRIVER_NAME,
+	},
+};
+module_platform_driver(dw_mipi_dsi_driver);
+
+MODULE_DESCRIPTION("ROCKCHIP MIPI DSI host controller driver");
+MODULE_AUTHOR("Chris Zhong <zyw at rock-chips.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 056cac3..46c2a8d 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -818,6 +818,9 @@ int rockchip_drm_crtc_mode_config(struct drm_crtc *crtc,
 	case DRM_MODE_CONNECTOR_HDMIA:
 		VOP_CTRL_SET(vop, hdmi_en, 1);
 		break;
+	case DRM_MODE_CONNECTOR_DSI:
+		VOP_CTRL_SET(vop, mipi_en, 1);
+		break;
 	default:
 		DRM_ERROR("unsupport connector_type[%d]\n", connector_type);
 		return -EINVAL;
-- 
2.6.3



From mark.yao at rock-chips.com  Wed Jan  6 00:39:24 2016
From: mark.yao at rock-chips.com (Mark yao)
Date: Wed, 06 Jan 2016 16:39:24 +0800
Subject: [PATCH v7 0/5] Add mipi dsi support for rk3288
In-Reply-To: <1452053038-32098-1-git-send-email-zyw@rock-chips.com>
References: <1452053038-32098-1-git-send-email-zyw@rock-chips.com>
Message-ID: <568CD2BC.7080800@rock-chips.com>

On 2016?01?06? 12:03, Chris Zhong wrote:
> The rk3288 MIPI DSI is a Synopsys DesignWare MIPI DSI host controller
> IP. This series adds support for a Synopsys DesignWare MIPI DSI host
> controller DRM driver.
>
> The MIPI DSI feature is tested on rk3288 evb board, backport them to
> chrome os kernel chrome_v3.14, and it can display normally.
>
> This patchset is base on the patchset from Ying.liu at freescale.com.
> <http://www.spinics.net/lists/dri-devel/msg77181.html>
>
> According to the suggestion from Thierry, I have get rid of the bridge,
> and register the encoder & connecter in drm/rockchip/dw-mipi-dsi.c.
>
> After backport below patches from linux-next, I can merge this serial into
> Dave's drm-next branch.
>
> 94d5d6a0fbf3 clk: rockchip: add an id for rk3288 crypto clk
> c6d49fbcfcc4 clk: rockchip: add id for mipidsi sclk on rk3288
> a2f4c560f18e clk: rockchip: add mipidsi clock on rk3288
> 69c923743640 ARM: dts: rockchip: correct the name of REG8 for rk3288-evb-act8846
> 662513a14c9d ARM: dts: rockchip: add 2 regulators for rk3288-evb-act8846
> b04061e6599b ARM: dts: rockchip: move the public part to rk3288-evb common
>
>
>
Hi Chris

I had picked following three patches and send a pull-request to Dave
       [PATCH v7 1/5] drm/rockchip: return a true clock rate to 
adjusted_mode
       [PATCH v7 2/5] Documentation: dt-bindings: Add bindings for 
rk3288 DW MIPI DSI driver
       [PATCH v7.2 3/5] drm: rockchip: Support Synopsys DW MIPI DSI

Thanks.

-- 
?ark Yao




From zhengxing at rock-chips.com  Wed Jan  6 05:05:56 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Wed, 06 Jan 2016 21:05:56 +0800
Subject: [RESEND PATCH v1 4/4] clk: rockchip: rk3036: fix and add node
 id for emac clock
In-Reply-To: <01110793-A349-4006-82A2-4489CE832B19@rock-chips.com>
References: <1451293433-32392-1-git-send-email-zhengxing@rock-chips.com>
 <5681E8E4.2010303@rock-chips.com> <5681F121.3070307@rock-chips.com>
 <1562268.fb58QJ4jV7@diego>
 <01110793-A349-4006-82A2-4489CE832B19@rock-chips.com>
Message-ID: <568D1134.9010305@rock-chips.com>

Hi Heiko,

On 2016?01?02? 10:34, Xing Zheng wrote:
> Hi Heiko,
>     Thank you for your patch, I will apply and test it later.
>
> Thanks.
>
>> ? 2016?1?2??06:10?Heiko St?bner <heiko at sntech.de> ???
>>
>> Hi Xing,
>>
>> Am Dienstag, 29. Dezember 2015, 10:34:09 schrieb Xing Zheng:
>>> On 2015?12?29? 09:59, Yakir Yang wrote:
>>>> On 12/28/2015 08:41 PM, Heiko St?bner wrote:
>>>>> Am Montag, 28. Dezember 2015, 17:03:53 schrieb Xing Zheng:
>>>>>> Due to referred old version TRM, there is incorrect emac clock node,
>>>>>> we should fix it. The SEL_21_9 is the parent of SEL_21_4.
>>>>>>
>>>>>> In the emac driver, we need to refer HCLK_MAC, and because There are
>>>>>> only 3PLLs (APLL/GPLL/DPLL) on the rk3036, most clock are under the
>>>>>> GPLL, and it is unable to provide the accurate rate for mac_ref which
>>>>>> need to 50MHz probability, we should let it under the APLL and are
>>>>>> able to set the freq which integer multiples of 50MHz, so we add these
>>>>>> emac node for reference.
>>>>> I don't really follow here. While I do understand that the emac needs
>>>>> 50MHz, I
>>>>> don't think using the APLL as source is helpful.
>>>>>
>>>>> The APLL is the main clocksource for the cpu-cores, including frequency
>>>>> scaling, and while it currently only lists 816MHz as sole frequency,
>>>>> you're
>>>>> pretty much guaranteed to not get your correct multiple of 50MHz from
>>>>> there
>>>>> either. And limiting the cpu to just do 600MHz to get the mac working
>>>>> sounds
>>>>> pretty bad ;-) .
>>>>>
>>>>>
>>>>> In the rk3036 cru-node the gpll gets set to 594MHz. Is there a
>>>>> special reason
>>>>> why it needs to be 594MHz and cannot be a round 600MHz? Because that
>>>>> would
>>>>> also provide your 50MHz-multiple nicely.
>>>> Yes, this magic 594MHz would help to support the standard HDMI
>>>> resolutions, here are the math:
>>>>
>>>> 1920x1080-60Hz DCLK = 148.5MHz = 594MHz / 4
>>>> 1280x720-60Hz DCLK = 74.25MHz = 594MHz / 8
>>>> 720x480-60Hz DCLK = 27MHz = 594MHz / 22
>>>>
>>>> Thanks,
>>>> - Yakir
>>> Thanks Yakir.
>>>
>>> Hi Heiko,
>>> From the above, do you have better idea for the RK3036's emac without
>>> ext-oscillator?
>> During the last days I did play a bit with the clock framework. As I don't 
>> have a Kylin (or any rk3036) board, I did build a test-case with pclk_cpu on 
>> the rk3188 (which can be affected by the armclk if not reparented to the 
>> gpll), which got sucessfully adapted to get back to (or near) the originally 
>> requested frequency.
>>
>> So ideally you could roll back your mux/div split here and try the attached 
>> diff. In theory it should help :-) .
>> As can be seen by the FIXMEs, not fully finished, but I'd like to determine if 
>> it fixes the issue at least.
>>
>>
>> Heiko
>> <clk-keep-req-rate.diff>

I tested your patch just now, it seems to work well. I will do more tests.

Thank you. :-)



From ykk at rock-chips.com  Wed Jan  6 20:34:48 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Thu,  7 Jan 2016 12:34:48 +0800
Subject: [RFC PATCH v1 0/2] Add RK3229 HDMI support
Message-ID: <1452141288-9292-1-git-send-email-ykk@rock-chips.com>


RK3229 have integrated an DesignedWare HDMI controller and an INNO HDMI phy,
so we can still reuse the dw-hdmi driver for RK3229 HDMI controller, but
we need to create an separate driver for RK3229 HDMI PHY.


Yakir Yang (2):
  drm: rockchip: hdmi: add RK3229 HDMI support
  dt-bindings: add document for rk3229-hdmi

 .../bindings/display/rockchip/dw_hdmi-rockchip.txt |   4 +-
 drivers/gpu/drm/bridge/dw-hdmi.c                   |  33 +-
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c        | 380 +++++++++++++++++++--
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h        | 137 ++++++++
 include/drm/bridge/dw_hdmi.h                       |   5 +-
 5 files changed, 519 insertions(+), 40 deletions(-)
 create mode 100644 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h

-- 
2.1.2




From ykk at rock-chips.com  Wed Jan  6 20:37:27 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Thu,  7 Jan 2016 12:37:27 +0800
Subject: [RFC PATCH v1 1/2] drm: rockchip: hdmi: add RK3229 HDMI support
In-Reply-To: <1452141288-9292-1-git-send-email-ykk@rock-chips.com>
References: <1452141288-9292-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1452141447-9441-1-git-send-email-ykk@rock-chips.com>

RK3229 integrate an DesignedWare HDMI2.0 controller and an INNO HDMI2.0 phy,
the max output resolution is 4K.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
 drivers/gpu/drm/bridge/dw-hdmi.c            |  33 ++-
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 380 +++++++++++++++++++++++++---
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h | 137 ++++++++++
 include/drm/bridge/dw_hdmi.h                |   5 +-
 4 files changed, 516 insertions(+), 39 deletions(-)
 create mode 100644 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h

diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
index 6fbec99..60b1dcf 100644
--- a/drivers/gpu/drm/bridge/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/dw-hdmi.c
@@ -735,10 +735,12 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
 {
 	unsigned res_idx;
 	u8 val, msec;
+	int ret;
 	const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
 	const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg;
 	const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr;
 	const struct dw_hdmi_phy_config *phy_config = pdata->phy_config;
+	int mpixelclock = hdmi->hdmi_data.video_mode.mpixelclock;
 
 	if (prep)
 		return -EINVAL;
@@ -758,27 +760,38 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
 		return -EINVAL;
 	}
 
+	if (hdmi->plat_data->extphy_config) {
+		/* gen2 tx power off */
+		dw_hdmi_phy_gen2_txpwron(hdmi, 0);
+		dw_hdmi_phy_gen2_pddq(hdmi, 1);
+
+		ret = hdmi->plat_data->extphy_config(hdmi->plat_data, res_idx,
+						     mpixelclock);
+		/* gen2 tx power on */
+		dw_hdmi_phy_gen2_txpwron(hdmi, 1);
+		dw_hdmi_phy_gen2_pddq(hdmi, 0);
+
+		return ret;
+	}
+
 	/* PLL/MPLL Cfg - always match on final entry */
 	for (; mpll_config->mpixelclock != ~0UL; mpll_config++)
-		if (hdmi->hdmi_data.video_mode.mpixelclock <=
-		    mpll_config->mpixelclock)
+		if (mpixelclock <= mpll_config->mpixelclock)
 			break;
 
 	for (; curr_ctrl->mpixelclock != ~0UL; curr_ctrl++)
-		if (hdmi->hdmi_data.video_mode.mpixelclock <=
-		    curr_ctrl->mpixelclock)
+		if (mpixelclock <= curr_ctrl->mpixelclock)
 			break;
 
 	for (; phy_config->mpixelclock != ~0UL; phy_config++)
-		if (hdmi->hdmi_data.video_mode.mpixelclock <=
-		    phy_config->mpixelclock)
+		if (mpixelclock <= phy_config->mpixelclock)
 			break;
 
 	if (mpll_config->mpixelclock == ~0UL ||
 	    curr_ctrl->mpixelclock == ~0UL ||
 	    phy_config->mpixelclock == ~0UL) {
 		dev_err(hdmi->dev, "Pixel clock %d - unsupported by HDMI\n",
-			hdmi->hdmi_data.video_mode.mpixelclock);
+			mpixelclock);
 		return -EINVAL;
 	}
 
@@ -1476,14 +1489,16 @@ dw_hdmi_connector_mode_valid(struct drm_connector *connector,
 {
 	struct dw_hdmi *hdmi = container_of(connector,
 					   struct dw_hdmi, connector);
+	struct dw_hdmi_plat_data *plat_data = hdmi->plat_data;
 	enum drm_mode_status mode_status = MODE_OK;
 
 	/* We don't support double-clocked modes */
 	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
 		return MODE_BAD;
 
-	if (hdmi->plat_data->mode_valid)
-		mode_status = hdmi->plat_data->mode_valid(connector, mode);
+	if (plat_data->mode_valid)
+		mode_status = plat_data->mode_valid(plat_data, mode);
+
 
 	return mode_status;
 }
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index c65ce8c..424d548 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -7,6 +7,7 @@
  * (at your option) any later version.
  */
 
+#include <linux/clk.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/mfd/syscon.h>
@@ -21,18 +22,135 @@
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_vop.h"
 
-#define GRF_SOC_CON6                    0x025c
-#define HDMI_SEL_VOP_LIT                (1 << 4)
+#include "dw_hdmi-rockchip.h"
 
 struct rockchip_hdmi {
 	struct device *dev;
 	struct regmap *regmap;
 	struct drm_encoder encoder;
+	struct dw_hdmi_plat_data plat_data;
+
+	void __iomem *extphy_regbase;
+	struct clk *extphy_pclk;
 };
 
 #define to_rockchip_hdmi(x)	container_of(x, struct rockchip_hdmi, x)
 
-static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
+static const struct extphy_config_tab rk3229_extphy_cfg[] = {
+	{ .mpixelclock = 165000000,
+	  .pre_emphasis = 0, .slopeboost = 0, .clk_level = 4,
+	  .data0_level = 4, 4, 4,
+	},
+
+	{ .mpixelclock = 225000000,
+	  .pre_emphasis = 0, .slopeboost = 0, .clk_level = 6,
+	  .data0_level = 6, 6, 6,
+	},
+
+	{ .mpixelclock = 340000000,
+	  .pre_emphasis = 1, .slopeboost = 0, .clk_level = 6,
+	  .data0_level = 10, 10, 10,
+	},
+
+	{ .mpixelclock = 594000000,
+	  .pre_emphasis = 1, .slopeboost = 0, .clk_level = 7,
+	  .data0_level = 10, 10, 10,
+	},
+
+	{ .mpixelclock = ~0UL},
+};
+
+static const struct extphy_pll_config_tab rk3229_extphy_pll_cfg[] = {
+	{
+		.mpixelclock = 27000000, .param = {
+			{ .pll_nd = 1, .pll_nf = 45,
+			  .tmsd_divider_a = 3, 1, 1,
+			  .pclk_divider_a = 1, 3, 3, 4,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
+			},
+			{ .pll_nd = 1, .pll_nf = 45,
+			  .tmsd_divider_a = 0, 3, 3,
+			  .pclk_divider_a = 1, 3, 3, 4,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
+			},
+		},
+	}, {
+		.mpixelclock = 59400000, .param = {
+			{ .pll_nd = 2, .pll_nf = 99,
+			  .tmsd_divider_a = 3, 1, 1,
+			  .pclk_divider_a = 1, 3, 2, 2,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
+			},
+			{ .pll_nd = 2, .pll_nf = 99,
+			  .tmsd_divider_a = 1, 1, 1,
+			  .pclk_divider_a = 1, 3, 2, 2,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
+			},
+		},
+	}, {
+		.mpixelclock = 74250000, .param = {
+			{ .pll_nd = 2, .pll_nf = 99,
+			  .tmsd_divider_a = 1, 1, 1,
+			  .pclk_divider_a = 1, 2, 2, 2,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
+			},
+			{ .pll_nd = 4, .pll_nf = 495,
+			  .tmsd_divider_a = 1, 2, 2,
+			  .pclk_divider_a = 1, 3, 3, 4,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 2, .ppll_nf = 40, .ppll_no = 4,
+			},
+		},
+	}, {
+		.mpixelclock = 148500000, .param = {
+			{ .pll_nd = 2, .pll_nf = 99,
+			  .tmsd_divider_a = 1, 0, 0,
+			  .pclk_divider_a = 1, 2, 1, 1,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 2, .ppll_nf = 40, .ppll_no = 4,
+			},
+			{ .pll_nd = 4, .pll_nf = 495,
+			  .tmsd_divider_a = 0, 2, 2,
+			  .pclk_divider_a = 1, 3, 2, 2,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 4, .ppll_nf = 40, .ppll_no = 2,
+			},
+		},
+	}, {
+		.mpixelclock = 297000000, .param = {
+			{ .pll_nd = 2, .pll_nf = 99,
+			  .tmsd_divider_a = 0, 0, 0,
+			  .pclk_divider_a = 1, 0, 1, 1,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 4, .ppll_nf = 40, .ppll_no = 2,
+			},
+			{ .pll_nd = 4, .pll_nf = 495,
+			  .tmsd_divider_a = 1, 2, 0,
+			  .pclk_divider_a = 1, 3, 1, 1,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 8, .ppll_nf = 40, .ppll_no = 1,
+			},
+		},
+	}, {
+		.mpixelclock = 594000000, .param = {
+			{ .pll_nd = 1, .pll_nf = 99,
+			  .tmsd_divider_a = 0, 2, 0,
+			  .pclk_divider_a = 1, 0, 1, 1,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 8, .ppll_nf = 40, .ppll_no = 1,
+			},
+		}
+	}, {
+		.mpixelclock = ~0UL,
+	}
+};
+
+static const struct dw_hdmi_mpll_config rk3288_mpll_cfg[] = {
 	{
 		27000000, {
 			{ 0x00b3, 0x0000},
@@ -112,7 +230,7 @@ static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
 	}
 };
 
-static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = {
+static const struct dw_hdmi_curr_ctrl rk3288_cur_ctr[] = {
 	/*      pixelclk    bpp8    bpp10   bpp12 */
 	{
 		40000000,  { 0x0018, 0x0018, 0x0018 },
@@ -133,7 +251,7 @@ static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = {
 	}
 };
 
-static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
+static const struct dw_hdmi_phy_config rk3288_phy_config[] = {
 	/*pixelclk   symbol   term   vlev*/
 	{ 74250000,  0x8009, 0x0004, 0x0272},
 	{ 148500000, 0x802b, 0x0004, 0x028d},
@@ -141,9 +259,158 @@ static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
 	{ ~0UL,	     0x0000, 0x0000, 0x0000}
 };
 
+static int hdmi_ext_phy_write(struct rockchip_hdmi *hdmi, unsigned short data,
+			      unsigned char addr)
+{
+	writel_relaxed(data, hdmi->extphy_regbase + (addr) * 0x04);
+	return 0;
+}
+
+static unsigned int hdmi_phy_i2c_read(struct rockchip_hdmi *hdmi,
+				      unsigned char addr)
+{
+	return readl_relaxed(hdmi->extphy_regbase + (addr) * 0x04);
+}
+
+static int rk3229_extphy_config(struct dw_hdmi_plat_data *plat_data,
+				  int res, int pixelclock)
+{
+	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(plat_data);
+	const struct extphy_pll_config_tab *mpll = rk3229_extphy_pll_cfg;
+	const struct extphy_config_tab *ctrl = rk3229_extphy_cfg;
+	unsigned long timeout;
+	int i, stat;
+
+	/* Find out the extphy MPLL configure parameters */
+	for (i = 0; mpll[i].mpixelclock != ~0UL; i++)
+		if (pixelclock == mpll[i].mpixelclock)
+			break;
+	if (mpll[i].mpixelclock == ~0UL) {
+		dev_err(hdmi->dev, "Extphy couldn't support %dHz\n", pixelclock);
+		return -EINVAL;
+	}
+
+	regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2,
+		     RK3229_PLL_POWER_DOWN | RK3229_PLL_PDATA_DEN);
+
+	/*
+	 * Configure external phy PLL registers.
+	 */
+	stat = ((mpll[i].param[res].pll_nf >> 1) & EXT_PHY_PLL_FB_BIT8_MASK) |
+	       ((mpll[i].param[res].vco_div_5 & 1) << 5) |
+	       (mpll[i].param[res].pll_nd & EXT_PHY_PLL_PRE_DIVIDER_MASK);
+	hdmi_ext_phy_write(hdmi, stat, EXT_PHY_PLL_PRE_DIVIDER);
+
+	hdmi_ext_phy_write(hdmi, mpll[i].param[res].pll_nf, EXT_PHY_PLL_FB_DIVIDER);
+
+	stat = (mpll[i].param[res].pclk_divider_a & EXT_PHY_PCLK_DIVIDERA_MASK) |
+	       ((mpll[i].param[res].pclk_divider_b & 3) << 5);
+	hdmi_ext_phy_write(hdmi, stat, EXT_PHY_PCLK_DIVIDER1);
+
+	stat = (mpll[i].param[res].pclk_divider_d & EXT_PHY_PCLK_DIVIDERD_MASK) |
+	       ((mpll[i].param[res].pclk_divider_c & 3) << 5);
+	hdmi_ext_phy_write(hdmi, stat, EXT_PHY_PCLK_DIVIDER2);
+
+	stat = ((mpll[i].param[res].tmsd_divider_c & 3) << 4) |
+	       ((mpll[i].param[res].tmsd_divider_a & 3) << 2) |
+	       (mpll[i].param[res].tmsd_divider_b & 3);
+	hdmi_ext_phy_write(hdmi, stat, EXT_PHY_TMDSCLK_DIVIDER);
+
+	hdmi_ext_phy_write(hdmi, mpll[i].param[res].ppll_nf, EXT_PHY_PPLL_FB_DIVIDER);
+
+	if (mpll[i].param[res].ppll_no == 1) {
+		hdmi_ext_phy_write(hdmi, 0, EXT_PHY_PPLL_POST_DIVIDER);
+
+		stat = 0x20 | mpll[i].param[res].ppll_nd;
+		hdmi_ext_phy_write(hdmi, stat, EXT_PHY_PPLL_PRE_DIVIDER);
+	} else {
+		stat = ((mpll[i].param[res].ppll_no / 2) - 1) << 4;
+		hdmi_ext_phy_write(hdmi, stat, EXT_PHY_PPLL_POST_DIVIDER);
+
+		stat = 0xe0 | mpll[i].param[res].ppll_nd;
+		hdmi_ext_phy_write(hdmi, stat, EXT_PHY_PPLL_PRE_DIVIDER);
+	}
+
+
+	/* Find out the extphy driver configure parameters */
+	for (i = 0; ctrl[i].mpixelclock != ~0UL; i++)
+		if (pixelclock <= ctrl[i].mpixelclock)
+			break;
+	if (ctrl[i].mpixelclock == ~0UL) {
+		dev_err(hdmi->dev, "Extphy couldn't support %dHz\n", pixelclock);
+		return -EINVAL;
+	}
+
+	/*
+	 * Configure the extphy driver registers.
+	 */
+	if (ctrl[i].slopeboost) {
+		hdmi_ext_phy_write(hdmi, 0xff, EXT_PHY_SIGNAL_CTRL);
+
+		stat = (ctrl[i].slopeboost - 1) & 3;
+		stat = (stat << 6) | (stat << 4) | (stat << 2) | stat;
+		hdmi_ext_phy_write(hdmi, stat, EXT_PHY_SLOPEBOOST);
+	} else
+		hdmi_ext_phy_write(hdmi, 0x0f, EXT_PHY_SIGNAL_CTRL);
+
+	stat = ctrl[i].pre_emphasis & 3;
+	stat = (stat << 4) | (stat << 2) | stat;
+	hdmi_ext_phy_write(hdmi, stat, EXT_PHY_PREEMPHASIS);
+
+	stat = ((ctrl[i].clk_level & 0xf) << 4) | (ctrl[i].data2_level & 0xf);
+	hdmi_ext_phy_write(hdmi, stat, EXT_PHY_LEVEL1);
+
+	stat = ((ctrl[i].data1_level & 0xf) << 4) | (ctrl[i].data0_level & 0xf);
+	hdmi_ext_phy_write(hdmi, stat, EXT_PHY_LEVEL2);
+
+	hdmi_ext_phy_write(hdmi, 0x22, 0xf3);
+
+	stat = clk_get_rate(hdmi->extphy_pclk) / 100000;
+	hdmi_ext_phy_write(hdmi, ((stat >> 8) & 0xff) | 0x80, EXT_PHY_TERM_CAL);
+	hdmi_ext_phy_write(hdmi,  stat & 0xff, EXT_PHY_TERM_CAL_DIV_L);
+
+	if (pixelclock > 340000000)
+		stat = EXT_PHY_AUTO_R100_OHMS;
+	else if (pixelclock > 200000000)
+		stat = EXT_PHY_AUTO_R50_OHMS;
+	else
+		stat = EXT_PHY_AUTO_ROPEN_CIRCUIT;
+	hdmi_ext_phy_write(hdmi, stat | 0x20, EXT_PHY_TERM_RESIS_AUTO);
+	hdmi_ext_phy_write(hdmi, (stat >> 8) & 0xff, EXT_PHY_TERM_CAL);
+
+	stat = (pixelclock > 200000000) ? 0 : 0x11;
+	hdmi_ext_phy_write(hdmi, stat, EXT_PHY_PLL_BW);
+	hdmi_ext_phy_write(hdmi, 0x27, EXT_PHY_PPLL_BW);
+
+	regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2, RK3229_PLL_POWER_UP);
+
+	/* Detect whether PLL is lock or not */
+	timeout = jiffies + msecs_to_jiffies(100);
+	while (!time_after(jiffies, timeout)) {
+		usleep_range(1000, 2000);
+		stat = hdmi_phy_i2c_read(hdmi, EXT_PHY_PPLL_POST_DIVIDER);
+		if (stat & EXT_PHY_PPLL_LOCK_STATUS_MASK)
+			break;
+	}
+
+	regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2, RK3229_PLL_PDATA_EN);
+
+	if ((stat & EXT_PHY_PPLL_LOCK_STATUS_MASK) == 0) {
+		dev_err(hdmi->dev, "EXT PHY PLL not locked\n");
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
 static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 {
 	struct device_node *np = hdmi->dev->of_node;
+	struct platform_device *pdev;
+	struct resource *iores;
+	int ret;
+
+	pdev = container_of(hdmi->dev, struct platform_device, dev);
 
 	hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
 	if (IS_ERR(hdmi->regmap)) {
@@ -151,24 +418,62 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 		return PTR_ERR(hdmi->regmap);
 	}
 
+	if (hdmi->plat_data.dev_type == RK3229_HDMI) {
+		iores = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+		if (!iores)
+			return -ENXIO;
+
+		hdmi->extphy_regbase = devm_ioremap_resource(hdmi->dev, iores);
+		if (IS_ERR(hdmi->extphy_regbase)) {
+			dev_err(hdmi->dev, "failed to map extphy regbase\n");
+			return PTR_ERR(hdmi->extphy_regbase);
+		}
+
+		hdmi->extphy_pclk = devm_clk_get(hdmi->dev, "extphy");
+		if (IS_ERR(hdmi->extphy_pclk)) {
+			dev_err(hdmi->dev, "failed to get extphy clock\n");
+			return PTR_ERR(hdmi->extphy_pclk);
+		}
+
+		ret = clk_prepare_enable(hdmi->extphy_pclk);
+		if (ret) {
+			dev_err(hdmi->dev, "failed to enable extphy clk: %d\n",
+				ret);
+			return ret;
+		}
+
+		regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON6,
+			     RK3229_IO_3V_DOMAIN);
+
+		regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2,
+			     RK3229_DDC_MASK_EN);
+	}
+
 	return 0;
 }
 
 static enum drm_mode_status
-dw_hdmi_rockchip_mode_valid(struct drm_connector *connector,
+dw_hdmi_rockchip_mode_valid(const struct dw_hdmi_plat_data *plat_data,
 			    struct drm_display_mode *mode)
 {
-	const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg;
 	int pclk = mode->clock * 1000;
 	bool valid = false;
 	int i;
 
-	for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) {
-		if (pclk == mpll_cfg[i].mpixelclock) {
-			valid = true;
-			break;
+	if (plat_data->dev_type == RK3288_HDMI)
+		for (i = 0; rk3288_mpll_cfg[i].mpixelclock != (~0UL); i++)
+			if (pclk == rk3288_mpll_cfg[i].mpixelclock) {
+				valid = true;
+				break;
+			}
+
+	if (plat_data->dev_type == RK3229_HDMI)
+		for (i = 0; rk3229_extphy_pll_cfg[i].mpixelclock != ~0UL; i++) {
+			if (pclk == rk3229_extphy_pll_cfg[i].mpixelclock) {
+				valid = true;
+				break;
+			}
 		}
-	}
 
 	return (valid) ? MODE_OK : MODE_BAD;
 }
@@ -198,21 +503,29 @@ static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
 static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
 {
 	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
+	int out_mode = ROCKCHIP_OUT_MODE_AAAA;
 	u32 val;
 	int mux;
 
+	if (hdmi->plat_data.dev_type == RK3229_HDMI)
+		out_mode = ROCKCHIP_OUT_MODE_P888;
+
 	rockchip_drm_crtc_mode_config(encoder->crtc, DRM_MODE_CONNECTOR_HDMIA,
-				      ROCKCHIP_OUT_MODE_AAAA);
+				      out_mode);
 
-	mux = rockchip_drm_encoder_get_mux_id(hdmi->dev->of_node, encoder);
-	if (mux)
-		val = HDMI_SEL_VOP_LIT | (HDMI_SEL_VOP_LIT << 16);
-	else
-		val = HDMI_SEL_VOP_LIT << 16;
+	if (hdmi->plat_data.dev_type == RK3288_HDMI) {
+		mux = rockchip_drm_encoder_get_mux_id(hdmi->dev->of_node,
+						      encoder);
+		if (mux)
+			val = RK3288_HDMI_SEL_VOP_LIT |
+			      (RK3288_HDMI_SEL_VOP_LIT << 16);
+		else
+			val = RK3288_HDMI_SEL_VOP_LIT << 16;
 
-	regmap_write(hdmi->regmap, GRF_SOC_CON6, val);
-	dev_dbg(hdmi->dev, "vop %s output to hdmi\n",
-		(mux) ? "LIT" : "BIG");
+		regmap_write(hdmi->regmap, RK3288_GRF_SOC_CON6, val);
+
+		dev_dbg(hdmi->dev, "vop %s output to hdmi\n", (mux) ? "LIT" : "BIG");
+	}
 }
 
 static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = {
@@ -222,17 +535,26 @@ static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_fun
 	.disable    = dw_hdmi_rockchip_encoder_disable,
 };
 
-static const struct dw_hdmi_plat_data rockchip_hdmi_drv_data = {
+static const struct dw_hdmi_plat_data rk3288_hdmi_drv_data = {
 	.mode_valid = dw_hdmi_rockchip_mode_valid,
-	.mpll_cfg   = rockchip_mpll_cfg,
-	.cur_ctr    = rockchip_cur_ctr,
-	.phy_config = rockchip_phy_config,
+	.mpll_cfg   = rk3288_mpll_cfg,
+	.cur_ctr    = rk3288_cur_ctr,
+	.phy_config = rk3288_phy_config,
 	.dev_type   = RK3288_HDMI,
 };
 
+static const struct dw_hdmi_plat_data rk3229_hdmi_drv_data = {
+	.mode_valid = dw_hdmi_rockchip_mode_valid,
+	.extphy_config = rk3229_extphy_config,
+	.dev_type   = RK3229_HDMI,
+};
+
 static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
 	{ .compatible = "rockchip,rk3288-dw-hdmi",
-	  .data = &rockchip_hdmi_drv_data
+	  .data = &rk3288_hdmi_drv_data
+	},
+	{ .compatible = "rockchip,rk3229-dw-hdmi",
+	  .data = &rk3229_hdmi_drv_data
 	},
 	{},
 };
@@ -242,7 +564,6 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 				 void *data)
 {
 	struct platform_device *pdev = to_platform_device(dev);
-	const struct dw_hdmi_plat_data *plat_data;
 	const struct of_device_id *match;
 	struct drm_device *drm = data;
 	struct drm_encoder *encoder;
@@ -259,7 +580,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 		return -ENOMEM;
 
 	match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node);
-	plat_data = match->data;
+	hdmi->plat_data = *(struct dw_hdmi_plat_data *)(match->data);
 	hdmi->dev = &pdev->dev;
 	encoder = &hdmi->encoder;
 
@@ -293,7 +614,8 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 	drm_encoder_init(drm, encoder, &dw_hdmi_rockchip_encoder_funcs,
 			 DRM_MODE_ENCODER_TMDS, NULL);
 
-	return dw_hdmi_bind(dev, master, data, encoder, iores, irq, plat_data);
+	return dw_hdmi_bind(dev, master, data, encoder, iores, irq,
+			    &hdmi->plat_data);
 }
 
 static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master,
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h
new file mode 100644
index 0000000..aca2543
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h
@@ -0,0 +1,137 @@
+#ifndef __DW_HDMI_ROCKCHIP__
+#define __DW_HDMI_ROCKCHIP__
+
+struct extphy_config_tab {
+	u32 mpixelclock;
+	int pre_emphasis;
+	int slopeboost;
+	int clk_level;
+	int data0_level;
+	int data1_level;
+	int data2_level;
+};
+
+struct extphy_pll_config_tab {
+	unsigned long mpixelclock;
+	struct {
+		u8	pll_nd;
+		u16	pll_nf;
+		u8	tmsd_divider_a;
+		u8	tmsd_divider_b;
+		u8	tmsd_divider_c;
+		u8	pclk_divider_a;
+		u8	pclk_divider_b;
+		u8	pclk_divider_c;
+		u8	pclk_divider_d;
+		u8	vco_div_5;
+		u8	ppll_nd;
+		u8	ppll_nf;
+		u8	ppll_no;
+	} param[DW_HDMI_RES_MAX];
+};
+
+#define RK3288_GRF_SOC_CON6			0x025c
+#define RK3288_HDMI_SEL_VOP_LIT			(1 << 4)
+
+#define RK3229_GRF_SOC_CON6			0x0418
+#define RK3229_IO_3V_DOMAIN			((7 << 4) | (7 << (4 + 16)))
+
+#define RK3229_GRF_SOC_CON2			0x0408
+#define RK3229_DDC_MASK_EN			((3 << 13) | (3 << (13 + 16)))
+
+#define RK3229_PLL_POWER_DOWN			(BIT(12) | BIT(12 + 16))
+#define RK3229_PLL_POWER_UP			BIT(12 + 16)
+#define RK3229_PLL_PDATA_DEN			BIT(11 + 16)
+#define RK3229_PLL_PDATA_EN			(BIT(11) | BIT(11 + 16))
+
+/* PHY Defined for RK322X */
+#define EXT_PHY_CONTROL				0
+#define EXT_PHY_ANALOG_RESET_MASK		0x80
+#define EXT_PHY_DIGITAL_RESET_MASK		0x40
+#define EXT_PHY_PCLK_INVERT_MASK		0x08
+#define EXT_PHY_PREPCLK_INVERT_MASK		0x04
+#define EXT_PHY_TMDSCLK_INVERT_MASK		0x02
+#define EXT_PHY_SRC_SELECT_MASK			0x01
+
+#define EXT_PHY_TERM_CAL			0x03
+#define EXT_PHY_TERM_CAL_EN_MASK		0x80
+#define EXT_PHY_TERM_CAL_DIV_H_MASK		0x7f
+
+#define EXT_PHY_TERM_CAL_DIV_L			0x04
+
+#define EXT_PHY_PLL_PRE_DIVIDER			0xe2
+#define EXT_PHY_PLL_FB_BIT8_MASK		0x80
+#define EXT_PHY_PLL_PCLK_DIV5_EN_MASK		0x20
+#define EXT_PHY_PLL_PRE_DIVIDER_MASK		0x1f
+
+#define EXT_PHY_PLL_FB_DIVIDER			0xe3
+
+#define EXT_PHY_PCLK_DIVIDER1			0xe4
+#define EXT_PHY_PCLK_DIVIDERB_MASK		0x60
+#define EXT_PHY_PCLK_DIVIDERA_MASK		0x1f
+
+#define EXT_PHY_PCLK_DIVIDER2			0xe5
+#define EXT_PHY_PCLK_DIVIDERC_MASK		0x60
+#define EXT_PHY_PCLK_DIVIDERD_MASK		0x1f
+
+#define EXT_PHY_TMDSCLK_DIVIDER			0xe6
+#define EXT_PHY_TMDSCLK_DIVIDERC_MASK		0x30
+#define EXT_PHY_TMDSCLK_DIVIDERA_MASK		0x0c
+#define EXT_PHY_TMDSCLK_DIVIDERB_MASK		0x03
+
+#define EXT_PHY_PLL_BW				0xe7
+
+#define EXT_PHY_PPLL_PRE_DIVIDER		0xe9
+#define EXT_PHY_PPLL_ENABLE_MASK		0xc0
+#define EXT_PHY_PPLL_PRE_DIVIDER_MASK		0x1f
+
+#define EXT_PHY_PPLL_FB_DIVIDER			0xea
+
+#define EXT_PHY_PPLL_POST_DIVIDER		0xeb
+#define EXT_PHY_PPLL_FB_DIVIDER_BIT8_MASK	0x80
+#define EXT_PHY_PPLL_POST_DIVIDER_MASK		0x30
+#define EXT_PHY_PPLL_LOCK_STATUS_MASK		0x01
+
+#define EXT_PHY_PPLL_BW				0xec
+
+#define EXT_PHY_SIGNAL_CTRL			0xee
+#define EXT_PHY_TRANSITION_CLK_EN_MASK		0x80
+#define EXT_PHY_TRANSITION_D0_EN_MASK		0x40
+#define EXT_PHY_TRANSITION_D1_EN_MASK		0x20
+#define EXT_PHY_TRANSITION_D2_EN_MASK		0x10
+#define EXT_PHY_LEVEL_CLK_EN_MASK		0x08
+#define EXT_PHY_LEVEL_D0_EN_MASK		0x04
+#define EXT_PHY_LEVEL_D1_EN_MASK		0x02
+#define EXT_PHY_LEVEL_D2_EN_MASK		0x01
+
+#define EXT_PHY_SLOPEBOOST			0xef
+#define EXT_PHY_SLOPEBOOST_CLK_MASK		0x03
+#define EXT_PHY_SLOPEBOOST_D0_MASK		0x0c
+#define EXT_PHY_SLOPEBOOST_D1_MASK		0x30
+#define EXT_PHY_SLOPEBOOST_D2_MASK		0xc0
+
+#define EXT_PHY_PREEMPHASIS			0xf0
+#define EXT_PHY_PREEMPHASIS_D0_MASK		0x03
+#define EXT_PHY_PREEMPHASIS_D1_MASK		0x0c
+#define EXT_PHY_PREEMPHASIS_D2_MASK		0x30
+
+#define EXT_PHY_LEVEL1				0xf1
+#define EXT_PHY_LEVEL_CLK_MASK			0xf0
+#define EXT_PHY_LEVEL_D2_MASK			0x0f
+
+#define EXT_PHY_LEVEL2				0xf2
+#define EXT_PHY_LEVEL_D1_MASK			0xf0
+#define EXT_PHY_LEVEL_D0_MASK			0x0f
+
+#define EXT_PHY_TERM_RESIS_AUTO			0xf4
+#define EXT_PHY_AUTO_R50_OHMS			0
+#define EXT_PHY_AUTO_R75_OHMS			(1 << 2)
+#define EXT_PHY_AUTO_R100_OHMS			(2 << 2)
+#define EXT_PHY_AUTO_ROPEN_CIRCUIT		(3 << 2)
+
+#define EXT_PHY_TERM_RESIS_MANUAL_CLK		0xfb
+#define EXT_PHY_TERM_RESIS_MANUAL_D2		0xfc
+#define EXT_PHY_TERM_RESIS_MANUAL_D1		0xfd
+#define EXT_PHY_TERM_RESIS_MANUAL_D0		0xfe
+
+#endif /* __DW_HDMI_ROCKCHIP__ */
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
index bae79f3..44084e8 100644
--- a/include/drm/bridge/dw_hdmi.h
+++ b/include/drm/bridge/dw_hdmi.h
@@ -24,6 +24,7 @@ enum {
 enum dw_hdmi_devtype {
 	IMX6Q_HDMI,
 	IMX6DL_HDMI,
+	RK3229_HDMI,
 	RK3288_HDMI,
 };
 
@@ -52,8 +53,10 @@ struct dw_hdmi_plat_data {
 	const struct dw_hdmi_mpll_config *mpll_cfg;
 	const struct dw_hdmi_curr_ctrl *cur_ctr;
 	const struct dw_hdmi_phy_config *phy_config;
-	enum drm_mode_status (*mode_valid)(struct drm_connector *connector,
+	enum drm_mode_status (*mode_valid)(const struct dw_hdmi_plat_data *plat_data,
 					   struct drm_display_mode *mode);
+	int (*extphy_config)(struct dw_hdmi_plat_data *plat_data,
+			     int bpp, int pixelclock);
 };
 
 void dw_hdmi_unbind(struct device *dev, struct device *master, void *data);
-- 
2.1.2




From ykk at rock-chips.com  Wed Jan  6 20:39:33 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Thu,  7 Jan 2016 12:39:33 +0800
Subject: [RFC PATCH v1 2/2] dt-bindings: add document for rk3229-hdmi
In-Reply-To: <1452141288-9292-1-git-send-email-ykk@rock-chips.com>
References: <1452141288-9292-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1452141573-9490-1-git-send-email-ykk@rock-chips.com>

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
 .../devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt         | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
index 668091f..1cdc627 100644
--- a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
+++ b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
@@ -2,7 +2,7 @@ Rockchip specific extensions to the Synopsys Designware HDMI
 ================================
 
 Required properties:
-- compatible: "rockchip,rk3288-dw-hdmi";
+- compatible: "rockchip,rk3288-dw-hdmi", "rockchip,rk3229-dw-hdmi";
 - reg: Physical base address and length of the controller's registers.
 - clocks: phandle to hdmi iahb and isfr clocks.
 - clock-names: should be "iahb" "isfr"
@@ -15,8 +15,10 @@ Required properties:
   rk3288 platform
 
 Optional properties
+- reg: Physical base address and length of the external HDMI PHY's registers.
 - ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
 - clocks, clock-names: phandle to the HDMI CEC clock, name should be "cec"
+		       phandle to the external HDMI PHY clock, name should be "extphy"
 
 Example:
 hdmi: hdmi at ff980000 {
-- 
2.1.2




From ykk at rock-chips.com  Wed Jan  6 22:22:02 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Thu, 7 Jan 2016 14:22:02 +0800
Subject: [RFC PATCH v1 1/2] drm: rockchip: hdmi: add RK3229 HDMI support
In-Reply-To: <1452141447-9441-1-git-send-email-ykk@rock-chips.com>
References: <1452141288-9292-1-git-send-email-ykk@rock-chips.com>
 <1452141447-9441-1-git-send-email-ykk@rock-chips.com>
Message-ID: <568E040A.4020907@rock-chips.com>

Thanks for "Kbuild test robot" reminding, I forget to update 
"mode_valid" function define in imx-hdmi side, would send new version out,

--

>> drivers/gpu/drm/imx/dw_hdmi-imx.c:181:2: warning: initialization from incompatible pointer type

      .mode_valid = imx6q_hdmi_mode_valid,
      ^
    drivers/gpu/drm/imx/dw_hdmi-imx.c:181:2: warning: (near initialization for 'imx6q_hdmi_drv_data.mode_valid')
    drivers/gpu/drm/imx/dw_hdmi-imx.c:189:2: warning: initialization from incompatible pointer type
      .mode_valid = imx6dl_hdmi_mode_valid,
      ^
    drivers/gpu/drm/imx/dw_hdmi-imx.c:189:2: warning: (near initialization for 'imx6dl_hdmi_drv_data.mode_valid')

Sorry,
- Yakir


On 01/07/2016 12:37 PM, Yakir Yang wrote:
> RK3229 integrate an DesignedWare HDMI2.0 controller and an INNO HDMI2.0 phy,
> the max output resolution is 4K.
>
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
> ---
>   drivers/gpu/drm/bridge/dw-hdmi.c            |  33 ++-
>   drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 380 +++++++++++++++++++++++++---
>   drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h | 137 ++++++++++
>   include/drm/bridge/dw_hdmi.h                |   5 +-
>   4 files changed, 516 insertions(+), 39 deletions(-)
>   create mode 100644 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h
>
> diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
> index 6fbec99..60b1dcf 100644
> --- a/drivers/gpu/drm/bridge/dw-hdmi.c
> +++ b/drivers/gpu/drm/bridge/dw-hdmi.c
> @@ -735,10 +735,12 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
>   {
>   	unsigned res_idx;
>   	u8 val, msec;
> +	int ret;
>   	const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
>   	const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg;
>   	const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr;
>   	const struct dw_hdmi_phy_config *phy_config = pdata->phy_config;
> +	int mpixelclock = hdmi->hdmi_data.video_mode.mpixelclock;
>   
>   	if (prep)
>   		return -EINVAL;
> @@ -758,27 +760,38 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
>   		return -EINVAL;
>   	}
>   
> +	if (hdmi->plat_data->extphy_config) {
> +		/* gen2 tx power off */
> +		dw_hdmi_phy_gen2_txpwron(hdmi, 0);
> +		dw_hdmi_phy_gen2_pddq(hdmi, 1);
> +
> +		ret = hdmi->plat_data->extphy_config(hdmi->plat_data, res_idx,
> +						     mpixelclock);
> +		/* gen2 tx power on */
> +		dw_hdmi_phy_gen2_txpwron(hdmi, 1);
> +		dw_hdmi_phy_gen2_pddq(hdmi, 0);
> +
> +		return ret;
> +	}
> +
>   	/* PLL/MPLL Cfg - always match on final entry */
>   	for (; mpll_config->mpixelclock != ~0UL; mpll_config++)
> -		if (hdmi->hdmi_data.video_mode.mpixelclock <=
> -		    mpll_config->mpixelclock)
> +		if (mpixelclock <= mpll_config->mpixelclock)
>   			break;
>   
>   	for (; curr_ctrl->mpixelclock != ~0UL; curr_ctrl++)
> -		if (hdmi->hdmi_data.video_mode.mpixelclock <=
> -		    curr_ctrl->mpixelclock)
> +		if (mpixelclock <= curr_ctrl->mpixelclock)
>   			break;
>   
>   	for (; phy_config->mpixelclock != ~0UL; phy_config++)
> -		if (hdmi->hdmi_data.video_mode.mpixelclock <=
> -		    phy_config->mpixelclock)
> +		if (mpixelclock <= phy_config->mpixelclock)
>   			break;
>   
>   	if (mpll_config->mpixelclock == ~0UL ||
>   	    curr_ctrl->mpixelclock == ~0UL ||
>   	    phy_config->mpixelclock == ~0UL) {
>   		dev_err(hdmi->dev, "Pixel clock %d - unsupported by HDMI\n",
> -			hdmi->hdmi_data.video_mode.mpixelclock);
> +			mpixelclock);
>   		return -EINVAL;
>   	}
>   
> @@ -1476,14 +1489,16 @@ dw_hdmi_connector_mode_valid(struct drm_connector *connector,
>   {
>   	struct dw_hdmi *hdmi = container_of(connector,
>   					   struct dw_hdmi, connector);
> +	struct dw_hdmi_plat_data *plat_data = hdmi->plat_data;
>   	enum drm_mode_status mode_status = MODE_OK;
>   
>   	/* We don't support double-clocked modes */
>   	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
>   		return MODE_BAD;
>   
> -	if (hdmi->plat_data->mode_valid)
> -		mode_status = hdmi->plat_data->mode_valid(connector, mode);
> +	if (plat_data->mode_valid)
> +		mode_status = plat_data->mode_valid(plat_data, mode);
> +
>   
>   	return mode_status;
>   }
> diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
> index c65ce8c..424d548 100644
> --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
> +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
> @@ -7,6 +7,7 @@
>    * (at your option) any later version.
>    */
>   
> +#include <linux/clk.h>
>   #include <linux/module.h>
>   #include <linux/platform_device.h>
>   #include <linux/mfd/syscon.h>
> @@ -21,18 +22,135 @@
>   #include "rockchip_drm_drv.h"
>   #include "rockchip_drm_vop.h"
>   
> -#define GRF_SOC_CON6                    0x025c
> -#define HDMI_SEL_VOP_LIT                (1 << 4)
> +#include "dw_hdmi-rockchip.h"
>   
>   struct rockchip_hdmi {
>   	struct device *dev;
>   	struct regmap *regmap;
>   	struct drm_encoder encoder;
> +	struct dw_hdmi_plat_data plat_data;
> +
> +	void __iomem *extphy_regbase;
> +	struct clk *extphy_pclk;
>   };
>   
>   #define to_rockchip_hdmi(x)	container_of(x, struct rockchip_hdmi, x)
>   
> -static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
> +static const struct extphy_config_tab rk3229_extphy_cfg[] = {
> +	{ .mpixelclock = 165000000,
> +	  .pre_emphasis = 0, .slopeboost = 0, .clk_level = 4,
> +	  .data0_level = 4, 4, 4,
> +	},
> +
> +	{ .mpixelclock = 225000000,
> +	  .pre_emphasis = 0, .slopeboost = 0, .clk_level = 6,
> +	  .data0_level = 6, 6, 6,
> +	},
> +
> +	{ .mpixelclock = 340000000,
> +	  .pre_emphasis = 1, .slopeboost = 0, .clk_level = 6,
> +	  .data0_level = 10, 10, 10,
> +	},
> +
> +	{ .mpixelclock = 594000000,
> +	  .pre_emphasis = 1, .slopeboost = 0, .clk_level = 7,
> +	  .data0_level = 10, 10, 10,
> +	},
> +
> +	{ .mpixelclock = ~0UL},
> +};
> +
> +static const struct extphy_pll_config_tab rk3229_extphy_pll_cfg[] = {
> +	{
> +		.mpixelclock = 27000000, .param = {
> +			{ .pll_nd = 1, .pll_nf = 45,
> +			  .tmsd_divider_a = 3, 1, 1,
> +			  .pclk_divider_a = 1, 3, 3, 4,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
> +			},
> +			{ .pll_nd = 1, .pll_nf = 45,
> +			  .tmsd_divider_a = 0, 3, 3,
> +			  .pclk_divider_a = 1, 3, 3, 4,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
> +			},
> +		},
> +	}, {
> +		.mpixelclock = 59400000, .param = {
> +			{ .pll_nd = 2, .pll_nf = 99,
> +			  .tmsd_divider_a = 3, 1, 1,
> +			  .pclk_divider_a = 1, 3, 2, 2,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
> +			},
> +			{ .pll_nd = 2, .pll_nf = 99,
> +			  .tmsd_divider_a = 1, 1, 1,
> +			  .pclk_divider_a = 1, 3, 2, 2,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
> +			},
> +		},
> +	}, {
> +		.mpixelclock = 74250000, .param = {
> +			{ .pll_nd = 2, .pll_nf = 99,
> +			  .tmsd_divider_a = 1, 1, 1,
> +			  .pclk_divider_a = 1, 2, 2, 2,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
> +			},
> +			{ .pll_nd = 4, .pll_nf = 495,
> +			  .tmsd_divider_a = 1, 2, 2,
> +			  .pclk_divider_a = 1, 3, 3, 4,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 2, .ppll_nf = 40, .ppll_no = 4,
> +			},
> +		},
> +	}, {
> +		.mpixelclock = 148500000, .param = {
> +			{ .pll_nd = 2, .pll_nf = 99,
> +			  .tmsd_divider_a = 1, 0, 0,
> +			  .pclk_divider_a = 1, 2, 1, 1,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 2, .ppll_nf = 40, .ppll_no = 4,
> +			},
> +			{ .pll_nd = 4, .pll_nf = 495,
> +			  .tmsd_divider_a = 0, 2, 2,
> +			  .pclk_divider_a = 1, 3, 2, 2,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 4, .ppll_nf = 40, .ppll_no = 2,
> +			},
> +		},
> +	}, {
> +		.mpixelclock = 297000000, .param = {
> +			{ .pll_nd = 2, .pll_nf = 99,
> +			  .tmsd_divider_a = 0, 0, 0,
> +			  .pclk_divider_a = 1, 0, 1, 1,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 4, .ppll_nf = 40, .ppll_no = 2,
> +			},
> +			{ .pll_nd = 4, .pll_nf = 495,
> +			  .tmsd_divider_a = 1, 2, 0,
> +			  .pclk_divider_a = 1, 3, 1, 1,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 8, .ppll_nf = 40, .ppll_no = 1,
> +			},
> +		},
> +	}, {
> +		.mpixelclock = 594000000, .param = {
> +			{ .pll_nd = 1, .pll_nf = 99,
> +			  .tmsd_divider_a = 0, 2, 0,
> +			  .pclk_divider_a = 1, 0, 1, 1,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 8, .ppll_nf = 40, .ppll_no = 1,
> +			},
> +		}
> +	}, {
> +		.mpixelclock = ~0UL,
> +	}
> +};
> +
> +static const struct dw_hdmi_mpll_config rk3288_mpll_cfg[] = {
>   	{
>   		27000000, {
>   			{ 0x00b3, 0x0000},
> @@ -112,7 +230,7 @@ static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
>   	}
>   };
>   
> -static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = {
> +static const struct dw_hdmi_curr_ctrl rk3288_cur_ctr[] = {
>   	/*      pixelclk    bpp8    bpp10   bpp12 */
>   	{
>   		40000000,  { 0x0018, 0x0018, 0x0018 },
> @@ -133,7 +251,7 @@ static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = {
>   	}
>   };
>   
> -static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
> +static const struct dw_hdmi_phy_config rk3288_phy_config[] = {
>   	/*pixelclk   symbol   term   vlev*/
>   	{ 74250000,  0x8009, 0x0004, 0x0272},
>   	{ 148500000, 0x802b, 0x0004, 0x028d},
> @@ -141,9 +259,158 @@ static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
>   	{ ~0UL,	     0x0000, 0x0000, 0x0000}
>   };
>   
> +static int hdmi_ext_phy_write(struct rockchip_hdmi *hdmi, unsigned short data,
> +			      unsigned char addr)
> +{
> +	writel_relaxed(data, hdmi->extphy_regbase + (addr) * 0x04);
> +	return 0;
> +}
> +
> +static unsigned int hdmi_phy_i2c_read(struct rockchip_hdmi *hdmi,
> +				      unsigned char addr)
> +{
> +	return readl_relaxed(hdmi->extphy_regbase + (addr) * 0x04);
> +}
> +
> +static int rk3229_extphy_config(struct dw_hdmi_plat_data *plat_data,
> +				  int res, int pixelclock)
> +{
> +	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(plat_data);
> +	const struct extphy_pll_config_tab *mpll = rk3229_extphy_pll_cfg;
> +	const struct extphy_config_tab *ctrl = rk3229_extphy_cfg;
> +	unsigned long timeout;
> +	int i, stat;
> +
> +	/* Find out the extphy MPLL configure parameters */
> +	for (i = 0; mpll[i].mpixelclock != ~0UL; i++)
> +		if (pixelclock == mpll[i].mpixelclock)
> +			break;
> +	if (mpll[i].mpixelclock == ~0UL) {
> +		dev_err(hdmi->dev, "Extphy couldn't support %dHz\n", pixelclock);
> +		return -EINVAL;
> +	}
> +
> +	regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2,
> +		     RK3229_PLL_POWER_DOWN | RK3229_PLL_PDATA_DEN);
> +
> +	/*
> +	 * Configure external phy PLL registers.
> +	 */
> +	stat = ((mpll[i].param[res].pll_nf >> 1) & EXT_PHY_PLL_FB_BIT8_MASK) |
> +	       ((mpll[i].param[res].vco_div_5 & 1) << 5) |
> +	       (mpll[i].param[res].pll_nd & EXT_PHY_PLL_PRE_DIVIDER_MASK);
> +	hdmi_ext_phy_write(hdmi, stat, EXT_PHY_PLL_PRE_DIVIDER);
> +
> +	hdmi_ext_phy_write(hdmi, mpll[i].param[res].pll_nf, EXT_PHY_PLL_FB_DIVIDER);
> +
> +	stat = (mpll[i].param[res].pclk_divider_a & EXT_PHY_PCLK_DIVIDERA_MASK) |
> +	       ((mpll[i].param[res].pclk_divider_b & 3) << 5);
> +	hdmi_ext_phy_write(hdmi, stat, EXT_PHY_PCLK_DIVIDER1);
> +
> +	stat = (mpll[i].param[res].pclk_divider_d & EXT_PHY_PCLK_DIVIDERD_MASK) |
> +	       ((mpll[i].param[res].pclk_divider_c & 3) << 5);
> +	hdmi_ext_phy_write(hdmi, stat, EXT_PHY_PCLK_DIVIDER2);
> +
> +	stat = ((mpll[i].param[res].tmsd_divider_c & 3) << 4) |
> +	       ((mpll[i].param[res].tmsd_divider_a & 3) << 2) |
> +	       (mpll[i].param[res].tmsd_divider_b & 3);
> +	hdmi_ext_phy_write(hdmi, stat, EXT_PHY_TMDSCLK_DIVIDER);
> +
> +	hdmi_ext_phy_write(hdmi, mpll[i].param[res].ppll_nf, EXT_PHY_PPLL_FB_DIVIDER);
> +
> +	if (mpll[i].param[res].ppll_no == 1) {
> +		hdmi_ext_phy_write(hdmi, 0, EXT_PHY_PPLL_POST_DIVIDER);
> +
> +		stat = 0x20 | mpll[i].param[res].ppll_nd;
> +		hdmi_ext_phy_write(hdmi, stat, EXT_PHY_PPLL_PRE_DIVIDER);
> +	} else {
> +		stat = ((mpll[i].param[res].ppll_no / 2) - 1) << 4;
> +		hdmi_ext_phy_write(hdmi, stat, EXT_PHY_PPLL_POST_DIVIDER);
> +
> +		stat = 0xe0 | mpll[i].param[res].ppll_nd;
> +		hdmi_ext_phy_write(hdmi, stat, EXT_PHY_PPLL_PRE_DIVIDER);
> +	}
> +
> +
> +	/* Find out the extphy driver configure parameters */
> +	for (i = 0; ctrl[i].mpixelclock != ~0UL; i++)
> +		if (pixelclock <= ctrl[i].mpixelclock)
> +			break;
> +	if (ctrl[i].mpixelclock == ~0UL) {
> +		dev_err(hdmi->dev, "Extphy couldn't support %dHz\n", pixelclock);
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * Configure the extphy driver registers.
> +	 */
> +	if (ctrl[i].slopeboost) {
> +		hdmi_ext_phy_write(hdmi, 0xff, EXT_PHY_SIGNAL_CTRL);
> +
> +		stat = (ctrl[i].slopeboost - 1) & 3;
> +		stat = (stat << 6) | (stat << 4) | (stat << 2) | stat;
> +		hdmi_ext_phy_write(hdmi, stat, EXT_PHY_SLOPEBOOST);
> +	} else
> +		hdmi_ext_phy_write(hdmi, 0x0f, EXT_PHY_SIGNAL_CTRL);
> +
> +	stat = ctrl[i].pre_emphasis & 3;
> +	stat = (stat << 4) | (stat << 2) | stat;
> +	hdmi_ext_phy_write(hdmi, stat, EXT_PHY_PREEMPHASIS);
> +
> +	stat = ((ctrl[i].clk_level & 0xf) << 4) | (ctrl[i].data2_level & 0xf);
> +	hdmi_ext_phy_write(hdmi, stat, EXT_PHY_LEVEL1);
> +
> +	stat = ((ctrl[i].data1_level & 0xf) << 4) | (ctrl[i].data0_level & 0xf);
> +	hdmi_ext_phy_write(hdmi, stat, EXT_PHY_LEVEL2);
> +
> +	hdmi_ext_phy_write(hdmi, 0x22, 0xf3);
> +
> +	stat = clk_get_rate(hdmi->extphy_pclk) / 100000;
> +	hdmi_ext_phy_write(hdmi, ((stat >> 8) & 0xff) | 0x80, EXT_PHY_TERM_CAL);
> +	hdmi_ext_phy_write(hdmi,  stat & 0xff, EXT_PHY_TERM_CAL_DIV_L);
> +
> +	if (pixelclock > 340000000)
> +		stat = EXT_PHY_AUTO_R100_OHMS;
> +	else if (pixelclock > 200000000)
> +		stat = EXT_PHY_AUTO_R50_OHMS;
> +	else
> +		stat = EXT_PHY_AUTO_ROPEN_CIRCUIT;
> +	hdmi_ext_phy_write(hdmi, stat | 0x20, EXT_PHY_TERM_RESIS_AUTO);
> +	hdmi_ext_phy_write(hdmi, (stat >> 8) & 0xff, EXT_PHY_TERM_CAL);
> +
> +	stat = (pixelclock > 200000000) ? 0 : 0x11;
> +	hdmi_ext_phy_write(hdmi, stat, EXT_PHY_PLL_BW);
> +	hdmi_ext_phy_write(hdmi, 0x27, EXT_PHY_PPLL_BW);
> +
> +	regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2, RK3229_PLL_POWER_UP);
> +
> +	/* Detect whether PLL is lock or not */
> +	timeout = jiffies + msecs_to_jiffies(100);
> +	while (!time_after(jiffies, timeout)) {
> +		usleep_range(1000, 2000);
> +		stat = hdmi_phy_i2c_read(hdmi, EXT_PHY_PPLL_POST_DIVIDER);
> +		if (stat & EXT_PHY_PPLL_LOCK_STATUS_MASK)
> +			break;
> +	}
> +
> +	regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2, RK3229_PLL_PDATA_EN);
> +
> +	if ((stat & EXT_PHY_PPLL_LOCK_STATUS_MASK) == 0) {
> +		dev_err(hdmi->dev, "EXT PHY PLL not locked\n");
> +		return -EBUSY;
> +	}
> +
> +	return 0;
> +}
> +
>   static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
>   {
>   	struct device_node *np = hdmi->dev->of_node;
> +	struct platform_device *pdev;
> +	struct resource *iores;
> +	int ret;
> +
> +	pdev = container_of(hdmi->dev, struct platform_device, dev);
>   
>   	hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
>   	if (IS_ERR(hdmi->regmap)) {
> @@ -151,24 +418,62 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
>   		return PTR_ERR(hdmi->regmap);
>   	}
>   
> +	if (hdmi->plat_data.dev_type == RK3229_HDMI) {
> +		iores = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +		if (!iores)
> +			return -ENXIO;
> +
> +		hdmi->extphy_regbase = devm_ioremap_resource(hdmi->dev, iores);
> +		if (IS_ERR(hdmi->extphy_regbase)) {
> +			dev_err(hdmi->dev, "failed to map extphy regbase\n");
> +			return PTR_ERR(hdmi->extphy_regbase);
> +		}
> +
> +		hdmi->extphy_pclk = devm_clk_get(hdmi->dev, "extphy");
> +		if (IS_ERR(hdmi->extphy_pclk)) {
> +			dev_err(hdmi->dev, "failed to get extphy clock\n");
> +			return PTR_ERR(hdmi->extphy_pclk);
> +		}
> +
> +		ret = clk_prepare_enable(hdmi->extphy_pclk);
> +		if (ret) {
> +			dev_err(hdmi->dev, "failed to enable extphy clk: %d\n",
> +				ret);
> +			return ret;
> +		}
> +
> +		regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON6,
> +			     RK3229_IO_3V_DOMAIN);
> +
> +		regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2,
> +			     RK3229_DDC_MASK_EN);
> +	}
> +
>   	return 0;
>   }
>   
>   static enum drm_mode_status
> -dw_hdmi_rockchip_mode_valid(struct drm_connector *connector,
> +dw_hdmi_rockchip_mode_valid(const struct dw_hdmi_plat_data *plat_data,
>   			    struct drm_display_mode *mode)
>   {
> -	const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg;
>   	int pclk = mode->clock * 1000;
>   	bool valid = false;
>   	int i;
>   
> -	for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) {
> -		if (pclk == mpll_cfg[i].mpixelclock) {
> -			valid = true;
> -			break;
> +	if (plat_data->dev_type == RK3288_HDMI)
> +		for (i = 0; rk3288_mpll_cfg[i].mpixelclock != (~0UL); i++)
> +			if (pclk == rk3288_mpll_cfg[i].mpixelclock) {
> +				valid = true;
> +				break;
> +			}
> +
> +	if (plat_data->dev_type == RK3229_HDMI)
> +		for (i = 0; rk3229_extphy_pll_cfg[i].mpixelclock != ~0UL; i++) {
> +			if (pclk == rk3229_extphy_pll_cfg[i].mpixelclock) {
> +				valid = true;
> +				break;
> +			}
>   		}
> -	}
>   
>   	return (valid) ? MODE_OK : MODE_BAD;
>   }
> @@ -198,21 +503,29 @@ static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
>   static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
>   {
>   	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
> +	int out_mode = ROCKCHIP_OUT_MODE_AAAA;
>   	u32 val;
>   	int mux;
>   
> +	if (hdmi->plat_data.dev_type == RK3229_HDMI)
> +		out_mode = ROCKCHIP_OUT_MODE_P888;
> +
>   	rockchip_drm_crtc_mode_config(encoder->crtc, DRM_MODE_CONNECTOR_HDMIA,
> -				      ROCKCHIP_OUT_MODE_AAAA);
> +				      out_mode);
>   
> -	mux = rockchip_drm_encoder_get_mux_id(hdmi->dev->of_node, encoder);
> -	if (mux)
> -		val = HDMI_SEL_VOP_LIT | (HDMI_SEL_VOP_LIT << 16);
> -	else
> -		val = HDMI_SEL_VOP_LIT << 16;
> +	if (hdmi->plat_data.dev_type == RK3288_HDMI) {
> +		mux = rockchip_drm_encoder_get_mux_id(hdmi->dev->of_node,
> +						      encoder);
> +		if (mux)
> +			val = RK3288_HDMI_SEL_VOP_LIT |
> +			      (RK3288_HDMI_SEL_VOP_LIT << 16);
> +		else
> +			val = RK3288_HDMI_SEL_VOP_LIT << 16;
>   
> -	regmap_write(hdmi->regmap, GRF_SOC_CON6, val);
> -	dev_dbg(hdmi->dev, "vop %s output to hdmi\n",
> -		(mux) ? "LIT" : "BIG");
> +		regmap_write(hdmi->regmap, RK3288_GRF_SOC_CON6, val);
> +
> +		dev_dbg(hdmi->dev, "vop %s output to hdmi\n", (mux) ? "LIT" : "BIG");
> +	}
>   }
>   
>   static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = {
> @@ -222,17 +535,26 @@ static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_fun
>   	.disable    = dw_hdmi_rockchip_encoder_disable,
>   };
>   
> -static const struct dw_hdmi_plat_data rockchip_hdmi_drv_data = {
> +static const struct dw_hdmi_plat_data rk3288_hdmi_drv_data = {
>   	.mode_valid = dw_hdmi_rockchip_mode_valid,
> -	.mpll_cfg   = rockchip_mpll_cfg,
> -	.cur_ctr    = rockchip_cur_ctr,
> -	.phy_config = rockchip_phy_config,
> +	.mpll_cfg   = rk3288_mpll_cfg,
> +	.cur_ctr    = rk3288_cur_ctr,
> +	.phy_config = rk3288_phy_config,
>   	.dev_type   = RK3288_HDMI,
>   };
>   
> +static const struct dw_hdmi_plat_data rk3229_hdmi_drv_data = {
> +	.mode_valid = dw_hdmi_rockchip_mode_valid,
> +	.extphy_config = rk3229_extphy_config,
> +	.dev_type   = RK3229_HDMI,
> +};
> +
>   static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
>   	{ .compatible = "rockchip,rk3288-dw-hdmi",
> -	  .data = &rockchip_hdmi_drv_data
> +	  .data = &rk3288_hdmi_drv_data
> +	},
> +	{ .compatible = "rockchip,rk3229-dw-hdmi",
> +	  .data = &rk3229_hdmi_drv_data
>   	},
>   	{},
>   };
> @@ -242,7 +564,6 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
>   				 void *data)
>   {
>   	struct platform_device *pdev = to_platform_device(dev);
> -	const struct dw_hdmi_plat_data *plat_data;
>   	const struct of_device_id *match;
>   	struct drm_device *drm = data;
>   	struct drm_encoder *encoder;
> @@ -259,7 +580,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
>   		return -ENOMEM;
>   
>   	match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node);
> -	plat_data = match->data;
> +	hdmi->plat_data = *(struct dw_hdmi_plat_data *)(match->data);
>   	hdmi->dev = &pdev->dev;
>   	encoder = &hdmi->encoder;
>   
> @@ -293,7 +614,8 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
>   	drm_encoder_init(drm, encoder, &dw_hdmi_rockchip_encoder_funcs,
>   			 DRM_MODE_ENCODER_TMDS, NULL);
>   
> -	return dw_hdmi_bind(dev, master, data, encoder, iores, irq, plat_data);
> +	return dw_hdmi_bind(dev, master, data, encoder, iores, irq,
> +			    &hdmi->plat_data);
>   }
>   
>   static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master,
> diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h
> new file mode 100644
> index 0000000..aca2543
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h
> @@ -0,0 +1,137 @@
> +#ifndef __DW_HDMI_ROCKCHIP__
> +#define __DW_HDMI_ROCKCHIP__
> +
> +struct extphy_config_tab {
> +	u32 mpixelclock;
> +	int pre_emphasis;
> +	int slopeboost;
> +	int clk_level;
> +	int data0_level;
> +	int data1_level;
> +	int data2_level;
> +};
> +
> +struct extphy_pll_config_tab {
> +	unsigned long mpixelclock;
> +	struct {
> +		u8	pll_nd;
> +		u16	pll_nf;
> +		u8	tmsd_divider_a;
> +		u8	tmsd_divider_b;
> +		u8	tmsd_divider_c;
> +		u8	pclk_divider_a;
> +		u8	pclk_divider_b;
> +		u8	pclk_divider_c;
> +		u8	pclk_divider_d;
> +		u8	vco_div_5;
> +		u8	ppll_nd;
> +		u8	ppll_nf;
> +		u8	ppll_no;
> +	} param[DW_HDMI_RES_MAX];
> +};
> +
> +#define RK3288_GRF_SOC_CON6			0x025c
> +#define RK3288_HDMI_SEL_VOP_LIT			(1 << 4)
> +
> +#define RK3229_GRF_SOC_CON6			0x0418
> +#define RK3229_IO_3V_DOMAIN			((7 << 4) | (7 << (4 + 16)))
> +
> +#define RK3229_GRF_SOC_CON2			0x0408
> +#define RK3229_DDC_MASK_EN			((3 << 13) | (3 << (13 + 16)))
> +
> +#define RK3229_PLL_POWER_DOWN			(BIT(12) | BIT(12 + 16))
> +#define RK3229_PLL_POWER_UP			BIT(12 + 16)
> +#define RK3229_PLL_PDATA_DEN			BIT(11 + 16)
> +#define RK3229_PLL_PDATA_EN			(BIT(11) | BIT(11 + 16))
> +
> +/* PHY Defined for RK322X */
> +#define EXT_PHY_CONTROL				0
> +#define EXT_PHY_ANALOG_RESET_MASK		0x80
> +#define EXT_PHY_DIGITAL_RESET_MASK		0x40
> +#define EXT_PHY_PCLK_INVERT_MASK		0x08
> +#define EXT_PHY_PREPCLK_INVERT_MASK		0x04
> +#define EXT_PHY_TMDSCLK_INVERT_MASK		0x02
> +#define EXT_PHY_SRC_SELECT_MASK			0x01
> +
> +#define EXT_PHY_TERM_CAL			0x03
> +#define EXT_PHY_TERM_CAL_EN_MASK		0x80
> +#define EXT_PHY_TERM_CAL_DIV_H_MASK		0x7f
> +
> +#define EXT_PHY_TERM_CAL_DIV_L			0x04
> +
> +#define EXT_PHY_PLL_PRE_DIVIDER			0xe2
> +#define EXT_PHY_PLL_FB_BIT8_MASK		0x80
> +#define EXT_PHY_PLL_PCLK_DIV5_EN_MASK		0x20
> +#define EXT_PHY_PLL_PRE_DIVIDER_MASK		0x1f
> +
> +#define EXT_PHY_PLL_FB_DIVIDER			0xe3
> +
> +#define EXT_PHY_PCLK_DIVIDER1			0xe4
> +#define EXT_PHY_PCLK_DIVIDERB_MASK		0x60
> +#define EXT_PHY_PCLK_DIVIDERA_MASK		0x1f
> +
> +#define EXT_PHY_PCLK_DIVIDER2			0xe5
> +#define EXT_PHY_PCLK_DIVIDERC_MASK		0x60
> +#define EXT_PHY_PCLK_DIVIDERD_MASK		0x1f
> +
> +#define EXT_PHY_TMDSCLK_DIVIDER			0xe6
> +#define EXT_PHY_TMDSCLK_DIVIDERC_MASK		0x30
> +#define EXT_PHY_TMDSCLK_DIVIDERA_MASK		0x0c
> +#define EXT_PHY_TMDSCLK_DIVIDERB_MASK		0x03
> +
> +#define EXT_PHY_PLL_BW				0xe7
> +
> +#define EXT_PHY_PPLL_PRE_DIVIDER		0xe9
> +#define EXT_PHY_PPLL_ENABLE_MASK		0xc0
> +#define EXT_PHY_PPLL_PRE_DIVIDER_MASK		0x1f
> +
> +#define EXT_PHY_PPLL_FB_DIVIDER			0xea
> +
> +#define EXT_PHY_PPLL_POST_DIVIDER		0xeb
> +#define EXT_PHY_PPLL_FB_DIVIDER_BIT8_MASK	0x80
> +#define EXT_PHY_PPLL_POST_DIVIDER_MASK		0x30
> +#define EXT_PHY_PPLL_LOCK_STATUS_MASK		0x01
> +
> +#define EXT_PHY_PPLL_BW				0xec
> +
> +#define EXT_PHY_SIGNAL_CTRL			0xee
> +#define EXT_PHY_TRANSITION_CLK_EN_MASK		0x80
> +#define EXT_PHY_TRANSITION_D0_EN_MASK		0x40
> +#define EXT_PHY_TRANSITION_D1_EN_MASK		0x20
> +#define EXT_PHY_TRANSITION_D2_EN_MASK		0x10
> +#define EXT_PHY_LEVEL_CLK_EN_MASK		0x08
> +#define EXT_PHY_LEVEL_D0_EN_MASK		0x04
> +#define EXT_PHY_LEVEL_D1_EN_MASK		0x02
> +#define EXT_PHY_LEVEL_D2_EN_MASK		0x01
> +
> +#define EXT_PHY_SLOPEBOOST			0xef
> +#define EXT_PHY_SLOPEBOOST_CLK_MASK		0x03
> +#define EXT_PHY_SLOPEBOOST_D0_MASK		0x0c
> +#define EXT_PHY_SLOPEBOOST_D1_MASK		0x30
> +#define EXT_PHY_SLOPEBOOST_D2_MASK		0xc0
> +
> +#define EXT_PHY_PREEMPHASIS			0xf0
> +#define EXT_PHY_PREEMPHASIS_D0_MASK		0x03
> +#define EXT_PHY_PREEMPHASIS_D1_MASK		0x0c
> +#define EXT_PHY_PREEMPHASIS_D2_MASK		0x30
> +
> +#define EXT_PHY_LEVEL1				0xf1
> +#define EXT_PHY_LEVEL_CLK_MASK			0xf0
> +#define EXT_PHY_LEVEL_D2_MASK			0x0f
> +
> +#define EXT_PHY_LEVEL2				0xf2
> +#define EXT_PHY_LEVEL_D1_MASK			0xf0
> +#define EXT_PHY_LEVEL_D0_MASK			0x0f
> +
> +#define EXT_PHY_TERM_RESIS_AUTO			0xf4
> +#define EXT_PHY_AUTO_R50_OHMS			0
> +#define EXT_PHY_AUTO_R75_OHMS			(1 << 2)
> +#define EXT_PHY_AUTO_R100_OHMS			(2 << 2)
> +#define EXT_PHY_AUTO_ROPEN_CIRCUIT		(3 << 2)
> +
> +#define EXT_PHY_TERM_RESIS_MANUAL_CLK		0xfb
> +#define EXT_PHY_TERM_RESIS_MANUAL_D2		0xfc
> +#define EXT_PHY_TERM_RESIS_MANUAL_D1		0xfd
> +#define EXT_PHY_TERM_RESIS_MANUAL_D0		0xfe
> +
> +#endif /* __DW_HDMI_ROCKCHIP__ */
> diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
> index bae79f3..44084e8 100644
> --- a/include/drm/bridge/dw_hdmi.h
> +++ b/include/drm/bridge/dw_hdmi.h
> @@ -24,6 +24,7 @@ enum {
>   enum dw_hdmi_devtype {
>   	IMX6Q_HDMI,
>   	IMX6DL_HDMI,
> +	RK3229_HDMI,
>   	RK3288_HDMI,
>   };
>   
> @@ -52,8 +53,10 @@ struct dw_hdmi_plat_data {
>   	const struct dw_hdmi_mpll_config *mpll_cfg;
>   	const struct dw_hdmi_curr_ctrl *cur_ctr;
>   	const struct dw_hdmi_phy_config *phy_config;
> -	enum drm_mode_status (*mode_valid)(struct drm_connector *connector,
> +	enum drm_mode_status (*mode_valid)(const struct dw_hdmi_plat_data *plat_data,
>   					   struct drm_display_mode *mode);
> +	int (*extphy_config)(struct dw_hdmi_plat_data *plat_data,
> +			     int bpp, int pixelclock);
>   };
>   
>   void dw_hdmi_unbind(struct device *dev, struct device *master, void *data);




From wdc at rock-chips.com  Wed Jan  6 23:41:38 2016
From: wdc at rock-chips.com (David Wu)
Date: Thu,  7 Jan 2016 15:41:38 +0800
Subject: [PATCH v2] pinctrl: rockchip: add support for the rk3399
In-Reply-To: <1451997055-47655-1-git-send-email-wdc@rock-chips.com>
References: <1451997055-47655-1-git-send-email-wdc@rock-chips.com>
Message-ID: <1452152498-21780-1-git-send-email-wdc@rock-chips.com>

From: David Wu <david.wu at rock-chips.com>

The pinctrl of rk3399 is much different from other's,
especially the 3bits of drive strength.

Signed-off-by: David Wu <david.wu at rock-chips.com>
---
change from v1:
- need spin_unlock_irqrestore for set drive default case

 .../bindings/pinctrl/rockchip,pinctrl.txt          |   1 +
 drivers/pinctrl/pinctrl-rockchip.c                 | 339 ++++++++++++++++++++-
 2 files changed, 326 insertions(+), 14 deletions(-)

diff --git a/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
index 391ef4b..3bb9456 100644
--- a/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
+++ b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
@@ -22,6 +22,7 @@ Required properties for iomux controller:
   - compatible: one of "rockchip,rk2928-pinctrl", "rockchip,rk3066a-pinctrl"
 		       "rockchip,rk3066b-pinctrl", "rockchip,rk3188-pinctrl"
 		       "rockchip,rk3288-pinctrl", "rockchip,rk3368-pinctrl"
+		       "rockchip,rk3399-pinctrl"
   - rockchip,grf: phandle referencing a syscon providing the
 	 "general register files"
 
diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c
index a065112..9d61c6e 100644
--- a/drivers/pinctrl/pinctrl-rockchip.c
+++ b/drivers/pinctrl/pinctrl-rockchip.c
@@ -64,6 +64,7 @@ enum rockchip_pinctrl_type {
 	RK3188,
 	RK3288,
 	RK3368,
+	RK3399,
 };
 
 /**
@@ -86,6 +87,31 @@ struct rockchip_iomux {
 };
 
 /**
+ * enum type index corresponding to rockchip_perpin_drv_list arrays index.
+ */
+enum rockchip_pin_drv_type {
+	DRV_TYPE_IO_DEFAULT = 0,
+	DRV_TYPE_IO_1V8_OR_3V0,
+	DRV_TYPE_IO_1V8_ONLY,
+	DRV_TYPE_IO_1V8_3V0_AUTO,
+	DRV_TYPE_IO_3V3_ONLY,
+	DRV_TYPE_MAX
+};
+
+/**
+ * @drv_type: drive strength variant using rockchip_perpin_drv_type
+ * @offset: if initialized to -1 it will be autocalculated, by specifying
+ *	    an initial offset value the relevant source offset can be reset
+ *	    to a new value for autocalculating the following drive strength
+ *	    registers. if used chips own cal_drv func instead to calculate
+ *	    registers offset, the variant could be ignored.
+ */
+struct rockchip_drv {
+	enum rockchip_pin_drv_type	drv_type;
+	int				offset;
+};
+
+/**
  * @reg_base: register base of the gpio bank
  * @reg_pull: optional separate register for additional pull settings
  * @clk: clock of the gpio bank
@@ -96,6 +122,7 @@ struct rockchip_iomux {
  * @name: name of the bank
  * @bank_num: number of the bank, to account for holes
  * @iomux: array describing the 4 iomux sources of the bank
+ * @drv: array describing the 4 drive strength sources of the bank
  * @valid: are all necessary informations present
  * @of_node: dt node of this bank
  * @drvdata: common pinctrl basedata
@@ -115,6 +142,7 @@ struct rockchip_pin_bank {
 	char				*name;
 	u8				bank_num;
 	struct rockchip_iomux		iomux[4];
+	struct rockchip_drv		drv[4];
 	bool				valid;
 	struct device_node		*of_node;
 	struct rockchip_pinctrl		*drvdata;
@@ -151,6 +179,47 @@ struct rockchip_pin_bank {
 		},							\
 	}
 
+#define PIN_BANK_DRV_FLAGS(id, pins, label, type0, type1, type2, type3) \
+	{								\
+		.bank_num	= id,					\
+		.nr_pins	= pins,					\
+		.name		= label,				\
+		.iomux		= {					\
+			{ .offset = -1 },				\
+			{ .offset = -1 },				\
+			{ .offset = -1 },				\
+			{ .offset = -1 },				\
+		},							\
+		.drv		= {					\
+			{ .drv_type = type0, .offset = -1 },		\
+			{ .drv_type = type1, .offset = -1 },		\
+			{ .drv_type = type2, .offset = -1 },		\
+			{ .drv_type = type3, .offset = -1 },		\
+		},							\
+	}
+
+#define PIN_BANK_IOMUX_DRV_FLAGS_OFFSET(id, pins, label, iom0, iom1,	\
+					iom2, iom3, drv0, drv1, drv2,	\
+					drv3, offset0, offset1,		\
+					offset2, offset3)		\
+	{								\
+		.bank_num	= id,					\
+		.nr_pins	= pins,					\
+		.name		= label,				\
+		.iomux		= {					\
+			{ .type = iom0, .offset = -1 },			\
+			{ .type = iom1, .offset = -1 },			\
+			{ .type = iom2, .offset = -1 },			\
+			{ .type = iom3, .offset = -1 },			\
+		},							\
+		.drv		= {					\
+			{ .drv_type = drv0, .offset = offset0 },	\
+			{ .drv_type = drv1, .offset = offset1 },	\
+			{ .drv_type = drv2, .offset = offset2 },	\
+			{ .drv_type = drv3, .offset = offset3 },	\
+		},							\
+	}
+
 /**
  */
 struct rockchip_pin_ctrl {
@@ -161,6 +230,9 @@ struct rockchip_pin_ctrl {
 	enum rockchip_pinctrl_type	type;
 	int				grf_mux_offset;
 	int				pmu_mux_offset;
+	int				grf_drv_offset;
+	int				pmu_drv_offset;
+
 	void	(*pull_calc_reg)(struct rockchip_pin_bank *bank,
 				    int pin_num, struct regmap **regmap,
 				    int *reg, u8 *bit);
@@ -676,7 +748,71 @@ static void rk3368_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
 	}
 }
 
-static int rockchip_perpin_drv_list[] = { 2, 4, 8, 12 };
+#define RK3399_PULL_GRF_OFFSET		0xe040
+#define RK3399_PULL_PMU_OFFSET		0x40
+#define RK3399_DRV_3BITS_PER_PIN	3
+
+static void rk3399_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+					 int pin_num, struct regmap **regmap,
+					 int *reg, u8 *bit)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+
+	/* The bank0:16 and bank1:32 pins are located in PMU */
+	if ((bank->bank_num == 0) || (bank->bank_num == 1)) {
+		*regmap = info->regmap_pmu;
+		*reg = RK3399_PULL_PMU_OFFSET;
+
+		*reg += bank->bank_num * RK3188_PULL_BANK_STRIDE;
+
+		*reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4);
+		*bit = pin_num % RK3188_PULL_PINS_PER_REG;
+		*bit *= RK3188_PULL_BITS_PER_PIN;
+	} else {
+		*regmap = info->regmap_base;
+		*reg = RK3399_PULL_GRF_OFFSET;
+
+		/* correct the offset, as we're starting with the 3rd bank */
+		*reg -= 0x20;
+		*reg += bank->bank_num * RK3188_PULL_BANK_STRIDE;
+		*reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4);
+
+		*bit = (pin_num % RK3188_PULL_PINS_PER_REG);
+		*bit *= RK3188_PULL_BITS_PER_PIN;
+	}
+}
+
+static void rk3399_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
+					int pin_num, struct regmap **regmap,
+					int *reg, u8 *bit)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+	int drv_num = (pin_num / 8);
+
+	/*  The bank0:16 and bank1:32 pins are located in PMU */
+	if ((bank->bank_num == 0) || (bank->bank_num == 1))
+		*regmap = info->regmap_pmu;
+	else
+		*regmap = info->regmap_base;
+
+	*reg = bank->drv[drv_num].offset;
+	if ((bank->drv[drv_num].drv_type == DRV_TYPE_IO_1V8_3V0_AUTO) ||
+	    (bank->drv[drv_num].drv_type == DRV_TYPE_IO_3V3_ONLY)) {
+		if ((pin_num % 8) >= 6)
+			*reg += 0x4;
+		*bit = ((pin_num % 8) * 3) % 16;
+	} else {
+		*bit = (pin_num % 8) * 2;
+	}
+}
+
+static int rockchip_perpin_drv_list[DRV_TYPE_MAX][8] = {
+	{ 2, 4, 8, 12, -1, -1, -1, -1 },
+	{ 3, 6, 9, 12, -1, -1, -1, -1 },
+	{ 5, 10, 15, 20, -1, -1, -1, -1 },
+	{ 4, 6, 8, 10, 12, 14, 16, 18 },
+	{ 4, 7, 10, 13, 16, 19, 22, 26 }
+};
 
 static int rockchip_get_drive_perpin(struct rockchip_pin_bank *bank,
 				     int pin_num)
@@ -685,19 +821,60 @@ static int rockchip_get_drive_perpin(struct rockchip_pin_bank *bank,
 	struct rockchip_pin_ctrl *ctrl = info->ctrl;
 	struct regmap *regmap;
 	int reg, ret;
-	u32 data;
+	u32 data, temp, rmask_bits;
 	u8 bit;
+	int drv_type = bank->drv[pin_num / 8].drv_type;
 
 	ctrl->drv_calc_reg(bank, pin_num, &regmap, &reg, &bit);
 
+	switch (drv_type) {
+	case DRV_TYPE_IO_1V8_3V0_AUTO:
+	case DRV_TYPE_IO_3V3_ONLY:
+		if (RK3399_DRV_3BITS_PER_PIN + bit >= 16) {
+			/* need to read regs twice */
+			ret = regmap_read(regmap, reg, &temp);
+			if (ret)
+				return ret;
+
+			temp >>= bit;
+			rmask_bits = 16 - bit;
+			temp &= (1 << rmask_bits) - 1;
+
+			reg = reg + 0x4;
+			ret = regmap_read(regmap, reg, &data);
+			if (ret)
+				return ret;
+
+			rmask_bits = RK3399_DRV_3BITS_PER_PIN
+				+ bit - 16;
+			data &= (1 << rmask_bits) - 1;
+			data <<= 16 - bit;
+			data |= temp;
+
+			return rockchip_perpin_drv_list[drv_type][data];
+		}
+
+		rmask_bits = RK3399_DRV_3BITS_PER_PIN;
+		break;
+	case DRV_TYPE_IO_DEFAULT:
+	case DRV_TYPE_IO_1V8_OR_3V0:
+	case DRV_TYPE_IO_1V8_ONLY:
+		rmask_bits = RK3288_DRV_BITS_PER_PIN;
+		break;
+	default:
+		dev_err(info->dev, "unsupported pinctrl drive type: %d\n",
+			drv_type);
+		return -EINVAL;
+	}
+
 	ret = regmap_read(regmap, reg, &data);
 	if (ret)
 		return ret;
 
 	data >>= bit;
-	data &= (1 << RK3288_DRV_BITS_PER_PIN) - 1;
+	data &= (1 << rmask_bits) - 1;
 
-	return rockchip_perpin_drv_list[data];
+	return rockchip_perpin_drv_list[drv_type][data];
 }
 
 static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank,
@@ -708,16 +885,23 @@ static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank,
 	struct regmap *regmap;
 	unsigned long flags;
 	int reg, ret, i;
-	u32 data, rmask;
+	u32 data, rmask, rmask_bits;
 	u8 bit;
+	int drv_type = bank->drv[pin_num / 8].drv_type;
+
+	dev_dbg(info->dev, "setting drive of GPIO%d-%d to %d\n",
+		bank->bank_num, pin_num, strength);
 
 	ctrl->drv_calc_reg(bank, pin_num, &regmap, &reg, &bit);
 
 	ret = -EINVAL;
-	for (i = 0; i < ARRAY_SIZE(rockchip_perpin_drv_list); i++) {
-		if (rockchip_perpin_drv_list[i] == strength) {
+	for (i = 0; i < ARRAY_SIZE(rockchip_perpin_drv_list[drv_type]); i++) {
+		if (rockchip_perpin_drv_list[drv_type][i] == strength) {
 			ret = i;
 			break;
+		} else if (rockchip_perpin_drv_list[drv_type][i] < 0) {
+			ret = rockchip_perpin_drv_list[drv_type][i];
+			break;
 		}
 	}
 
@@ -729,8 +913,45 @@ static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank,
 
 	spin_lock_irqsave(&bank->slock, flags);
 
+	switch (drv_type) {
+	case DRV_TYPE_IO_1V8_3V0_AUTO:
+	case DRV_TYPE_IO_3V3_ONLY:
+		if (RK3399_DRV_3BITS_PER_PIN + bit >= 16) {
+			/* need to write regs twice */
+			rmask_bits = 16 - bit;
+			/* enable the write to the equivalent lower bits */
+			data = ((1 << rmask_bits) - 1) << (bit + 16);
+			rmask = data | (data >> 16);
+			ret &= (1 << rmask_bits) - 1;
+			data |= (ret << bit);
+
+			ret = regmap_update_bits(regmap, reg, rmask, data);
+			if (ret)
+				return ret;
+
+			ret = i >> rmask_bits;
+			rmask_bits = RK3399_DRV_3BITS_PER_PIN + bit - 16;
+			reg = reg + 0x4;
+			bit = 0;
+		} else {
+			rmask_bits = RK3399_DRV_3BITS_PER_PIN;
+		}
+		break;
+	case DRV_TYPE_IO_DEFAULT:
+	case DRV_TYPE_IO_1V8_OR_3V0:
+	case DRV_TYPE_IO_1V8_ONLY:
+		rmask_bits = RK3288_DRV_BITS_PER_PIN;
+		break;
+	default:
+		spin_unlock_irqrestore(&bank->slock, flags);
+		dev_err(info->dev, "unsupported pinctrl drive type: %d\n",
+			drv_type);
+		return -EINVAL;
+
+	}
+
 	/* enable the write to the equivalent lower bits */
-	data = ((1 << RK3288_DRV_BITS_PER_PIN) - 1) << (bit + 16);
+	data = ((1 << rmask_bits) - 1) << (bit + 16);
 	rmask = data | (data >> 16);
 	data |= (ret << bit);
 
@@ -767,6 +988,7 @@ static int rockchip_get_pull(struct rockchip_pin_bank *bank, int pin_num)
 	case RK3188:
 	case RK3288:
 	case RK3368:
+	case RK3399:
 		data >>= bit;
 		data &= (1 << RK3188_PULL_BITS_PER_PIN) - 1;
 
@@ -823,6 +1045,7 @@ static int rockchip_set_pull(struct rockchip_pin_bank *bank,
 	case RK3188:
 	case RK3288:
 	case RK3368:
+	case RK3399:
 		spin_lock_irqsave(&bank->slock, flags);
 
 		/* enable the write to the equivalent lower bits */
@@ -1003,6 +1226,7 @@ static bool rockchip_pinconf_pull_valid(struct rockchip_pin_ctrl *ctrl,
 	case RK3188:
 	case RK3288:
 	case RK3368:
+	case RK3399:
 		return (pull != PIN_CONFIG_BIAS_PULL_PIN_DEFAULT);
 	}
 
@@ -1860,7 +2084,7 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
 	struct device_node *np;
 	struct rockchip_pin_ctrl *ctrl;
 	struct rockchip_pin_bank *bank;
-	int grf_offs, pmu_offs, i, j;
+	int grf_offs, pmu_offs, drv_grf_offs, drv_pmu_offs, i, j;
 
 	match = of_match_node(rockchip_pinctrl_dt_match, node);
 	ctrl = (struct rockchip_pin_ctrl *)match->data;
@@ -1884,6 +2108,8 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
 
 	grf_offs = ctrl->grf_mux_offset;
 	pmu_offs = ctrl->pmu_mux_offset;
+	drv_pmu_offs = ctrl->pmu_drv_offset;
+	drv_grf_offs = ctrl->grf_drv_offset;
 	bank = ctrl->pin_banks;
 	for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
 		int bank_pins = 0;
@@ -1893,27 +2119,39 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
 		bank->pin_base = ctrl->nr_pins;
 		ctrl->nr_pins += bank->nr_pins;
 
-		/* calculate iomux offsets */
+		/* calculate iomux and drv offsets */
 		for (j = 0; j < 4; j++) {
 			struct rockchip_iomux *iom = &bank->iomux[j];
+			struct rockchip_drv *drv = &bank->drv[j];
 			int inc;
 
 			if (bank_pins >= bank->nr_pins)
 				break;
 
-			/* preset offset value, set new start value */
+			/* preset iomux offset value, set new start value */
 			if (iom->offset >= 0) {
 				if (iom->type & IOMUX_SOURCE_PMU)
 					pmu_offs = iom->offset;
 				else
 					grf_offs = iom->offset;
-			} else { /* set current offset */
+			} else { /* set current iomux offset */
 				iom->offset = (iom->type & IOMUX_SOURCE_PMU) ?
 							pmu_offs : grf_offs;
 			}
 
-			dev_dbg(d->dev, "bank %d, iomux %d has offset 0x%x\n",
-				 i, j, iom->offset);
+			/* preset drv offset value, set new start value */
+			if (drv->offset >= 0) {
+				if (iom->type & IOMUX_SOURCE_PMU)
+					drv_pmu_offs = drv->offset;
+				else
+					drv_grf_offs = drv->offset;
+			} else { /* set current drv offset */
+				drv->offset = (iom->type & IOMUX_SOURCE_PMU) ?
+						drv_pmu_offs : drv_grf_offs;
+			}
+
+			dev_dbg(d->dev, "bank %d, iomux %d has iom_offset 0x%x drv_offset 0x%x\n",
+				i, j, iom->offset, drv->offset);
 
 			/*
 			 * Increase offset according to iomux width.
@@ -1925,6 +2163,21 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
 			else
 				grf_offs += inc;
 
+			/*
+			 * Increase offset according to drv width.
+			 * 3bit drive-strenth'es are spread over two registers.
+			 */
+			if ((drv->drv_type == DRV_TYPE_IO_1V8_3V0_AUTO) ||
+			    (drv->drv_type == DRV_TYPE_IO_3V3_ONLY))
+				inc = 8;
+			else
+				inc = 4;
+
+			if (iom->type & IOMUX_SOURCE_PMU)
+				drv_pmu_offs += inc;
+			else
+				drv_grf_offs += inc;
+
 			bank_pins += 8;
 		}
 	}
@@ -2208,6 +2461,62 @@ static struct rockchip_pin_ctrl rk3368_pin_ctrl = {
 		.drv_calc_reg		= rk3368_calc_drv_reg_and_bit,
 };
 
+static struct rockchip_pin_bank rk3399_pin_banks[] = {
+	PIN_BANK_IOMUX_DRV_FLAGS_OFFSET(0, 32, "gpio0", IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					DRV_TYPE_IO_1V8_ONLY,
+					DRV_TYPE_IO_1V8_ONLY,
+					0,
+					0,
+					0x0,
+					0x8,
+					-1,
+					-1
+					),
+	PIN_BANK_IOMUX_DRV_FLAGS_OFFSET(1, 32, "gpio1", IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					DRV_TYPE_IO_1V8_OR_3V0,
+					DRV_TYPE_IO_1V8_OR_3V0,
+					DRV_TYPE_IO_1V8_OR_3V0,
+					DRV_TYPE_IO_1V8_OR_3V0,
+					0x20,
+					0x28,
+					0x30,
+					0x38
+					),
+	PIN_BANK_DRV_FLAGS(2, 32, "gpio2", DRV_TYPE_IO_1V8_OR_3V0,
+			   DRV_TYPE_IO_1V8_OR_3V0,
+			   DRV_TYPE_IO_1V8_ONLY,
+			   DRV_TYPE_IO_1V8_ONLY
+			   ),
+	PIN_BANK_DRV_FLAGS(3, 32, "gpio3", DRV_TYPE_IO_3V3_ONLY,
+			   DRV_TYPE_IO_3V3_ONLY,
+			   DRV_TYPE_IO_3V3_ONLY,
+			   DRV_TYPE_IO_1V8_OR_3V0
+			   ),
+	PIN_BANK_DRV_FLAGS(4, 32, "gpio4", DRV_TYPE_IO_1V8_OR_3V0,
+			   DRV_TYPE_IO_1V8_3V0_AUTO,
+			   DRV_TYPE_IO_1V8_OR_3V0,
+			   DRV_TYPE_IO_1V8_OR_3V0
+			   ),
+};
+
+static struct rockchip_pin_ctrl rk3399_pin_ctrl = {
+		.pin_banks		= rk3399_pin_banks,
+		.nr_banks		= ARRAY_SIZE(rk3399_pin_banks),
+		.label			= "RK3399-GPIO",
+		.type			= RK3399,
+		.grf_mux_offset		= 0xe000,
+		.pmu_mux_offset		= 0x0,
+		.grf_drv_offset		= 0xe100,
+		.pmu_drv_offset		= 0x80,
+		.pull_calc_reg		= rk3399_calc_pull_reg_and_bit,
+		.drv_calc_reg		= rk3399_calc_drv_reg_and_bit,
+};
 
 static const struct of_device_id rockchip_pinctrl_dt_match[] = {
 	{ .compatible = "rockchip,rk2928-pinctrl",
@@ -2224,6 +2533,8 @@ static const struct of_device_id rockchip_pinctrl_dt_match[] = {
 		.data = (void *)&rk3288_pin_ctrl },
 	{ .compatible = "rockchip,rk3368-pinctrl",
 		.data = (void *)&rk3368_pin_ctrl },
+	{ .compatible = "rockchip,rk3399-pinctrl",
+		.data = (void *)&rk3399_pin_ctrl },
 	{},
 };
 MODULE_DEVICE_TABLE(of, rockchip_pinctrl_dt_match);
-- 
1.9.1




From jean-michel.hautbois at veo-labs.com  Thu Jan  7 00:21:43 2016
From: jean-michel.hautbois at veo-labs.com (Jean-Michel Hautbois)
Date: Thu, 7 Jan 2016 09:21:43 +0100
Subject: [alsa-devel] [PATCH 0/9] dw-hdmi audio support
In-Reply-To: <20160105160421.GX19062@n2100.arm.linux.org.uk>
References: <20150808160251.GM7557@n2100.arm.linux.org.uk>
 <20150808160936.GN7557@n2100.arm.linux.org.uk>
 <1440664941.3233.38.camel@pengutronix.de>
 <CAH-u=810V863yYhMvvtaQ7jxNgrOXAL4bi1mS506a-yOTbuYhQ@mail.gmail.com>
 <20160105160421.GX19062@n2100.arm.linux.org.uk>
Message-ID: <CAH-u=80F1AUQbG9-ivcspo4vmYcqYC0PnC8uTg2RWhgP1nTazA@mail.gmail.com>

Hi Russell,

2016-01-05 17:04 GMT+01:00 Russell King - ARM Linux <linux at arm.linux.org.uk>:
> On Tue, Jan 05, 2016 at 04:40:54PM +0100, Jean-Michel Hautbois wrote:
>> Hi Russell,
>>
>> 2015-08-27 10:42 GMT+02:00 Philipp Zabel <p.zabel at pengutronix.de>:
>> >
>> > Am Samstag, den 08.08.2015, 17:09 +0100 schrieb Russell King - ARM
>> > Linux:
>> > > Following on from the previous sub-series, this sub-series adds audio
>> > > support to dw-hdmi.
>> > >
>> > > The two different variants are now in this patch: AHB audio support
>> > > found on iMX6 platforms, and I2S support found on Rockchip patches.
>> > > Thanks to Yakir Yang for contributing the I2S support.
>> > >
>> > > I suspect that there is still some discussion to be had on this
>> > > series, though I would like to see it moving forward so that we can
>> > > get something merged.
>> >
>> > Tested-by: Philipp Zabel <p.zabel at pengutronix.de>
>> > on i.MX6 GK802 via HDMI connected to a TV (stereo only).
>> >
>> > except for the i2s patch, which is broken in this series.
>> >
>> > regards
>> > Philipp
>>
>>
>> What is the status of this series ?
>> I would like to use audio output in HDMI on my i.MX6 board, but I
>> don't know if you have some pending WIP on this ?
>
> The I2S part has been dropped.  The DesignWare HDMI block is configurable
> when it is synthesized - it can contain either the AHB audio interface or
> an I2S interface.  Freescale chose to synthesize it with the AHB audio
> interface, and this is what my patches are geared up to provide.
>
> On Rockchip devices, they chose to synthesize it with the I2S audio
> block, and so they need a different driver for it.  Yakir Yang has been
> working on that, but I've not seen anything recently.  After merging
> his I2S patch, I found some problems and decided with Yakir that the
> best thing to do was to drop it.
>
> So, the result is we support HDMI audio on iMX6.
>
> The changes were merged into mainline during the 4.4 merge window, so
> Linux 4.4 will support iMX6 HDMI audio.
>

Thank you for this detailed answer. I will rebase onto 4.4 then :).

JM


From wxt at rock-chips.com  Thu Jan  7 00:25:43 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Thu,  7 Jan 2016 16:25:43 +0800
Subject: [PATCH v2 00/12] Add the family patches to support for kylin board
Message-ID: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>

Hi all,

Happy new year! :)
Let's go on working for happy.

This series patches are based on kernel version 4.4.0-rc8+
(Linux version 4.4.0-rc8+ (wxt at ubuntu)...#142 SMP Thu Jan 7 12:38:40 CST 2016)

This series pacthes are verified on the following github:
https://github.com/Caesar-github/rockchip/commits/kylin-develop4.4

This series has 12 patches: (12--->1)
bbd1a01 ARM: dts: Add pl330-broken-no-flushp quirk for rk3036 SoCs
fb8c1fd ARM: dts: rockchip: add the lcdc and hdmi node for rk3036
1aeb49d ARM: dts: rockchip: add the sdmmc for kylin board
3e19128 ARM: dts: rockchip: enable the uart0 for kylin board
e7de154 ARM: dts: rockchip: add the wifi/bt regulator for kylin board
8699698 ARM: dts: rockchip: enable the high speed on sdio for kylin board
21eded4 mmc: pwrseq: add support for power-on sequencing through DT
cdf3a7e mmc: pwrseq: Document optional exteral vcc for the simple power sequence
131c249 clk: rockchip: rk3036: enable the CLK_IGNORE_UNUSED flag for sclk_i2s_out
b7ff8e1 ARM: dts: rockchip: override the clocks in i2s for kylin board
5f56af4 ARM: dts: rockchip: add the sound codec for kylin board
914d9c0 ARM: dts: rockchip: set the pinctrl default setting for rk3036 i2s
..

This series patches have the following decriptions:

PATCH[1/12-4/12]:====> Support rt5616 codec for kylin board
914d9c0 ARM: dts: rockchip: set the pinctrl default setting for rk3036 i2s
5f56af4 ARM: dts: rockchip: add the sound codec for kylin board
b7ff8e1 ARM: dts: rockchip: override the clocks in i2s for kylin board
131c249 clk: rockchip: rk3036: enable the CLK_IGNORE_UNUSED flag for sclk_i2s_out

This patches is working for codec rt5616 on kylin board. (RK3036 SoCs)

That's needed the following rt5616 driver from Mark Brown Branch.
(cherry picked from git.kernel.org broonie/sound.git for-next)

cf94b7a UPSTREAM: ASoC: rt5616: rename some alsa control names
86f561d UPSTREAM: ASoC: rt5616: add devicetree document for rt5616
0e0f5f8 UPSTREAM: ASoC: rt5616: add an of_match table
263a330 UPSTREAM: ASoC: rt5616: Return error if device ID mismatch
0094139 UPSTREAM: ASoC: rt5616: add rt5616 codec driver

Notes:
I'm run it with the ubuntu 15.04 to set the configure.
./amixer "HP Playback Switch" 1
./amixer "HP Playback Volume" 20
./amixer "HPO MIX DAC1 Switch" 1
./amixer "OUT MIXR DAC R1 Switch" 1
./amixer "OUT MIXL DAC L1 Switch" 1
./amixer "Stereo DAC MIXR DAC R1 Switch" 1
./amixer "Stereo DAC MIXL DAC L1 Switch" 1

root at linaro-alip:/media/aplay# ./aplay pcm.wav
Playing sample: 2 ch, 48000 hz, 16 bit
..
That can be normal playing the music.
----

PATCH[5/12-9/12]: ====> Support wifi/bt module for kylin board
cdf3a7e mmc: pwrseq: Document optional exteral vcc for the simple power sequence
21eded4 mmc: pwrseq: add support for power-on sequencing through DT
8699698 ARM: dts: rockchip: enable the high speed on sdio for kylin board
e7de154 ARM: dts: rockchip: add the wifi/bt regulator for kylin board
3e19128 ARM: dts: rockchip: enable the uart0 for kylin board

The wifi/bt driver hasn't land in mainline, the driver is in here:
https://github.com/Caesar-github/rockchip/commit/1912ef4c0e358677904b0a0bbb83f9745066f0ec

wpa_supplicant -Dnl80211 -c /etc/wifi/wpa_supplicant.conf -i wlan0 -B
wpa_cli add_network
wpa_cli set_network 1 ssid \"wxt\"
wpa_cli set_network 1 psk \"123456789\"
wpa_cli select_network 1
wpa_cli save_config

ifconfig wlan0 192.168.1.105
route add default gw 192.168.1.1

root at linaro-alip:/# ping 192.168.1.1
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=6.83 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=184 ms
64 bytes from 192.168.1.1: icmp_seq=3 ttl=64 time=9.21 ms
64 bytes from 192.168.1.1: icmp_seq=4 ttl=64 time=6.84 ms
64 bytes from 192.168.1.1: icmp_seq=5 ttl=64 time=11.2 ms
64 bytes from 192.168.1.1: icmp_seq=6 ttl=64 time=9.24 ms
64 bytes from 192.168.1.1: icmp_seq=7 ttl=64 time=12.9 ms
64 bytes from 192.168.1.1: icmp_seq=8 ttl=64 time=7.02 ms
64 bytes from 192.168.1.1: icmp_seq=9 ttl=64 time=24.3 ms
64 bytes from 192.168.1.1: icmp_seq=10 ttl=64 time=13.6 ms
64 bytes from 192.168.1.1: icmp_seq=11 ttl=64 time=3.87 m
...

The wifi throughput:

iperf -s -i 1
...
[  4] 116.0-117.0 sec  2.88 MBytes  24.1 Mbits/sec
[  4] 117.0-118.0 sec  2.99 MBytes  25.0 Mbits/sec
[  4] 118.0-119.0 sec  3.01 MBytes  25.2 Mbits/sec
[  4] 119.0-120.0 sec  2.89 MBytes  24.2 Mbits/sec
[  4]  0.0-120.4 sec   332 MBytes  23.1 Mbits/sec
----

PATCH[10/12]: ====> sdmmc patch for kylin board
ARM: dts: rockchip: add the sdmmc for kylin board

You should mark it "oaky" if you want to use the sdmmc.
This patch default is disabing the sdmmc since the sdmmc and
uart2(debug port) is reusing pin.
----

PATCH[11/12]: ====> hdmi/lcdc diaply patch for rk3036 dts
ARM: dts: rockchip: add the lcdc and hdmi node for rk3036

We can add this patch to support it since the Mark Yao has land
your vop support.
https://github.com/markyzq/kernel-drm-rockchip/commits/drm-rockchip-next-2015-12-28
----

PATCH[12/12]:====> DMA Patchs
ARM: dts: Add pl330-broken-no-flushp quirk for rk3036 SoCs

In order to be better read/write for emmc.
This patch is based on the following:(Fix broken DMAFLUSHP on Rockchips platform)

3055695 FROMLIST: DMA: dmaengine: expose max burst capability to clientmZ
(am from https://patchwork.kernel.org/patch/7399361/)
ed56bee FROMLIST: DMA: pl330: add quirk for broken no flushp
(am from https://patchwork.kernel.org/patch/7399371/)
0c43bc3 FROMLIST: Documentation: arm-pl330: add description of arm, pl330-broken-no-flushp
(am from https://patchwork.kernel.org/patch/7399371/)
623a699 FROMLIST: DMA: pl330: support burst mode for dev-to-mem and mem-to-dev transmit
(am from https://patchwork.kernel.org/patch/7399381/)
----


Changes in v2:
- modify the commit.
- fix the mclk-fs as the 256.
- This fix inmmc-power-sequences, as Heiko comment on
  https://patchwork.kernel.org/patch/7903161/
- As Heiko comment, modify it to fit the simple-mmc-sequence.
- Add the uart0 commit content.

Caesar Wang (12):
  ARM: dts: rockchip: set the pinctrl default setting for rk3036 i2s
  ARM: dts: rockchip: add the sound codec for kylin board
  ARM: dts: rockchip: override the clocks in i2s for kylin board
  clk: rockchip: rk3036: enable the CLK_IGNORE_UNUSED flag for
    sclk_i2s_out
  mmc: pwrseq: Document optional exteral vcc for the simple power
    sequence
  mmc: pwrseq: add support for power-on sequencing through DT
  ARM: dts: rockchip: enable the high speed on sdio for kylin board
  ARM: dts: rockchip: add the wifi/bt regulator for kylin board
  ARM: dts: rockchip: enable the uart0 for kylin board
  ARM: dts: rockchip: add the sdmmc for kylin board
  ARM: dts: rockchip: add the lcdc and hdmi node for rk3036
  ARM: dts: Add pl330-broken-no-flushp quirk for rk3036 SoCs

 .../devicetree/bindings/mmc/mmc-pwrseq-simple.txt  |   9 ++
 arch/arm/boot/dts/rk3036-kylin.dts                 | 119 +++++++++++++++++++++
 arch/arm/boot/dts/rk3036.dtsi                      |  74 +++++++++++--
 drivers/clk/rockchip/clk-rk3036.c                  |   2 +-
 drivers/mmc/core/pwrseq_simple.c                   |  16 +++
 5 files changed, 213 insertions(+), 7 deletions(-)

-- 
1.9.1



From wxt at rock-chips.com  Thu Jan  7 00:25:44 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Thu,  7 Jan 2016 16:25:44 +0800
Subject: [PATCH v2 01/12] ARM: dts: rockchip: set the pinctrl default setting
 for rk3036 i2s
In-Reply-To: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
References: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1452155155-16232-2-git-send-email-wxt@rock-chips.com>

Sometime will hang if you set the i2s pinctrl as the none setting.
Let's set the pinctrl as the default setting to enable the gpio up/down.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v2:
- modify the commit.

 arch/arm/boot/dts/rk3036.dtsi | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
index b9567c1..8f1bb0f 100644
--- a/arch/arm/boot/dts/rk3036.dtsi
+++ b/arch/arm/boot/dts/rk3036.dtsi
@@ -579,12 +579,12 @@
 
 		i2s {
 			i2s_bus: i2s-bus {
-				rockchip,pins = <1 0 RK_FUNC_1 &pcfg_pull_none>,
-						<1 1 RK_FUNC_1 &pcfg_pull_none>,
-						<1 2 RK_FUNC_1 &pcfg_pull_none>,
-						<1 3 RK_FUNC_1 &pcfg_pull_none>,
-						<1 4 RK_FUNC_1 &pcfg_pull_none>,
-						<1 5 RK_FUNC_1 &pcfg_pull_none>;
+				rockchip,pins = <1 0 RK_FUNC_1 &pcfg_pull_default>,
+						<1 1 RK_FUNC_1 &pcfg_pull_default>,
+						<1 2 RK_FUNC_1 &pcfg_pull_default>,
+						<1 3 RK_FUNC_1 &pcfg_pull_default>,
+						<1 4 RK_FUNC_1 &pcfg_pull_default>,
+						<1 5 RK_FUNC_1 &pcfg_pull_default>;
 			};
 		};
 
-- 
1.9.1



From wxt at rock-chips.com  Thu Jan  7 00:25:45 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Thu,  7 Jan 2016 16:25:45 +0800
Subject: [PATCH v2 02/12] ARM: dts: rockchip: add the sound codec for kylin
 board
In-Reply-To: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
References: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1452155155-16232-3-git-send-email-wxt@rock-chips.com>

Support the rt5616 codec for kylin board, but we need
enable the i2s firstly.
Also, this patch adds the CPU/CODEC information for simple card
to make the happy work.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v2:
- fix the mclk-fs as the 256.

 arch/arm/boot/dts/rk3036-kylin.dts | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/arch/arm/boot/dts/rk3036-kylin.dts b/arch/arm/boot/dts/rk3036-kylin.dts
index 992f9ca..27b6326 100644
--- a/arch/arm/boot/dts/rk3036-kylin.dts
+++ b/arch/arm/boot/dts/rk3036-kylin.dts
@@ -46,6 +46,28 @@
 	model = "Rockchip RK3036 KylinBoard";
 	compatible = "rockchip,rk3036-kylin", "rockchip,rk3036";
 
+	sound {
+		compatible = "simple-audio-card";
+		simple-audio-card,format = "i2s";
+		simple-audio-card,name = "rockchip,rt5616-codec";
+		simple-audio-card,mclk-fs = <256>;
+		simple-audio-card,widgets =
+			"Microphone", "Microphone Jack",
+			"Headphone", "Headphone Jack";
+		simple-audio-card,routing =
+			"MIC1", "Microphone Jack",
+			"MIC2", "Microphone Jack",
+			"Microphone Jack", "micbias1",
+			"Headphone Jack", "HPOL",
+			"Headphone Jack", "HPOR";
+		simple-audio-card,cpu {
+			sound-dai = <&i2s>;
+		};
+		simple-audio-card,codec {
+			sound-dai = <&rt5616>;
+		};
+	};
+
 	vcc_sys: vsys-regulator {
 		compatible = "regulator-fixed";
 		regulator-name = "vcc_sys";
@@ -257,6 +279,17 @@
 
 &i2c2 {
 	status = "okay";
+
+	rt5616: rt5616 at 1b {
+		#sound-dai-cells = <0>;
+		compatible = "rt5616";
+		reg = <0x1b>;
+	};
+};
+
+&i2s {
+	#sound-dai-cells = <0>;
+	status = "okay";
 };
 
 &sdio {
-- 
1.9.1



From wxt at rock-chips.com  Thu Jan  7 00:25:46 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Thu,  7 Jan 2016 16:25:46 +0800
Subject: [PATCH v2 03/12] ARM: dts: rockchip: override the clocks in i2s for
 kylin board
In-Reply-To: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
References: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1452155155-16232-4-git-send-email-wxt@rock-chips.com>

The sysclk seems be incorrect since we use the simple card for kylin
board.

The simple card call:

asoc_simple_card_probe ->
 asoc_simple_card_dai_link_of ->
  asoc_simple_card_sub_parse_of ->
...
clk = of_clk_get(args.np, 0);
if (!IS_ERR(clk))
dai->sysclk = clk_get_rate(clk);

The sysclk come from the first clock, then first clock is hclk_i2s
in i2s for rk3036 dtsi.
So, we can override the clocks to fit the simple card in here.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
---

Changes in v2: None

 arch/arm/boot/dts/rk3036-kylin.dts | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/arch/arm/boot/dts/rk3036-kylin.dts b/arch/arm/boot/dts/rk3036-kylin.dts
index 27b6326..b642369 100644
--- a/arch/arm/boot/dts/rk3036-kylin.dts
+++ b/arch/arm/boot/dts/rk3036-kylin.dts
@@ -288,6 +288,13 @@
 };
 
 &i2s {
+	/**
+	 * We should override the clocks since sysclk from the first clock
+	 * in simple card.
+	 */
+	clock-names = "i2s_clk", "i2s_hclk";
+	clocks = <&cru SCLK_I2S>, <&cru HCLK_I2S>;
+
 	#sound-dai-cells = <0>;
 	status = "okay";
 };
-- 
1.9.1



From wxt at rock-chips.com  Thu Jan  7 00:25:47 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Thu,  7 Jan 2016 16:25:47 +0800
Subject: [PATCH v2 04/12] clk: rockchip: rk3036: enable the CLK_IGNORE_UNUSED
 flag for sclk_i2s_out
In-Reply-To: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
References: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1452155155-16232-5-git-send-email-wxt@rock-chips.com>

SCLk_I2S_OUT is the noc bus clock for i2s module, this clock is used by
extra codecs.

Due to it shouldn't belong to any driver, but we need it enabled,
so just mark it as the CLK_IGNORE_UNUSED flag.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
---

Changes in v2: None

 drivers/clk/rockchip/clk-rk3036.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/clk/rockchip/clk-rk3036.c b/drivers/clk/rockchip/clk-rk3036.c
index 34c78f4..c33ed3c 100644
--- a/drivers/clk/rockchip/clk-rk3036.c
+++ b/drivers/clk/rockchip/clk-rk3036.c
@@ -310,7 +310,7 @@ static struct rockchip_clk_branch rk3036_clk_branches[] __initdata = {
 			RK2928_CLKSEL_CON(7), 0,
 			RK2928_CLKGATE_CON(0), 10, GFLAGS,
 			&rk3036_i2s_fracmux),
-	COMPOSITE_NODIV(SCLK_I2S_OUT, "i2s_clkout", mux_i2s_clkout_p, 0,
+	COMPOSITE_NODIV(SCLK_I2S_OUT, "i2s_clkout", mux_i2s_clkout_p, CLK_IGNORE_UNUSED,
 			RK2928_CLKSEL_CON(3), 12, 1, MFLAGS,
 			RK2928_CLKGATE_CON(0), 13, GFLAGS),
 	GATE(SCLK_I2S, "sclk_i2s", "i2s_pre", CLK_SET_RATE_PARENT,
-- 
1.9.1



From wxt at rock-chips.com  Thu Jan  7 00:25:48 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Thu,  7 Jan 2016 16:25:48 +0800
Subject: [PATCH v2 05/12] mmc: pwrseq: Document optional exteral vcc for the
 simple power sequence
In-Reply-To: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
References: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1452155155-16232-6-git-send-email-wxt@rock-chips.com>

This patch adds the exteral vcc optional for the simple power sequence.

Some WLAN chips attached to a SDIO interface, need an external vcc
to be operational. Since this is very common, extend the simple MMC
power sequence DT binding to support an optional exteral vcc.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
---

Changes in v2: None

 Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt
index ce0e767..fdcc6a1 100644
--- a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt
+++ b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt
@@ -12,6 +12,7 @@ Optional properties:
 	at initialization and prior we start the power up procedure of the card.
 	They will be de-asserted right after the power has been provided to the
 	card.
+- ext-vcc-supply : Regulator to drive (independent) card VCC.
 - clocks : Must contain an entry for the entry in clock-names.
   See ../clocks/clock-bindings.txt for details.
 - clock-names : Must include the following entry:
@@ -19,9 +20,17 @@ Optional properties:
 
 Example:
 
+	wifi_regulator: wifi-regulator {
+		compatible = "regulator-fixed";
+		enable-active-high;
+		gpio = <&gpio0 26 GPIO_ACTIVE_HIGH>;
+		regulator-name = "wifi_regulator";
+	};
+
 	sdhci0_pwrseq {
 		compatible = "mmc-pwrseq-simple";
 		reset-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>;
+		ext-vcc-supply = <&wifi_regulator>;
 		clocks = <&clk_32768_ck>;
 		clock-names = "ext_clock";
 	}
-- 
1.9.1



From wxt at rock-chips.com  Thu Jan  7 00:25:49 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Thu,  7 Jan 2016 16:25:49 +0800
Subject: [PATCH v2 06/12] mmc: pwrseq: add support for power-on sequencing
 through DT
In-Reply-To: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
References: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1452155155-16232-7-git-send-email-wxt@rock-chips.com>

This patch enables support for power-on sequencing of SDIO
peripherals through DT.

In general, it's quite common that wifi modules and other similar
peripherals have several signals in addition to the SDIO interface that
needs wiggling before the module will power on.

For example:
we need enable wifi module power to via the WL_REG_ON
pin, we need enable it as the regulator if this pin is connected to
the gpio of cpu.

Maybe, someone will say that can pull up/down from dts.
Unfortunately some SoCs can't support pinctrl pull up/down in
internal.

Anyway, we can add this patch to supprt the power-on sequencing for
sdio.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v2:
- This fix inmmc-power-sequences, as Heiko comment on
  https://patchwork.kernel.org/patch/7903161/

 drivers/mmc/core/pwrseq_simple.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
index d10538b..455dd0c 100644
--- a/drivers/mmc/core/pwrseq_simple.c
+++ b/drivers/mmc/core/pwrseq_simple.c
@@ -14,6 +14,7 @@
 #include <linux/err.h>
 #include <linux/of_gpio.h>
 #include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
 
 #include <linux/mmc/host.h>
 
@@ -24,6 +25,7 @@ struct mmc_pwrseq_simple {
 	bool clk_enabled;
 	struct clk *ext_clk;
 	struct gpio_descs *reset_gpios;
+	struct regulator *regulator;
 };
 
 static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
@@ -45,6 +47,13 @@ static void mmc_pwrseq_simple_pre_power_on(struct mmc_host *host)
 	struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq,
 					struct mmc_pwrseq_simple, pwrseq);
 
+	if (!IS_ERR(pwrseq->regulator)) {
+		dev_dbg(host->parent, "Enabling external regulator\n");
+		if (regulator_enable(pwrseq->regulator))
+			dev_err(host->parent,
+				"Failed to enable external regulator\n");
+	}
+
 	if (!IS_ERR(pwrseq->ext_clk) && !pwrseq->clk_enabled) {
 		clk_prepare_enable(pwrseq->ext_clk);
 		pwrseq->clk_enabled = true;
@@ -117,6 +126,13 @@ struct mmc_pwrseq *mmc_pwrseq_simple_alloc(struct mmc_host *host,
 		goto clk_put;
 	}
 
+	pwrseq->regulator = devm_regulator_get(dev, "ext-vcc");
+	if (IS_ERR(pwrseq->regulator) &&
+	    PTR_ERR(pwrseq->regulator) != -EPROBE_DEFER) {
+		ret = PTR_ERR(pwrseq->regulator);
+		goto clk_put;
+	}
+
 	pwrseq->pwrseq.ops = &mmc_pwrseq_simple_ops;
 
 	return &pwrseq->pwrseq;
-- 
1.9.1



From wxt at rock-chips.com  Thu Jan  7 00:25:50 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Thu,  7 Jan 2016 16:25:50 +0800
Subject: [PATCH v2 07/12] ARM: dts: rockchip: enable the high speed on sdio
 for kylin board
In-Reply-To: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
References: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1452155155-16232-8-git-send-email-wxt@rock-chips.com>

We want to the higher speed for wifi module working.

Bootup kernel log:
...
mmc_host mmc0: Bus speed (slot 0) = 37125000Hz (slot req 37500000Hz,
actual 37125000HZ div = 0)

or run 'cat /sys/kernel/debug/clk/clk_summary |grep phase -C 1' to check
Otherwise, the mmc0 will run 400khz defalult value to work.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
---

Changes in v2: None

 arch/arm/boot/dts/rk3036-kylin.dts | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/arch/arm/boot/dts/rk3036-kylin.dts b/arch/arm/boot/dts/rk3036-kylin.dts
index b642369..b2e5f5d 100644
--- a/arch/arm/boot/dts/rk3036-kylin.dts
+++ b/arch/arm/boot/dts/rk3036-kylin.dts
@@ -304,6 +304,11 @@
 
 	broken-cd;
 	bus-width = <4>;
+	cap-sd-highspeed;
+	sd-uhs-sdr12;
+	sd-uhs-sdr25;
+	sd-uhs-sdr50;
+	sd-uhs-sdr104;
 	cap-sdio-irq;
 	default-sample-phase = <90>;
 	keep-power-in-suspend;
-- 
1.9.1



From wxt at rock-chips.com  Thu Jan  7 00:25:51 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Thu,  7 Jan 2016 16:25:51 +0800
Subject: [PATCH v2 08/12] ARM: dts: rockchip: add the wifi/bt regulator for
 kylin board
In-Reply-To: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
References: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1452155155-16232-9-git-send-email-wxt@rock-chips.com>

This patch adds the wifi/bt regulator for simple mmc sequence.
Also, add the exteral clock and reset gpios for simple mmmc sequence
on kylin board.

Without this the wifi/bluetooth module doesn't work properly at bootup.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v2:
- As Heiko comment, modify it to fit the simple-mmc-sequence.

 arch/arm/boot/dts/rk3036-kylin.dts | 53 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 53 insertions(+)

diff --git a/arch/arm/boot/dts/rk3036-kylin.dts b/arch/arm/boot/dts/rk3036-kylin.dts
index b2e5f5d..4346ce5 100644
--- a/arch/arm/boot/dts/rk3036-kylin.dts
+++ b/arch/arm/boot/dts/rk3036-kylin.dts
@@ -40,6 +40,7 @@
 
 /dts-v1/;
 
+#include <dt-bindings/clock/rockchip,rk808.h>
 #include "rk3036.dtsi"
 
 / {
@@ -76,6 +77,43 @@
 		regulator-always-on;
 		regulator-boot-on;
 	};
+
+	bt_regulator: bt-regulator {
+		compatible = "regulator-fixed";
+		enable-active-high;
+		gpio = <&gpio2 9 GPIO_ACTIVE_HIGH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&bt_enable_l>;
+		regulator-name = "bt_regulator";
+	};
+
+	wifi_regulator: wifi-regulator {
+		compatible = "regulator-fixed";
+		enable-active-high;
+		gpio = <&gpio0 26 GPIO_ACTIVE_HIGH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&wifi_enable_h>;
+		regulator-name = "wifi_regulator";
+		vin-supply = <&bt_regulator>;
+	};
+
+	sdio_pwrseq: sdio-pwrseq {
+		compatible = "mmc-pwrseq-simple";
+		clocks = <&rk808 RK808_CLKOUT1>;
+		clock-names = "ext_clock";
+		pinctrl-names = "default";
+		pinctrl-0 = <&bt_wake_h>;
+
+		/*
+		 * On the module itself this is one of these (depending
+		 * on the actual card populated):
+		 * - SDIO_RESET_L_WL_REG_ON
+		 * - PDN (power down when low)
+		 */
+		reset-gpios = <&gpio0 27 GPIO_ACTIVE_LOW>;
+		ext-vcc-supply = <&wifi_regulator>;
+	};
+
 };
 
 &acodec {
@@ -312,6 +350,7 @@
 	cap-sdio-irq;
 	default-sample-phase = <90>;
 	keep-power-in-suspend;
+	mmc-pwrseq = <&sdio_pwrseq>;
 	non-removable;
 	num-slots = <1>;
 	pinctrl-names = "default";
@@ -337,6 +376,20 @@
 		};
 	};
 
+	sdio {
+		wifi_enable_h: wifienable-h {
+			rockchip,pins = <0 26 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+
+		bt_enable_l: bt-enable-l {
+			rockchip,pins = <2 9 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+
+		bt_wake_h: bt-wake-h {
+			rockchip,pins = <2 8 RK_FUNC_GPIO &pcfg_pull_default>;
+		};
+	};
+
 	sleep {
 		global_pwroff: global-pwroff {
 			rockchip,pins = <2 7 RK_FUNC_1 &pcfg_pull_none>;
-- 
1.9.1



From wxt at rock-chips.com  Thu Jan  7 00:25:52 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Thu,  7 Jan 2016 16:25:52 +0800
Subject: [PATCH v2 09/12] ARM: dts: rockchip: enable the uart0 for kylin board
In-Reply-To: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
References: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1452155155-16232-10-git-send-email-wxt@rock-chips.com>

This patch is enabling the uart0 for bluetooth module.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v2:
- Add the uart0 commit content.

 arch/arm/boot/dts/rk3036-kylin.dts | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/arm/boot/dts/rk3036-kylin.dts b/arch/arm/boot/dts/rk3036-kylin.dts
index 4346ce5..c5e46f9 100644
--- a/arch/arm/boot/dts/rk3036-kylin.dts
+++ b/arch/arm/boot/dts/rk3036-kylin.dts
@@ -361,6 +361,10 @@
 	status = "okay";
 };
 
+&uart0 {
+	status = "okay";
+};
+
 &usb_host {
 	status = "okay";
 };
-- 
1.9.1



From wxt at rock-chips.com  Thu Jan  7 00:25:53 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Thu,  7 Jan 2016 16:25:53 +0800
Subject: [PATCH v2 10/12] ARM: dts: rockchip: add the sdmmc for kylin board
In-Reply-To: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
References: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1452155155-16232-11-git-send-email-wxt@rock-chips.com>

We should add the sdmmc but enabling it, since the sdmmc and
uart2 ie reusing the same pin.
That will cause the debug information can't display on console.

Anyway, you only need add the okay status if someone wants to enable
the sdmmc.

e.g.
if you use the adb to debug and run with android os.
you can add the
status = "okay" to enable the sdmmc for sd card working.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
---

Changes in v2: None

 arch/arm/boot/dts/rk3036-kylin.dts | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/arch/arm/boot/dts/rk3036-kylin.dts b/arch/arm/boot/dts/rk3036-kylin.dts
index c5e46f9..2dbc7bb 100644
--- a/arch/arm/boot/dts/rk3036-kylin.dts
+++ b/arch/arm/boot/dts/rk3036-kylin.dts
@@ -357,6 +357,17 @@
 	pinctrl-0 = <&sdio_clk &sdio_cmd &sdio_bus4>;
 };
 
+&sdmmc {
+	bus-width = <4>;
+	cap-mmc-highspeed;
+	cap-sd-highspeed;
+	card-detect-delay = <200>;
+	disable-wp;
+	num-slots = <1>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&sdmmc_clk>, <&sdmmc_cmd>, <&sdmmc_cd>, <&sdmmc_bus4>;
+};
+
 &uart2 {
 	status = "okay";
 };
@@ -394,6 +405,12 @@
 		};
 	};
 
+	sdmmc {
+		sdmmc_pwr: sdmmc-pwr {
+			rockchip,pins = <2 28 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+
 	sleep {
 		global_pwroff: global-pwroff {
 			rockchip,pins = <2 7 RK_FUNC_1 &pcfg_pull_none>;
-- 
1.9.1



From wxt at rock-chips.com  Thu Jan  7 00:25:54 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Thu,  7 Jan 2016 16:25:54 +0800
Subject: [PATCH v2 11/12] ARM: dts: rockchip: add the lcdc and hdmi node for
 rk3036
In-Reply-To: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
References: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1452155155-16232-12-git-send-email-wxt@rock-chips.com>

The rk3036 support two overlay plane and one hwc plane,
it support IOMMU, and its IOMMU same as rk3288's.

Meanwhile, add the inno hdmi for HDMI display.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v2: None

 arch/arm/boot/dts/rk3036.dtsi | 61 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 61 insertions(+)

diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
index 8f1bb0f..6ff03e3 100644
--- a/arch/arm/boot/dts/rk3036.dtsi
+++ b/arch/arm/boot/dts/rk3036.dtsi
@@ -147,6 +147,37 @@
 		};
 	};
 
+	lcdc_mmu: iommu at 10118300 {
+		compatible = "rockchip,iommu";
+		reg = <0x10118300 0x100>;
+		interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-names = "lcdc_mmu";
+		#iommu-cells = <0>;
+		status = "disabled";
+	};
+
+	lcdc: lcdc at 10118000 {
+		compatible = "rockchip,rk3036-lcdc";
+		reg = <0x10118000 0x19c>;
+		interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru ACLK_LCDC>, <&cru SCLK_LCDC>, <&cru HCLK_LCDC>;
+		clock-names = "aclk_vop", "dclk_vop", "hclk_vop";
+		resets = <&cru SRST_LCDC1_A>, <&cru SRST_LCDC1_H>, <&cru SRST_LCDC1_D>;
+		reset-names = "axi", "ahb", "dclk";
+		iommus = <&lcdc_mmu>;
+
+		status = "disabled";
+
+		lcdc_out: port {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			lcdc_out_hdmi: endpoint at 0 {
+				reg = <1>;
+				remote-endpoint = <&hdmi_in_lcdc>;
+			};
+		};
+	};
+
 	gic: interrupt-controller at 10139000 {
 		compatible = "arm,gic-400";
 		interrupt-controller;
@@ -274,6 +305,27 @@
 		status = "disabled";
 	};
 
+	hdmi: hdmi at 20034000 {
+		compatible = "rockchip,rk3036-inno-hdmi";
+		reg = <0x20034000 0x4000>;
+		interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru  PCLK_HDMI>;
+		clock-names = "pclk";
+		rockchip,grf = <&grf>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&hdmi_ctl>;
+		status = "disabled";
+
+		hdmi_in: port {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			hdmi_in_lcdc: endpoint at 0 {
+				reg = <0>;
+				remote-endpoint = <&lcdc_out_hdmi>;
+			};
+		};
+	};
+
 	timer: timer at 20044000 {
 		compatible = "rockchip,rk3036-timer", "rockchip,rk3288-timer";
 		reg = <0x20044000 0x20>;
@@ -556,6 +608,15 @@
 			};
 		};
 
+		hdmi {
+			hdmi_ctl: hdmi-ctl {
+				rockchip,pins = <1 8  RK_FUNC_1 &pcfg_pull_none>,
+						<1 9  RK_FUNC_1 &pcfg_pull_none>,
+						<1 10 RK_FUNC_1 &pcfg_pull_none>,
+						<1 11 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
 		i2c0 {
 			i2c0_xfer: i2c0-xfer {
 				rockchip,pins = <0 0 RK_FUNC_1 &pcfg_pull_none>,
-- 
1.9.1



From wxt at rock-chips.com  Thu Jan  7 00:25:55 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Thu,  7 Jan 2016 16:25:55 +0800
Subject: [PATCH v2 12/12] ARM: dts: Add pl330-broken-no-flushp quirk for
 rk3036 SoCs
In-Reply-To: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
References: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1452155155-16232-13-git-send-email-wxt@rock-chips.com>

Pl330 integrated in rk3036platform doesn't support
DMAFLUSHP function. So we add arm,pl330-broken-no-flushp quirk
for it.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
---

Changes in v2: None

 arch/arm/boot/dts/rk3036.dtsi | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
index 6ff03e3..0168817 100644
--- a/arch/arm/boot/dts/rk3036.dtsi
+++ b/arch/arm/boot/dts/rk3036.dtsi
@@ -102,6 +102,7 @@
 		pdma: pdma at 20078000 {
 			compatible = "arm,pl330", "arm,primecell";
 			reg = <0x20078000 0x4000>;
+			arm,pl330-broken-no-flushp;
 			interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>,
 				     <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;
 			#dma-cells = <1>;
-- 
1.9.1



From ykk at rock-chips.com  Thu Jan  7 00:53:31 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Thu,  7 Jan 2016 16:53:31 +0800
Subject: [RFC PATCH v2 0/4] Add RK3229 HDMI support
Message-ID: <1452156811-18150-1-git-send-email-ykk@rock-chips.com>


RK3229 have integrated an DesignedWare HDMI controller and an INNO HDMI phy,
so we can still reuse the dw-hdmi driver for RK3229 HDMI controller, but
we need to create an separate driver for RK3229 HDMI PHY.

This series is based on Mark Yao's drm-next branch
[https://github.com/markyzq/kernel-drm-rockchip/tree/drm-rockchip-next-2015-12-28]

After picking my "Add RK3229 vop support" series, HDMI monitor could light up
normally on RK3229 SDK board.


Changes in v2:
- Split some dw-hdmi driver changes into separate patches [01/04] & [02/04]

Yakir Yang (4):
  drm: dw-hdmi: make it easy to recovery the platform data for platform
    driver
  drm: dw-hdmi: passing the "plat_data" when calling platform mode_valid
  drm: rockchip: hdmi: add RK3229 HDMI support
  dt-bindings: add document for rk3229-hdmi

 .../bindings/display/rockchip/dw_hdmi-rockchip.txt |   4 +-
 drivers/gpu/drm/bridge/dw-hdmi.c                   |  32 +-
 drivers/gpu/drm/imx/dw_hdmi-imx.c                  |  17 +-
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c        | 376 +++++++++++++++++++--
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h        | 137 ++++++++
 include/drm/bridge/dw_hdmi.h                       |   5 +-
 6 files changed, 529 insertions(+), 42 deletions(-)
 create mode 100644 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h

-- 
2.1.2




From ykk at rock-chips.com  Thu Jan  7 00:57:53 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Thu,  7 Jan 2016 16:57:53 +0800
Subject: [RFC PATCH v2 1/4] drm: dw-hdmi: make it easy to recovery the
 platform data for platform driver
In-Reply-To: <1452156811-18150-1-git-send-email-ykk@rock-chips.com>
References: <1452156811-18150-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1452157073-18576-1-git-send-email-ykk@rock-chips.com>

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v2: None

 drivers/gpu/drm/imx/dw_hdmi-imx.c           | 7 ++++---
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 7 ++++---
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/imx/dw_hdmi-imx.c b/drivers/gpu/drm/imx/dw_hdmi-imx.c
index 063825f..0968610 100644
--- a/drivers/gpu/drm/imx/dw_hdmi-imx.c
+++ b/drivers/gpu/drm/imx/dw_hdmi-imx.c
@@ -26,6 +26,7 @@ struct imx_hdmi {
 	struct device *dev;
 	struct drm_encoder encoder;
 	struct regmap *regmap;
+	struct dw_hdmi_plat_data plat_data;
 };
 
 static const struct dw_hdmi_mpll_config imx_mpll_cfg[] = {
@@ -204,7 +205,6 @@ static int dw_hdmi_imx_bind(struct device *dev, struct device *master,
 			    void *data)
 {
 	struct platform_device *pdev = to_platform_device(dev);
-	const struct dw_hdmi_plat_data *plat_data;
 	const struct of_device_id *match;
 	struct drm_device *drm = data;
 	struct drm_encoder *encoder;
@@ -221,7 +221,7 @@ static int dw_hdmi_imx_bind(struct device *dev, struct device *master,
 		return -ENOMEM;
 
 	match = of_match_node(dw_hdmi_imx_dt_ids, pdev->dev.of_node);
-	plat_data = match->data;
+	hdmi->plat_data = *(const struct dw_hdmi_plat_data *)match->data;
 	hdmi->dev = &pdev->dev;
 	encoder = &hdmi->encoder;
 
@@ -253,7 +253,8 @@ static int dw_hdmi_imx_bind(struct device *dev, struct device *master,
 	drm_encoder_init(drm, encoder, &dw_hdmi_imx_encoder_funcs,
 			 DRM_MODE_ENCODER_TMDS, NULL);
 
-	return dw_hdmi_bind(dev, master, data, encoder, iores, irq, plat_data);
+	return dw_hdmi_bind(dev, master, data, encoder, iores, irq,
+			    &hdmi->plat_data);
 }
 
 static void dw_hdmi_imx_unbind(struct device *dev, struct device *master,
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index c65ce8c..d5816b3 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -28,6 +28,7 @@ struct rockchip_hdmi {
 	struct device *dev;
 	struct regmap *regmap;
 	struct drm_encoder encoder;
+	struct dw_hdmi_plat_data plat_data;
 };
 
 #define to_rockchip_hdmi(x)	container_of(x, struct rockchip_hdmi, x)
@@ -242,7 +243,6 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 				 void *data)
 {
 	struct platform_device *pdev = to_platform_device(dev);
-	const struct dw_hdmi_plat_data *plat_data;
 	const struct of_device_id *match;
 	struct drm_device *drm = data;
 	struct drm_encoder *encoder;
@@ -259,7 +259,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 		return -ENOMEM;
 
 	match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node);
-	plat_data = match->data;
+	hdmi->plat_data = *(const struct dw_hdmi_plat_data *)match->data;
 	hdmi->dev = &pdev->dev;
 	encoder = &hdmi->encoder;
 
@@ -293,7 +293,8 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 	drm_encoder_init(drm, encoder, &dw_hdmi_rockchip_encoder_funcs,
 			 DRM_MODE_ENCODER_TMDS, NULL);
 
-	return dw_hdmi_bind(dev, master, data, encoder, iores, irq, plat_data);
+	return dw_hdmi_bind(dev, master, data, encoder, iores, irq,
+			    &hdmi->plat_data);
 }
 
 static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master,
-- 
2.1.2




From ykk at rock-chips.com  Thu Jan  7 01:00:19 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Thu,  7 Jan 2016 17:00:19 +0800
Subject: [RFC PATCH v2 2/4] drm: dw-hdmi: passing the "plat_data" when calling
 platform mode_valid
In-Reply-To: <1452156811-18150-1-git-send-email-ykk@rock-chips.com>
References: <1452156811-18150-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1452157219-18635-1-git-send-email-ykk@rock-chips.com>

Make it easy for platform driver could recovery the private data that
would help to valid the display mode.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v2: None

 drivers/gpu/drm/bridge/dw-hdmi.c            |  5 +++--
 drivers/gpu/drm/imx/dw_hdmi-imx.c           | 10 ++++++----
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c |  2 +-
 include/drm/bridge/dw_hdmi.h                |  2 +-
 4 files changed, 11 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
index 6fbec99..5ad72ec 100644
--- a/drivers/gpu/drm/bridge/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/dw-hdmi.c
@@ -1476,14 +1476,15 @@ dw_hdmi_connector_mode_valid(struct drm_connector *connector,
 {
 	struct dw_hdmi *hdmi = container_of(connector,
 					   struct dw_hdmi, connector);
+	const struct dw_hdmi_plat_data *plat_data = hdmi->plat_data;
 	enum drm_mode_status mode_status = MODE_OK;
 
 	/* We don't support double-clocked modes */
 	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
 		return MODE_BAD;
 
-	if (hdmi->plat_data->mode_valid)
-		mode_status = hdmi->plat_data->mode_valid(connector, mode);
+	if (plat_data->mode_valid)
+		mode_status = plat_data->mode_valid(plat_data, mode);
 
 	return mode_status;
 }
diff --git a/drivers/gpu/drm/imx/dw_hdmi-imx.c b/drivers/gpu/drm/imx/dw_hdmi-imx.c
index 0968610..8b0a7da 100644
--- a/drivers/gpu/drm/imx/dw_hdmi-imx.c
+++ b/drivers/gpu/drm/imx/dw_hdmi-imx.c
@@ -150,8 +150,9 @@ static const struct drm_encoder_funcs dw_hdmi_imx_encoder_funcs = {
 	.destroy = drm_encoder_cleanup,
 };
 
-static enum drm_mode_status imx6q_hdmi_mode_valid(struct drm_connector *con,
-						  struct drm_display_mode *mode)
+static enum drm_mode_status
+imx6q_hdmi_mode_valid(const struct dw_hdmi_plat_data *plat_data,
+		      struct drm_display_mode *mode)
 {
 	if (mode->clock < 13500)
 		return MODE_CLOCK_LOW;
@@ -162,8 +163,9 @@ static enum drm_mode_status imx6q_hdmi_mode_valid(struct drm_connector *con,
 	return MODE_OK;
 }
 
-static enum drm_mode_status imx6dl_hdmi_mode_valid(struct drm_connector *con,
-						   struct drm_display_mode *mode)
+static enum drm_mode_status
+imx6dl_hdmi_mode_valid(const struct dw_hdmi_plat_data *plat_data,
+		       struct drm_display_mode *mode)
 {
 	if (mode->clock < 13500)
 		return MODE_CLOCK_LOW;
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index d5816b3..8164823 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -156,7 +156,7 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 }
 
 static enum drm_mode_status
-dw_hdmi_rockchip_mode_valid(struct drm_connector *connector,
+dw_hdmi_rockchip_mode_valid(const struct dw_hdmi_plat_data *plat_data,
 			    struct drm_display_mode *mode)
 {
 	const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg;
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
index bae79f3..f8dec64 100644
--- a/include/drm/bridge/dw_hdmi.h
+++ b/include/drm/bridge/dw_hdmi.h
@@ -52,7 +52,7 @@ struct dw_hdmi_plat_data {
 	const struct dw_hdmi_mpll_config *mpll_cfg;
 	const struct dw_hdmi_curr_ctrl *cur_ctr;
 	const struct dw_hdmi_phy_config *phy_config;
-	enum drm_mode_status (*mode_valid)(struct drm_connector *connector,
+	enum drm_mode_status (*mode_valid)(const struct dw_hdmi_plat_data *pd,
 					   struct drm_display_mode *mode);
 };
 
-- 
2.1.2




From ykk at rock-chips.com  Thu Jan  7 01:02:53 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Thu,  7 Jan 2016 17:02:53 +0800
Subject: [RFC PATCH v2 3/4] drm: rockchip: hdmi: add RK3229 HDMI support
In-Reply-To: <1452156811-18150-1-git-send-email-ykk@rock-chips.com>
References: <1452156811-18150-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1452157373-18783-1-git-send-email-ykk@rock-chips.com>

RK3229 integrate an DesignedWare HDMI2.0 controller and an INNO HDMI2.0 phy,
the max output resolution is 4K.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v2:
- Split some dw-hdmi driver changes into separate patches [01/04] & [02/04]

 drivers/gpu/drm/bridge/dw-hdmi.c            |  27 +-
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 367 ++++++++++++++++++++++++++--
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h | 137 +++++++++++
 include/drm/bridge/dw_hdmi.h                |   3 +
 4 files changed, 507 insertions(+), 27 deletions(-)
 create mode 100644 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h

diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
index 5ad72ec..5e03d83 100644
--- a/drivers/gpu/drm/bridge/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/dw-hdmi.c
@@ -735,10 +735,12 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
 {
 	unsigned res_idx;
 	u8 val, msec;
+	int ret;
 	const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
 	const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg;
 	const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr;
 	const struct dw_hdmi_phy_config *phy_config = pdata->phy_config;
+	int mpixelclock = hdmi->hdmi_data.video_mode.mpixelclock;
 
 	if (prep)
 		return -EINVAL;
@@ -758,27 +760,38 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
 		return -EINVAL;
 	}
 
+	if (hdmi->plat_data->extphy_config) {
+		/* gen2 tx power off */
+		dw_hdmi_phy_gen2_txpwron(hdmi, 0);
+		dw_hdmi_phy_gen2_pddq(hdmi, 1);
+
+		ret = hdmi->plat_data->extphy_config(hdmi->plat_data, res_idx,
+						     mpixelclock);
+		/* gen2 tx power on */
+		dw_hdmi_phy_gen2_txpwron(hdmi, 1);
+		dw_hdmi_phy_gen2_pddq(hdmi, 0);
+
+		return ret;
+	}
+
 	/* PLL/MPLL Cfg - always match on final entry */
 	for (; mpll_config->mpixelclock != ~0UL; mpll_config++)
-		if (hdmi->hdmi_data.video_mode.mpixelclock <=
-		    mpll_config->mpixelclock)
+		if (mpixelclock <= mpll_config->mpixelclock)
 			break;
 
 	for (; curr_ctrl->mpixelclock != ~0UL; curr_ctrl++)
-		if (hdmi->hdmi_data.video_mode.mpixelclock <=
-		    curr_ctrl->mpixelclock)
+		if (mpixelclock <= curr_ctrl->mpixelclock)
 			break;
 
 	for (; phy_config->mpixelclock != ~0UL; phy_config++)
-		if (hdmi->hdmi_data.video_mode.mpixelclock <=
-		    phy_config->mpixelclock)
+		if (mpixelclock <= phy_config->mpixelclock)
 			break;
 
 	if (mpll_config->mpixelclock == ~0UL ||
 	    curr_ctrl->mpixelclock == ~0UL ||
 	    phy_config->mpixelclock == ~0UL) {
 		dev_err(hdmi->dev, "Pixel clock %d - unsupported by HDMI\n",
-			hdmi->hdmi_data.video_mode.mpixelclock);
+			mpixelclock);
 		return -EINVAL;
 	}
 
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 8164823..24fffaa 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -7,6 +7,7 @@
  * (at your option) any later version.
  */
 
+#include <linux/clk.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/mfd/syscon.h>
@@ -21,18 +22,134 @@
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_vop.h"
 
-#define GRF_SOC_CON6                    0x025c
-#define HDMI_SEL_VOP_LIT                (1 << 4)
+#include "dw_hdmi-rockchip.h"
 
 struct rockchip_hdmi {
 	struct device *dev;
 	struct regmap *regmap;
 	struct drm_encoder encoder;
 	struct dw_hdmi_plat_data plat_data;
+
+	void __iomem *extphy_regbase;
+	struct clk *extphy_pclk;
 };
 
 #define to_rockchip_hdmi(x)	container_of(x, struct rockchip_hdmi, x)
 
+static const struct extphy_config_tab rockchip_extphy_cfg[] = {
+	{ .mpixelclock = 165000000,
+	  .pre_emphasis = 0, .slopeboost = 0, .clk_level = 4,
+	  .data0_level = 4, 4, 4,
+	},
+
+	{ .mpixelclock = 225000000,
+	  .pre_emphasis = 0, .slopeboost = 0, .clk_level = 6,
+	  .data0_level = 6, 6, 6,
+	},
+
+	{ .mpixelclock = 340000000,
+	  .pre_emphasis = 1, .slopeboost = 0, .clk_level = 6,
+	  .data0_level = 10, 10, 10,
+	},
+
+	{ .mpixelclock = 594000000,
+	  .pre_emphasis = 1, .slopeboost = 0, .clk_level = 7,
+	  .data0_level = 10, 10, 10,
+	},
+
+	{ .mpixelclock = ~0UL},
+};
+
+static const struct extphy_pll_config_tab rockchip_extphy_pll_cfg[] = {
+	{
+		.mpixelclock = 27000000, .param = {
+			{ .pll_nd = 1, .pll_nf = 45,
+			  .tmsd_divider_a = 3, 1, 1,
+			  .pclk_divider_a = 1, 3, 3, 4,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
+			},
+			{ .pll_nd = 1, .pll_nf = 45,
+			  .tmsd_divider_a = 0, 3, 3,
+			  .pclk_divider_a = 1, 3, 3, 4,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
+			},
+		},
+	}, {
+		.mpixelclock = 59400000, .param = {
+			{ .pll_nd = 2, .pll_nf = 99,
+			  .tmsd_divider_a = 3, 1, 1,
+			  .pclk_divider_a = 1, 3, 2, 2,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
+			},
+			{ .pll_nd = 2, .pll_nf = 99,
+			  .tmsd_divider_a = 1, 1, 1,
+			  .pclk_divider_a = 1, 3, 2, 2,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
+			},
+		},
+	}, {
+		.mpixelclock = 74250000, .param = {
+			{ .pll_nd = 2, .pll_nf = 99,
+			  .tmsd_divider_a = 1, 1, 1,
+			  .pclk_divider_a = 1, 2, 2, 2,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
+			},
+			{ .pll_nd = 4, .pll_nf = 495,
+			  .tmsd_divider_a = 1, 2, 2,
+			  .pclk_divider_a = 1, 3, 3, 4,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 2, .ppll_nf = 40, .ppll_no = 4,
+			},
+		},
+	}, {
+		.mpixelclock = 148500000, .param = {
+			{ .pll_nd = 2, .pll_nf = 99,
+			  .tmsd_divider_a = 1, 0, 0,
+			  .pclk_divider_a = 1, 2, 1, 1,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 2, .ppll_nf = 40, .ppll_no = 4,
+			},
+			{ .pll_nd = 4, .pll_nf = 495,
+			  .tmsd_divider_a = 0, 2, 2,
+			  .pclk_divider_a = 1, 3, 2, 2,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 4, .ppll_nf = 40, .ppll_no = 2,
+			},
+		},
+	}, {
+		.mpixelclock = 297000000, .param = {
+			{ .pll_nd = 2, .pll_nf = 99,
+			  .tmsd_divider_a = 0, 0, 0,
+			  .pclk_divider_a = 1, 0, 1, 1,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 4, .ppll_nf = 40, .ppll_no = 2,
+			},
+			{ .pll_nd = 4, .pll_nf = 495,
+			  .tmsd_divider_a = 1, 2, 0,
+			  .pclk_divider_a = 1, 3, 1, 1,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 8, .ppll_nf = 40, .ppll_no = 1,
+			},
+		},
+	}, {
+		.mpixelclock = 594000000, .param = {
+			{ .pll_nd = 1, .pll_nf = 99,
+			  .tmsd_divider_a = 0, 2, 0,
+			  .pclk_divider_a = 1, 0, 1, 1,
+			  .vco_div_5 = 0,
+			  .ppll_nd = 8, .ppll_nf = 40, .ppll_no = 1,
+			},
+		}
+	}, {
+		.mpixelclock = ~0UL,
+	}
+};
+
 static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
 	{
 		27000000, {
@@ -142,9 +259,164 @@ static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
 	{ ~0UL,	     0x0000, 0x0000, 0x0000}
 };
 
+static inline void hdmi_extphy_write(struct rockchip_hdmi *hdmi,
+				     unsigned short data, unsigned char addr)
+{
+	writel_relaxed(data, hdmi->extphy_regbase + (addr) * 0x04);
+}
+
+static inline unsigned int hdmi_extphy_read(struct rockchip_hdmi *hdmi,
+					    unsigned char addr)
+{
+	return readl_relaxed(hdmi->extphy_regbase + (addr) * 0x04);
+}
+
+static int rockchip_extphy_config(const struct dw_hdmi_plat_data *plat_data,
+				  int res, int pixelclock)
+{
+	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(plat_data);
+	const struct extphy_pll_config_tab *mpll = rockchip_extphy_pll_cfg;
+	const struct extphy_config_tab *ctrl = rockchip_extphy_cfg;
+	const struct extphy_pll_config_param *param;
+	unsigned long timeout;
+	int i, stat;
+
+	if (res >= DW_HDMI_RES_MAX) {
+		dev_err(hdmi->dev, "Extphy can't support res %d\n", res);
+		return -EINVAL;
+	}
+
+	/* Find out the extphy MPLL configure parameters */
+	for (i = 0; mpll[i].mpixelclock != ~0UL; i++)
+		if (pixelclock == mpll[i].mpixelclock)
+			break;
+	if (mpll[i].mpixelclock == ~0UL) {
+		dev_err(hdmi->dev, "Extphy can't support %dHz\n", pixelclock);
+		return -EINVAL;
+	}
+	param = &mpll[i].param[res];
+
+	regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2,
+		     RK3229_PLL_POWER_DOWN | RK3229_PLL_PDATA_DEN);
+
+	/*
+	 * Configure external HDMI PHY PLL registers.
+	 */
+	stat = ((param->pll_nf >> 1) & EXT_PHY_PLL_FB_BIT8_MASK) |
+	       ((param->vco_div_5 & 1) << 5) |
+	       (param->pll_nd & EXT_PHY_PLL_PRE_DIVIDER_MASK);
+	hdmi_extphy_write(hdmi, stat, EXT_PHY_PLL_PRE_DIVIDER);
+
+	hdmi_extphy_write(hdmi, param->pll_nf, EXT_PHY_PLL_FB_DIVIDER);
+
+	stat = (param->pclk_divider_a & EXT_PHY_PCLK_DIVIDERA_MASK) |
+	       ((param->pclk_divider_b & 3) << 5);
+	hdmi_extphy_write(hdmi, stat, EXT_PHY_PCLK_DIVIDER1);
+
+	stat = (param->pclk_divider_d & EXT_PHY_PCLK_DIVIDERD_MASK) |
+	       ((param->pclk_divider_c & 3) << 5);
+	hdmi_extphy_write(hdmi, stat, EXT_PHY_PCLK_DIVIDER2);
+
+	stat = ((param->tmsd_divider_c & 3) << 4) |
+	       ((param->tmsd_divider_a & 3) << 2) |
+	       (param->tmsd_divider_b & 3);
+	hdmi_extphy_write(hdmi, stat, EXT_PHY_TMDSCLK_DIVIDER);
+
+	hdmi_extphy_write(hdmi, param->ppll_nf, EXT_PHY_PPLL_FB_DIVIDER);
+
+	if (param->ppll_no == 1) {
+		hdmi_extphy_write(hdmi, 0, EXT_PHY_PPLL_POST_DIVIDER);
+
+		stat = 0x20 | param->ppll_nd;
+		hdmi_extphy_write(hdmi, stat, EXT_PHY_PPLL_PRE_DIVIDER);
+	} else {
+		stat = ((param->ppll_no / 2) - 1) << 4;
+		hdmi_extphy_write(hdmi, stat, EXT_PHY_PPLL_POST_DIVIDER);
+
+		stat = 0xe0 | param->ppll_nd;
+		hdmi_extphy_write(hdmi, stat, EXT_PHY_PPLL_PRE_DIVIDER);
+	}
+
+
+	/* Find out the external HDMI PHY driver configure parameters */
+	for (i = 0; ctrl[i].mpixelclock != ~0UL; i++)
+		if (pixelclock <= ctrl[i].mpixelclock)
+			break;
+	if (ctrl[i].mpixelclock == ~0UL) {
+		dev_err(hdmi->dev, "Extphy can't support %dHz\n", pixelclock);
+		return -EINVAL;
+	}
+
+	/*
+	 * Configure the external HDMI PHY driver registers.
+	 */
+	if (ctrl[i].slopeboost) {
+		hdmi_extphy_write(hdmi, 0xff, EXT_PHY_SIGNAL_CTRL);
+
+		stat = (ctrl[i].slopeboost - 1) & 3;
+		stat = (stat << 6) | (stat << 4) | (stat << 2) | stat;
+		hdmi_extphy_write(hdmi, stat, EXT_PHY_SLOPEBOOST);
+	} else
+		hdmi_extphy_write(hdmi, 0x0f, EXT_PHY_SIGNAL_CTRL);
+
+	stat = ctrl[i].pre_emphasis & 3;
+	stat = (stat << 4) | (stat << 2) | stat;
+	hdmi_extphy_write(hdmi, stat, EXT_PHY_PREEMPHASIS);
+
+	stat = ((ctrl[i].clk_level & 0xf) << 4) | (ctrl[i].data2_level & 0xf);
+	hdmi_extphy_write(hdmi, stat, EXT_PHY_LEVEL1);
+
+	stat = ((ctrl[i].data1_level & 0xf) << 4) | (ctrl[i].data0_level & 0xf);
+	hdmi_extphy_write(hdmi, stat, EXT_PHY_LEVEL2);
+
+	hdmi_extphy_write(hdmi, 0x22, 0xf3);
+
+	stat = clk_get_rate(hdmi->extphy_pclk) / 100000;
+	hdmi_extphy_write(hdmi, ((stat >> 8) & 0xff) | 0x80, EXT_PHY_TERM_CAL);
+	hdmi_extphy_write(hdmi,  stat & 0xff, EXT_PHY_TERM_CAL_DIV_L);
+
+	if (pixelclock > 340000000)
+		stat = EXT_PHY_AUTO_R100_OHMS;
+	else if (pixelclock > 200000000)
+		stat = EXT_PHY_AUTO_R50_OHMS;
+	else
+		stat = EXT_PHY_AUTO_ROPEN_CIRCUIT;
+	hdmi_extphy_write(hdmi, stat | 0x20, EXT_PHY_TERM_RESIS_AUTO);
+	hdmi_extphy_write(hdmi, (stat >> 8) & 0xff, EXT_PHY_TERM_CAL);
+
+	stat = (pixelclock > 200000000) ? 0 : 0x11;
+	hdmi_extphy_write(hdmi, stat, EXT_PHY_PLL_BW);
+	hdmi_extphy_write(hdmi, 0x27, EXT_PHY_PPLL_BW);
+
+	regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2, RK3229_PLL_POWER_UP);
+
+	/* Detect whether PLL is lock or not */
+	timeout = jiffies + msecs_to_jiffies(100);
+	while (!time_after(jiffies, timeout)) {
+		usleep_range(1000, 2000);
+		stat = hdmi_extphy_read(hdmi, EXT_PHY_PPLL_POST_DIVIDER);
+		if (stat & EXT_PHY_PPLL_LOCK_STATUS_MASK)
+			break;
+	}
+
+	regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2, RK3229_PLL_PDATA_EN);
+
+	if ((stat & EXT_PHY_PPLL_LOCK_STATUS_MASK) == 0) {
+		dev_err(hdmi->dev, "EXT PHY PLL not locked\n");
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
 static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 {
 	struct device_node *np = hdmi->dev->of_node;
+	struct platform_device *pdev;
+	struct resource *iores;
+	int ret;
+
+	pdev = container_of(hdmi->dev, struct platform_device, dev);
 
 	hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
 	if (IS_ERR(hdmi->regmap)) {
@@ -152,6 +424,37 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 		return PTR_ERR(hdmi->regmap);
 	}
 
+	if (hdmi->plat_data.dev_type == RK3229_HDMI) {
+		iores = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+		if (!iores)
+			return -ENXIO;
+
+		hdmi->extphy_regbase = devm_ioremap_resource(hdmi->dev, iores);
+		if (IS_ERR(hdmi->extphy_regbase)) {
+			dev_err(hdmi->dev, "failed to map extphy regbase\n");
+			return PTR_ERR(hdmi->extphy_regbase);
+		}
+
+		hdmi->extphy_pclk = devm_clk_get(hdmi->dev, "extphy");
+		if (IS_ERR(hdmi->extphy_pclk)) {
+			dev_err(hdmi->dev, "failed to get extphy clock\n");
+			return PTR_ERR(hdmi->extphy_pclk);
+		}
+
+		ret = clk_prepare_enable(hdmi->extphy_pclk);
+		if (ret) {
+			dev_err(hdmi->dev, "failed to enable extphy clk: %d\n",
+				ret);
+			return ret;
+		}
+
+		regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON6,
+			     RK3229_IO_3V_DOMAIN);
+
+		regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2,
+			     RK3229_DDC_MASK_EN);
+	}
+
 	return 0;
 }
 
@@ -159,17 +462,23 @@ static enum drm_mode_status
 dw_hdmi_rockchip_mode_valid(const struct dw_hdmi_plat_data *plat_data,
 			    struct drm_display_mode *mode)
 {
-	const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg;
 	int pclk = mode->clock * 1000;
 	bool valid = false;
 	int i;
 
-	for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) {
-		if (pclk == mpll_cfg[i].mpixelclock) {
-			valid = true;
-			break;
-		}
-	}
+	if (plat_data->dev_type == RK3288_HDMI)
+		for (i = 0; rockchip_mpll_cfg[i].mpixelclock != ~0UL; i++)
+			if (pclk == rockchip_mpll_cfg[i].mpixelclock) {
+				valid = true;
+				break;
+			}
+
+	if (plat_data->dev_type == RK3229_HDMI)
+		for (i = 0; rockchip_extphy_pll_cfg[i].mpixelclock != ~0UL; i++)
+			if (pclk == rockchip_extphy_pll_cfg[i].mpixelclock) {
+				valid = true;
+				break;
+			}
 
 	return (valid) ? MODE_OK : MODE_BAD;
 }
@@ -199,21 +508,30 @@ static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
 static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
 {
 	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
+	int out_mode = ROCKCHIP_OUT_MODE_AAAA;
 	u32 val;
 	int mux;
 
+	if (hdmi->plat_data.dev_type == RK3229_HDMI)
+		out_mode = ROCKCHIP_OUT_MODE_P888;
+
 	rockchip_drm_crtc_mode_config(encoder->crtc, DRM_MODE_CONNECTOR_HDMIA,
-				      ROCKCHIP_OUT_MODE_AAAA);
+				      out_mode);
 
-	mux = rockchip_drm_encoder_get_mux_id(hdmi->dev->of_node, encoder);
-	if (mux)
-		val = HDMI_SEL_VOP_LIT | (HDMI_SEL_VOP_LIT << 16);
-	else
-		val = HDMI_SEL_VOP_LIT << 16;
+	if (hdmi->plat_data.dev_type == RK3288_HDMI) {
+		mux = rockchip_drm_encoder_get_mux_id(hdmi->dev->of_node,
+						      encoder);
+		if (mux)
+			val = RK3288_HDMI_SEL_VOP_LIT |
+			      (RK3288_HDMI_SEL_VOP_LIT << 16);
+		else
+			val = RK3288_HDMI_SEL_VOP_LIT << 16;
 
-	regmap_write(hdmi->regmap, GRF_SOC_CON6, val);
-	dev_dbg(hdmi->dev, "vop %s output to hdmi\n",
-		(mux) ? "LIT" : "BIG");
+		regmap_write(hdmi->regmap, RK3288_GRF_SOC_CON6, val);
+
+		dev_dbg(hdmi->dev, "vop %s output to hdmi\n",
+			(mux) ? "LIT" : "BIG");
+	}
 }
 
 static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = {
@@ -223,7 +541,7 @@ static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_fun
 	.disable    = dw_hdmi_rockchip_encoder_disable,
 };
 
-static const struct dw_hdmi_plat_data rockchip_hdmi_drv_data = {
+static const struct dw_hdmi_plat_data rk3288_hdmi_drv_data = {
 	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg   = rockchip_mpll_cfg,
 	.cur_ctr    = rockchip_cur_ctr,
@@ -231,9 +549,18 @@ static const struct dw_hdmi_plat_data rockchip_hdmi_drv_data = {
 	.dev_type   = RK3288_HDMI,
 };
 
+static const struct dw_hdmi_plat_data rk3229_hdmi_drv_data = {
+	.mode_valid = dw_hdmi_rockchip_mode_valid,
+	.extphy_config = rockchip_extphy_config,
+	.dev_type   = RK3229_HDMI,
+};
+
 static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
 	{ .compatible = "rockchip,rk3288-dw-hdmi",
-	  .data = &rockchip_hdmi_drv_data
+	  .data = &rk3288_hdmi_drv_data
+	},
+	{ .compatible = "rockchip,rk3229-dw-hdmi",
+	  .data = &rk3229_hdmi_drv_data
 	},
 	{},
 };
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h
new file mode 100644
index 0000000..f7ec733
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h
@@ -0,0 +1,137 @@
+#ifndef __DW_HDMI_ROCKCHIP__
+#define __DW_HDMI_ROCKCHIP__
+
+struct extphy_config_tab {
+	u32 mpixelclock;
+	int pre_emphasis;
+	int slopeboost;
+	int clk_level;
+	int data0_level;
+	int data1_level;
+	int data2_level;
+};
+
+struct extphy_pll_config_tab {
+	unsigned long mpixelclock;
+	struct extphy_pll_config_param {
+		u8	pll_nd;
+		u16	pll_nf;
+		u8	tmsd_divider_a;
+		u8	tmsd_divider_b;
+		u8	tmsd_divider_c;
+		u8	pclk_divider_a;
+		u8	pclk_divider_b;
+		u8	pclk_divider_c;
+		u8	pclk_divider_d;
+		u8	vco_div_5;
+		u8	ppll_nd;
+		u8	ppll_nf;
+		u8	ppll_no;
+	} param[DW_HDMI_RES_MAX];
+};
+
+#define RK3288_GRF_SOC_CON6			0x025c
+#define RK3288_HDMI_SEL_VOP_LIT			(1 << 4)
+
+#define RK3229_GRF_SOC_CON6			0x0418
+#define RK3229_IO_3V_DOMAIN			((7 << 4) | (7 << (4 + 16)))
+
+#define RK3229_GRF_SOC_CON2			0x0408
+#define RK3229_DDC_MASK_EN			((3 << 13) | (3 << (13 + 16)))
+
+#define RK3229_PLL_POWER_DOWN			(BIT(12) | BIT(12 + 16))
+#define RK3229_PLL_POWER_UP			BIT(12 + 16)
+#define RK3229_PLL_PDATA_DEN			BIT(11 + 16)
+#define RK3229_PLL_PDATA_EN			(BIT(11) | BIT(11 + 16))
+
+/* PHY Defined for RK322X */
+#define EXT_PHY_CONTROL				0
+#define EXT_PHY_ANALOG_RESET_MASK		0x80
+#define EXT_PHY_DIGITAL_RESET_MASK		0x40
+#define EXT_PHY_PCLK_INVERT_MASK		0x08
+#define EXT_PHY_PREPCLK_INVERT_MASK		0x04
+#define EXT_PHY_TMDSCLK_INVERT_MASK		0x02
+#define EXT_PHY_SRC_SELECT_MASK			0x01
+
+#define EXT_PHY_TERM_CAL			0x03
+#define EXT_PHY_TERM_CAL_EN_MASK		0x80
+#define EXT_PHY_TERM_CAL_DIV_H_MASK		0x7f
+
+#define EXT_PHY_TERM_CAL_DIV_L			0x04
+
+#define EXT_PHY_PLL_PRE_DIVIDER			0xe2
+#define EXT_PHY_PLL_FB_BIT8_MASK		0x80
+#define EXT_PHY_PLL_PCLK_DIV5_EN_MASK		0x20
+#define EXT_PHY_PLL_PRE_DIVIDER_MASK		0x1f
+
+#define EXT_PHY_PLL_FB_DIVIDER			0xe3
+
+#define EXT_PHY_PCLK_DIVIDER1			0xe4
+#define EXT_PHY_PCLK_DIVIDERB_MASK		0x60
+#define EXT_PHY_PCLK_DIVIDERA_MASK		0x1f
+
+#define EXT_PHY_PCLK_DIVIDER2			0xe5
+#define EXT_PHY_PCLK_DIVIDERC_MASK		0x60
+#define EXT_PHY_PCLK_DIVIDERD_MASK		0x1f
+
+#define EXT_PHY_TMDSCLK_DIVIDER			0xe6
+#define EXT_PHY_TMDSCLK_DIVIDERC_MASK		0x30
+#define EXT_PHY_TMDSCLK_DIVIDERA_MASK		0x0c
+#define EXT_PHY_TMDSCLK_DIVIDERB_MASK		0x03
+
+#define EXT_PHY_PLL_BW				0xe7
+
+#define EXT_PHY_PPLL_PRE_DIVIDER		0xe9
+#define EXT_PHY_PPLL_ENABLE_MASK		0xc0
+#define EXT_PHY_PPLL_PRE_DIVIDER_MASK		0x1f
+
+#define EXT_PHY_PPLL_FB_DIVIDER			0xea
+
+#define EXT_PHY_PPLL_POST_DIVIDER		0xeb
+#define EXT_PHY_PPLL_FB_DIVIDER_BIT8_MASK	0x80
+#define EXT_PHY_PPLL_POST_DIVIDER_MASK		0x30
+#define EXT_PHY_PPLL_LOCK_STATUS_MASK		0x01
+
+#define EXT_PHY_PPLL_BW				0xec
+
+#define EXT_PHY_SIGNAL_CTRL			0xee
+#define EXT_PHY_TRANSITION_CLK_EN_MASK		0x80
+#define EXT_PHY_TRANSITION_D0_EN_MASK		0x40
+#define EXT_PHY_TRANSITION_D1_EN_MASK		0x20
+#define EXT_PHY_TRANSITION_D2_EN_MASK		0x10
+#define EXT_PHY_LEVEL_CLK_EN_MASK		0x08
+#define EXT_PHY_LEVEL_D0_EN_MASK		0x04
+#define EXT_PHY_LEVEL_D1_EN_MASK		0x02
+#define EXT_PHY_LEVEL_D2_EN_MASK		0x01
+
+#define EXT_PHY_SLOPEBOOST			0xef
+#define EXT_PHY_SLOPEBOOST_CLK_MASK		0x03
+#define EXT_PHY_SLOPEBOOST_D0_MASK		0x0c
+#define EXT_PHY_SLOPEBOOST_D1_MASK		0x30
+#define EXT_PHY_SLOPEBOOST_D2_MASK		0xc0
+
+#define EXT_PHY_PREEMPHASIS			0xf0
+#define EXT_PHY_PREEMPHASIS_D0_MASK		0x03
+#define EXT_PHY_PREEMPHASIS_D1_MASK		0x0c
+#define EXT_PHY_PREEMPHASIS_D2_MASK		0x30
+
+#define EXT_PHY_LEVEL1				0xf1
+#define EXT_PHY_LEVEL_CLK_MASK			0xf0
+#define EXT_PHY_LEVEL_D2_MASK			0x0f
+
+#define EXT_PHY_LEVEL2				0xf2
+#define EXT_PHY_LEVEL_D1_MASK			0xf0
+#define EXT_PHY_LEVEL_D0_MASK			0x0f
+
+#define EXT_PHY_TERM_RESIS_AUTO			0xf4
+#define EXT_PHY_AUTO_R50_OHMS			0
+#define EXT_PHY_AUTO_R75_OHMS			(1 << 2)
+#define EXT_PHY_AUTO_R100_OHMS			(2 << 2)
+#define EXT_PHY_AUTO_ROPEN_CIRCUIT		(3 << 2)
+
+#define EXT_PHY_TERM_RESIS_MANUAL_CLK		0xfb
+#define EXT_PHY_TERM_RESIS_MANUAL_D2		0xfc
+#define EXT_PHY_TERM_RESIS_MANUAL_D1		0xfd
+#define EXT_PHY_TERM_RESIS_MANUAL_D0		0xfe
+
+#endif /* __DW_HDMI_ROCKCHIP__ */
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
index f8dec64..4e63158 100644
--- a/include/drm/bridge/dw_hdmi.h
+++ b/include/drm/bridge/dw_hdmi.h
@@ -24,6 +24,7 @@ enum {
 enum dw_hdmi_devtype {
 	IMX6Q_HDMI,
 	IMX6DL_HDMI,
+	RK3229_HDMI,
 	RK3288_HDMI,
 };
 
@@ -54,6 +55,8 @@ struct dw_hdmi_plat_data {
 	const struct dw_hdmi_phy_config *phy_config;
 	enum drm_mode_status (*mode_valid)(const struct dw_hdmi_plat_data *pd,
 					   struct drm_display_mode *mode);
+	int (*extphy_config)(const struct dw_hdmi_plat_data *plat_data,
+			     int res_idx, int pixelclock);
 };
 
 void dw_hdmi_unbind(struct device *dev, struct device *master, void *data);
-- 
2.1.2




From ykk at rock-chips.com  Thu Jan  7 01:05:11 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Thu,  7 Jan 2016 17:05:11 +0800
Subject: [RFC PATCH v2 4/4] dt-bindings: add document for rk3229-hdmi
In-Reply-To: <1452156811-18150-1-git-send-email-ykk@rock-chips.com>
References: <1452156811-18150-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1452157511-18891-1-git-send-email-ykk@rock-chips.com>

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v2: None

 .../devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt         | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
index 668091f..1cdc627 100644
--- a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
+++ b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
@@ -2,7 +2,7 @@ Rockchip specific extensions to the Synopsys Designware HDMI
 ================================
 
 Required properties:
-- compatible: "rockchip,rk3288-dw-hdmi";
+- compatible: "rockchip,rk3288-dw-hdmi", "rockchip,rk3229-dw-hdmi";
 - reg: Physical base address and length of the controller's registers.
 - clocks: phandle to hdmi iahb and isfr clocks.
 - clock-names: should be "iahb" "isfr"
@@ -15,8 +15,10 @@ Required properties:
   rk3288 platform
 
 Optional properties
+- reg: Physical base address and length of the external HDMI PHY's registers.
 - ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
 - clocks, clock-names: phandle to the HDMI CEC clock, name should be "cec"
+		       phandle to the external HDMI PHY clock, name should be "extphy"
 
 Example:
 hdmi: hdmi at ff980000 {
-- 
2.1.2




From wxt at rock-chips.com  Thu Jan  7 01:18:07 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Thu,  7 Jan 2016 17:18:07 +0800
Subject: [PATCH] clk: rockchip: fix some clock gates for rk3368
Message-ID: <1452158287-18742-1-git-send-email-wxt@rock-chips.com>

From: Jianqun xu <jay.xu at rock-chips.com>

Reference to the Rockchip RK3368 TRM v1.1, some clock
gates need to be updated.

Signed-off-by: Jianqun xu <jay.xu at rock-chips.com>
Signed-off-by: Caesar Wang <wxt at rock-chips.com>
---

 drivers/clk/rockchip/clk-rk3368.c | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/drivers/clk/rockchip/clk-rk3368.c b/drivers/clk/rockchip/clk-rk3368.c
index be0ede5..21f3ea9 100644
--- a/drivers/clk/rockchip/clk-rk3368.c
+++ b/drivers/clk/rockchip/clk-rk3368.c
@@ -780,13 +780,13 @@ static struct rockchip_clk_branch rk3368_clk_branches[] __initdata = {
 	GATE(PCLK_TSADC, "pclk_tsadc", "pclk_peri", 0, RK3368_CLKGATE_CON(20), 0, GFLAGS),
 
 	/* pclk_pd_alive gates */
-	GATE(PCLK_TIMER1, "pclk_timer1", "pclk_pd_alive", 0, RK3368_CLKGATE_CON(14), 8, GFLAGS),
-	GATE(PCLK_TIMER0, "pclk_timer0", "pclk_pd_alive", 0, RK3368_CLKGATE_CON(14), 7, GFLAGS),
-	GATE(0, "pclk_alive_niu", "pclk_pd_alive", CLK_IGNORE_UNUSED, RK3368_CLKGATE_CON(14), 12, GFLAGS),
-	GATE(PCLK_GRF, "pclk_grf", "pclk_pd_alive", CLK_IGNORE_UNUSED, RK3368_CLKGATE_CON(14), 11, GFLAGS),
-	GATE(PCLK_GPIO3, "pclk_gpio3", "pclk_pd_alive", 0, RK3368_CLKGATE_CON(14), 3, GFLAGS),
-	GATE(PCLK_GPIO2, "pclk_gpio2", "pclk_pd_alive", 0, RK3368_CLKGATE_CON(14), 2, GFLAGS),
-	GATE(PCLK_GPIO1, "pclk_gpio1", "pclk_pd_alive", 0, RK3368_CLKGATE_CON(14), 1, GFLAGS),
+	GATE(PCLK_TIMER1, "pclk_timer1", "pclk_pd_alive", 0, RK3368_CLKGATE_CON(22), 13, GFLAGS),
+	GATE(PCLK_TIMER0, "pclk_timer0", "pclk_pd_alive", 0, RK3368_CLKGATE_CON(22), 12, GFLAGS),
+	GATE(0, "pclk_alive_niu", "pclk_pd_alive", CLK_IGNORE_UNUSED, RK3368_CLKGATE_CON(22), 9, GFLAGS),
+	GATE(PCLK_GRF, "pclk_grf", "pclk_pd_alive", CLK_IGNORE_UNUSED, RK3368_CLKGATE_CON(22), 8, GFLAGS),
+	GATE(PCLK_GPIO3, "pclk_gpio3", "pclk_pd_alive", 0, RK3368_CLKGATE_CON(22), 3, GFLAGS),
+	GATE(PCLK_GPIO2, "pclk_gpio2", "pclk_pd_alive", 0, RK3368_CLKGATE_CON(22), 2, GFLAGS),
+	GATE(PCLK_GPIO1, "pclk_gpio1", "pclk_pd_alive", 0, RK3368_CLKGATE_CON(22), 1, GFLAGS),
 
 	/*
 	 * pclk_vio gates
@@ -796,12 +796,12 @@ static struct rockchip_clk_branch rk3368_clk_branches[] __initdata = {
 	GATE(0, "pclk_dphytx", "hclk_vio", CLK_IGNORE_UNUSED, RK3368_CLKGATE_CON(14), 8, GFLAGS),
 
 	/* pclk_pd_pmu gates */
-	GATE(PCLK_PMUGRF, "pclk_pmugrf", "pclk_pd_pmu", CLK_IGNORE_UNUSED, RK3368_CLKGATE_CON(17), 0, GFLAGS),
-	GATE(PCLK_GPIO0, "pclk_gpio0", "pclk_pd_pmu", 0, RK3368_CLKGATE_CON(17), 4, GFLAGS),
-	GATE(PCLK_SGRF, "pclk_sgrf", "pclk_pd_pmu", CLK_IGNORE_UNUSED, RK3368_CLKGATE_CON(17), 3, GFLAGS),
-	GATE(0, "pclk_pmu_noc", "pclk_pd_pmu", CLK_IGNORE_UNUSED, RK3368_CLKGATE_CON(17), 2, GFLAGS),
-	GATE(0, "pclk_intmem1", "pclk_pd_pmu", CLK_IGNORE_UNUSED, RK3368_CLKGATE_CON(17), 1, GFLAGS),
-	GATE(PCLK_PMU, "pclk_pmu", "pclk_pd_pmu", CLK_IGNORE_UNUSED, RK3368_CLKGATE_CON(17), 2, GFLAGS),
+	GATE(PCLK_PMUGRF, "pclk_pmugrf", "pclk_pd_pmu", CLK_IGNORE_UNUSED, RK3368_CLKGATE_CON(23), 5, GFLAGS),
+	GATE(PCLK_GPIO0, "pclk_gpio0", "pclk_pd_pmu", 0, RK3368_CLKGATE_CON(23), 4, GFLAGS),
+	GATE(PCLK_SGRF, "pclk_sgrf", "pclk_pd_pmu", CLK_IGNORE_UNUSED, RK3368_CLKGATE_CON(23), 3, GFLAGS),
+	GATE(0, "pclk_pmu_noc", "pclk_pd_pmu", CLK_IGNORE_UNUSED, RK3368_CLKGATE_CON(23), 2, GFLAGS),
+	GATE(0, "pclk_intmem1", "pclk_pd_pmu", CLK_IGNORE_UNUSED, RK3368_CLKGATE_CON(23), 1, GFLAGS),
+	GATE(PCLK_PMU, "pclk_pmu", "pclk_pd_pmu", CLK_IGNORE_UNUSED, RK3368_CLKGATE_CON(23), 0, GFLAGS),
 
 	/* timer gates */
 	GATE(0, "sclk_timer15", "xin24m", CLK_IGNORE_UNUSED, RK3368_CLKGATE_CON(24), 11, GFLAGS),
-- 
1.9.1



From ykk at rock-chips.com  Thu Jan  7 02:01:36 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Thu,  7 Jan 2016 18:01:36 +0800
Subject: [PATCH v2 0/2] Introduce Innosilicon HDMI driver on Rockchip platforms
Message-ID: <1452160896-27096-1-git-send-email-ykk@rock-chips.com>


Here are a brief introduction to Innosilicon HDMI IP:
 - Support HDMI 1.4a, HDCP 1.2 and DVI 1.0 standard compliant transmitter
 - Support HDMI1.4 a/b 3D function defined in HDMI 1.4 a/b spec
 - Digital video interface supports a pixel size of 24, 30, 36, 48bits color
   depth in RGB
 - S/PDIF output supports PCM, Dolby Digital, DTS digital audio transmission
   (32-192kHz Fs) using IEC60958 and IEC 61937
 - The EDID and CEC function are also supported by Innosilicon HDMI Transmitter
   Controlle


Changes in v2:
- Using DRM atomic helper functions for connector init (Mark)
- Remove "hdmi->connector.encoder = encoder;" (Mark)
- Correct the misspell "rk3036-dw-hdmi" (Heiko)

Yakir Yang (2):
  drm: rockchip/hdmi: add Innosilicon HDMI support
  dt-bindings: add document for Innosilicon HDMI on Rockchip platform

 .../display/rockchip/inno_hdmi-rockchip.txt        |   50 +
 drivers/gpu/drm/rockchip/Kconfig                   |    8 +
 drivers/gpu/drm/rockchip/Makefile                  |    1 +
 drivers/gpu/drm/rockchip/inno_hdmi.c               | 1010 ++++++++++++++++++++
 drivers/gpu/drm/rockchip/inno_hdmi.h               |  364 +++++++
 5 files changed, 1433 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt
 create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.c
 create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.h

-- 
2.1.2




From ykk at rock-chips.com  Thu Jan  7 02:03:44 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Thu,  7 Jan 2016 18:03:44 +0800
Subject: [PATCH v2 1/2] drm: rockchip/hdmi: add Innosilicon HDMI support
In-Reply-To: <1452160896-27096-1-git-send-email-ykk@rock-chips.com>
References: <1452160896-27096-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1452161024-27198-1-git-send-email-ykk@rock-chips.com>

The Innosilicon HDMI is a low power HDMI 1.4 transmitter
IP, and it have been integrated on some rockchip CPUs
(like RK3036, RK312x).

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v2:
- Using DRM atomic helper functions for connector init (Mark)
- Remove "hdmi->connector.encoder = encoder;" (Mark)

 drivers/gpu/drm/rockchip/Kconfig     |    8 +
 drivers/gpu/drm/rockchip/Makefile    |    1 +
 drivers/gpu/drm/rockchip/inno_hdmi.c | 1010 ++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/inno_hdmi.h |  364 ++++++++++++
 4 files changed, 1383 insertions(+)
 create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.c
 create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.h

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 35215f6..a5014e0 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -25,3 +25,11 @@ config ROCKCHIP_DW_HDMI
 	  for the Synopsys DesignWare HDMI driver. If you want to
 	  enable HDMI on RK3288 based SoC, you should selet this
 	  option.
+
+config ROCKCHIP_INNO_HDMI
+	tristate "Rockchip specific extensions for Innosilicon HDMI"
+        depends on DRM_ROCKCHIP
+        help
+	  This selects support for Rockchip SoC specific extensions
+	  for the Innosilicon HDMI driver. If you want to enable
+	  HDMI on RK3036 based SoC, you should selet this option.
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index a9d380f..da2bf76 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -6,6 +6,7 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
 		rockchip_drm_gem.o
 
 obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
+obj-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
 
 obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_drm_vop.o \
 				rockchip_vop_reg.o
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
new file mode 100644
index 0000000..9327617
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -0,0 +1,1010 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ *    Zheng Yang <zhengyang at rock-chips.com>
+ *    Yakir Yang <ykk at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/hdmi.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+
+#include <drm/drm_of.h>
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_encoder_slave.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_vop.h"
+
+#include "inno_hdmi.h"
+
+#define to_inno_hdmi(x)	container_of(x, struct inno_hdmi, x)
+
+struct hdmi_data_info {
+	int vic;
+	bool sink_is_hdmi;
+	bool sink_has_audio;
+	unsigned int enc_in_format;
+	unsigned int enc_out_format;
+	unsigned int colorimetry;
+};
+
+struct inno_hdmi_i2c {
+	struct i2c_adapter adap;
+
+	u8 ddc_addr;
+	u8 segment_addr;
+
+	struct mutex lock;
+	struct completion cmp;
+};
+
+struct inno_hdmi {
+	struct device *dev;
+	struct drm_device *drm_dev;
+
+	int irq;
+	struct clk *pclk;
+	void __iomem *regs;
+
+	struct drm_connector	connector;
+	struct drm_encoder	encoder;
+
+	struct inno_hdmi_i2c *i2c;
+	struct i2c_adapter *ddc;
+
+	int dpms_mode;
+	unsigned int tmds_rate;
+
+	struct hdmi_data_info	hdmi_data;
+	struct drm_display_mode previous_mode;
+};
+
+enum {
+	CSC_ITU601_16_235_TO_RGB_0_255_8BIT,
+	CSC_ITU601_0_255_TO_RGB_0_255_8BIT,
+	CSC_ITU709_16_235_TO_RGB_0_255_8BIT,
+	CSC_RGB_0_255_TO_ITU601_16_235_8BIT,
+	CSC_RGB_0_255_TO_ITU709_16_235_8BIT,
+	CSC_RGB_0_255_TO_RGB_16_235_8BIT,
+};
+
+static const char coeff_csc[][24] = {
+	/*
+	 * YUV2RGB:601 SD mode(Y[16:235], UV[16:240], RGB[0:255]):
+	 *   R = 1.164*Y + 1.596*V - 204
+	 *   G = 1.164*Y - 0.391*U - 0.813*V + 154
+	 *   B = 1.164*Y + 2.018*U - 258
+	 */
+	{
+		0x04, 0xa7, 0x00, 0x00, 0x06, 0x62, 0x02, 0xcc,
+		0x04, 0xa7, 0x11, 0x90, 0x13, 0x40, 0x00, 0x9a,
+		0x04, 0xa7, 0x08, 0x12, 0x00, 0x00, 0x03, 0x02
+	},
+	/*
+	 * YUV2RGB:601 SD mode(YUV[0:255],RGB[0:255]):
+	 *   R = Y + 1.402*V - 248
+	 *   G = Y - 0.344*U - 0.714*V + 135
+	 *   B = Y + 1.772*U - 227
+	 */
+	{
+		0x04, 0x00, 0x00, 0x00, 0x05, 0x9b, 0x02, 0xf8,
+		0x04, 0x00, 0x11, 0x60, 0x12, 0xdb, 0x00, 0x87,
+		0x04, 0x00, 0x07, 0x16, 0x00, 0x00, 0x02, 0xe3
+	},
+	/*
+	 * YUV2RGB:709 HD mode(Y[16:235],UV[16:240],RGB[0:255]):
+	 *   R = 1.164*Y + 1.793*V - 248
+	 *   G = 1.164*Y - 0.213*U - 0.534*V + 77
+	 *   B = 1.164*Y + 2.115*U - 289
+	 */
+	{
+		0x04, 0xa7, 0x00, 0x00, 0x07, 0x2c, 0x02, 0xf8,
+		0x04, 0xa7, 0x10, 0xda, 0x12, 0x22, 0x00, 0x4d,
+		0x04, 0xa7, 0x08, 0x74, 0x00, 0x00, 0x03, 0x21
+	},
+
+	/*
+	 * RGB2YUV:601 SD mode:
+	 *   Cb = -0.291G - 0.148R + 0.439B + 128
+	 *   Y  = 0.504G  + 0.257R + 0.098B + 16
+	 *   Cr = -0.368G + 0.439R - 0.071B + 128
+	 */
+	{
+		0x11, 0x5f, 0x01, 0x82, 0x10, 0x23, 0x00, 0x80,
+		0x02, 0x1c, 0x00, 0xa1, 0x00, 0x36, 0x00, 0x1e,
+		0x11, 0x29, 0x10, 0x59, 0x01, 0x82, 0x00, 0x80
+	},
+	/*
+	 * RGB2YUV:709 HD mode:
+	 *   Cb = - 0.338G - 0.101R + 0.439B + 128
+	 *   Y  = 0.614G   + 0.183R + 0.062B + 16
+	 *   Cr = - 0.399G + 0.439R - 0.040B + 128
+	 */
+	{
+		0x11, 0x98, 0x01, 0xc1, 0x10, 0x28, 0x00, 0x80,
+		0x02, 0x74, 0x00, 0xbb, 0x00, 0x3f, 0x00, 0x10,
+		0x11, 0x5a, 0x10, 0x67, 0x01, 0xc1, 0x00, 0x80
+	},
+	/*
+	 * RGB[0:255]2RGB[16:235]:
+	 *   R' = R x (235-16)/255 + 16;
+	 *   G' = G x (235-16)/255 + 16;
+	 *   B' = B x (235-16)/255 + 16;
+	 */
+	{
+		0x00, 0x00, 0x03, 0x6F, 0x00, 0x00, 0x00, 0x10,
+		0x03, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+		0x00, 0x00, 0x00, 0x00, 0x03, 0x6F, 0x00, 0x10
+	},
+};
+
+static inline u8 hdmi_readb(struct inno_hdmi *hdmi, u16 offset)
+{
+	return readl_relaxed(hdmi->regs + (offset) * 0x04);
+}
+
+static inline void hdmi_writeb(struct inno_hdmi *hdmi, u16 offset, u32 val)
+{
+	writel_relaxed(val, hdmi->regs + (offset) * 0x04);
+}
+
+static inline void hdmi_modb(struct inno_hdmi *hdmi, u16 offset,
+			     u32 msk, u32 val)
+{
+	u8 temp = hdmi_readb(hdmi, offset) & ~msk;
+
+	temp |= val & msk;
+	hdmi_writeb(hdmi, offset, temp);
+}
+
+static void inno_hdmi_i2c_init(struct inno_hdmi *hdmi)
+{
+	int ddc_bus_freq;
+
+	ddc_bus_freq = (hdmi->tmds_rate >> 2) / HDMI_SCL_RATE;
+
+	hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
+	hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
+
+	/* Clear the EDID interrupt flag and mute the interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+}
+
+static void inno_hdmi_sys_power(struct inno_hdmi *hdmi, bool enable)
+{
+	if (enable)
+		hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON);
+	else
+		hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
+}
+
+static void inno_hdmi_set_pwr_mode(struct inno_hdmi *hdmi, int mode)
+{
+	switch (mode) {
+	case NORMAL:
+		inno_hdmi_sys_power(hdmi, false);
+
+		hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x6f);
+		hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0xbb);
+
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x14);
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x10);
+		hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x0f);
+		hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x01);
+
+		inno_hdmi_sys_power(hdmi, true);
+
+		break;
+
+	case LOWER_PWR:
+		inno_hdmi_sys_power(hdmi, false);
+
+		hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x17);
+
+		break;
+
+	default:
+		dev_err(hdmi->dev, "Unknown power mode %d\n", mode);
+	}
+}
+
+static void inno_hdmi_reset(struct inno_hdmi *hdmi)
+{
+	u32 val;
+	u32 msk;
+
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_DIGITAL, v_NOT_RST_DIGITAL);
+	udelay(100);
+
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_ANALOG, v_NOT_RST_ANALOG);
+	udelay(100);
+
+	msk = m_REG_CLK_INV | m_REG_CLK_SOURCE | m_POWER | m_INT_POL;
+	val = v_REG_CLK_INV | v_REG_CLK_SOURCE_SYS | v_PWR_ON | v_INT_POL_HIGH;
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, msk, val);
+
+	inno_hdmi_set_pwr_mode(hdmi, NORMAL);
+}
+
+static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi)
+{
+	char info[HDMI_SIZE_AVI_INFOFRAME] = {0};
+	int avi_color_mode;
+	int i;
+
+	hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_AVI);
+
+	info[0] = 0x82;
+	info[1] = 0x02;
+	info[2] = 0x0D;
+	info[3] = info[0] + info[1] + info[2];
+
+	if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_RGB)
+		avi_color_mode = AVI_COLOR_MODE_RGB;
+	else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV444)
+		avi_color_mode = AVI_COLOR_MODE_YCBCR444;
+	else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV422)
+		avi_color_mode = AVI_COLOR_MODE_YCBCR422;
+	else
+		avi_color_mode = AVI_COLOR_MODE_RGB;
+
+	info[4] = (avi_color_mode << 5);
+	info[5] = (AVI_COLORIMETRY_NO_DATA << 6) |
+		  (AVI_CODED_FRAME_ASPECT_NO_DATA << 4) |
+		  ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME;
+
+	info[6] = 0;
+	info[7] = hdmi->hdmi_data.vic;
+
+	if (hdmi->hdmi_data.vic == 6 || hdmi->hdmi_data.vic == 7 ||
+	    hdmi->hdmi_data.vic == 21 || hdmi->hdmi_data.vic == 22)
+		info[8] = 1;
+	else
+		info[8] = 0;
+
+	/* Calculate avi info frame checKsum */
+	for (i = 4; i < HDMI_SIZE_AVI_INFOFRAME; i++)
+		info[3] += info[i];
+	info[3] = 0x100 - info[3];
+
+	for (i = 0; i < HDMI_SIZE_AVI_INFOFRAME; i++)
+		hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i, info[i]);
+
+	return 0;
+}
+
+static int inno_hdmi_config_video_vsi(struct inno_hdmi *hdmi)
+{
+	char info[HDMI_SIZE_VSI_INFOFRAME] = {0};
+	int i;
+
+	hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, m_PACKET_VSI_EN,
+		  v_PACKET_VSI_EN(0));
+
+	hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_VSI);
+
+	/* Header Bytes */
+	info[0] = 0x81;
+	info[1] = 0x01;
+
+	/* PB1 - PB3 contain the 24bit IEEE Registration Identifier */
+	info[4] = 0x03;
+	info[5] = 0x0c;
+	info[6] = 0x00;
+
+	/* PB4 - HDMI_Video_Format into bits 7:5 */
+	info[7] = 0;
+
+	/*
+	 * PB5 - Depending on the video format, this byte will contain
+	 * either the HDMI_VIC code in buts 7:0, OR the 3D_Structure in
+	 * bits 7:4.
+	 */
+	info[2] = 0x06 - 2;
+	info[8] = 0;
+	info[9] = 0;
+
+	info[3] = info[0] + info[1] + info[2];
+
+	/* Calculate info frame checKsum */
+	for (i = 4; i < HDMI_SIZE_VSI_INFOFRAME; i++)
+		info[3] += info[i];
+	info[3] = 0x100 - info[3];
+
+	for (i = 0; i < HDMI_SIZE_VSI_INFOFRAME; i++)
+		hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i, info[i]);
+
+	hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, m_PACKET_VSI_EN,
+		  v_PACKET_VSI_EN(1));
+
+	return 0;
+}
+
+static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
+{
+	struct hdmi_data_info *data = &hdmi->hdmi_data;
+	int c0_c2_change = 0;
+	int csc_enable = 0;
+	int csc_mode = 0;
+	int auto_csc = 0;
+	int value;
+	int i;
+
+	/* Input video mode is SDR RGB24bit, data enable signal from external */
+	hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL1, v_DE_EXTERNAL |
+		    v_VIDEO_INPUT_FORMAT(VIDEO_INPUT_SDR_RGB444));
+
+	/* Input color hardcode to RGB, and output color hardcode to RGB888 */
+	value = v_VIDEO_INPUT_BITS(VIDEO_INPUT_8BITS) |
+		v_VIDEO_OUTPUT_COLOR(0) |
+		v_VIDEO_INPUT_CSP(0);
+	hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL2, value);
+
+	if (data->enc_out_format == data->enc_out_format) {
+		if ((data->enc_in_format == HDMI_COLORSPACE_RGB) ||
+		    (data->enc_in_format >= HDMI_COLORSPACE_YUV444)) {
+			value = v_SOF_DISABLE | v_COLOR_DEPTH_NOT_INDICATED(1);
+			hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
+
+			hdmi_modb(hdmi, HDMI_VIDEO_CONTRL,
+				  m_VIDEO_AUTO_CSC | m_VIDEO_C0_C2_SWAP,
+				  v_VIDEO_AUTO_CSC(AUTO_CSC_DISABLE) |
+				  v_VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE));
+			return 0;
+		}
+	}
+
+	if (data->colorimetry == HDMI_COLORIMETRY_ITU_601) {
+		if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
+		    (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
+			csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT;
+			auto_csc = AUTO_CSC_DISABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = v_CSC_ENABLE;
+		} else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
+			   (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
+			csc_mode = CSC_ITU601_16_235_TO_RGB_0_255_8BIT;
+			auto_csc = AUTO_CSC_ENABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = v_CSC_DISABLE;
+		}
+	} else {
+		if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
+		    (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
+			csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT;
+			auto_csc = AUTO_CSC_DISABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = v_CSC_ENABLE;
+		} else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
+			   (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
+			csc_mode = CSC_ITU709_16_235_TO_RGB_0_255_8BIT;
+			auto_csc = AUTO_CSC_ENABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = v_CSC_DISABLE;
+		}
+	}
+
+	for (i = 0; i < 24; i++)
+		hdmi_writeb(hdmi, HDMI_VIDEO_CSC_COEF + i,
+			    coeff_csc[csc_mode][i]);
+
+	value = v_SOF_DISABLE | csc_enable | v_COLOR_DEPTH_NOT_INDICATED(1);
+	hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
+	hdmi_modb(hdmi, HDMI_VIDEO_CONTRL, m_VIDEO_AUTO_CSC |
+		  m_VIDEO_C0_C2_SWAP, v_VIDEO_AUTO_CSC(auto_csc) |
+		  v_VIDEO_C0_C2_SWAP(c0_c2_change));
+
+	return 0;
+}
+
+static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi,
+					 struct drm_display_mode *mode)
+{
+	int value;
+
+	/* Set detail external video timing polarity and interlace mode */
+	value = v_EXTERANL_VIDEO(1);
+	value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
+		 v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
+	value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
+		 v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
+	value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
+		 v_INETLACE(1) : v_INETLACE(0);
+	hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
+
+	/* Set detail external video timing */
+	value = mode->htotal;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
+
+	value = mode->htotal - mode->hdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
+
+	value = mode->hsync_start - mode->hdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
+
+	value = mode->hsync_end - mode->hsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
+
+	value = mode->vtotal;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
+
+	value = mode->vtotal - mode->vdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
+
+	value = mode->vsync_start - mode->vdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
+
+	value = mode->vsync_end - mode->vsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
+
+	hdmi_writeb(hdmi, HDMI_PHY_PRE_DIV_RATIO, 0x1e);
+	hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_LOW, 0x2c);
+	hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH, 0x01);
+
+	return 0;
+}
+
+static int inno_hdmi_setup(struct inno_hdmi *hdmi,
+			   struct drm_display_mode *mode)
+{
+	int value;
+
+	hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
+
+	hdmi->hdmi_data.enc_in_format = HDMI_COLORSPACE_RGB;
+	hdmi->hdmi_data.enc_out_format = HDMI_COLORSPACE_RGB;
+
+	if ((hdmi->hdmi_data.vic == 6) || (hdmi->hdmi_data.vic == 7) ||
+	    (hdmi->hdmi_data.vic == 21) || (hdmi->hdmi_data.vic == 22) ||
+	    (hdmi->hdmi_data.vic == 2) || (hdmi->hdmi_data.vic == 3) ||
+	    (hdmi->hdmi_data.vic == 17) || (hdmi->hdmi_data.vic == 18))
+		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601;
+	else
+		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
+
+	/* Mute video and audio output */
+	hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
+		  v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1));
+
+	/* Set HDMI Mode */
+	hdmi_writeb(hdmi, HDMI_HDCP_CTRL,
+		    v_HDMI_DVI(hdmi->hdmi_data.sink_is_hdmi));
+
+	inno_hdmi_config_video_timing(hdmi, mode);
+
+	inno_hdmi_config_video_csc(hdmi);
+
+	if (hdmi->hdmi_data.sink_is_hdmi) {
+		inno_hdmi_config_video_avi(hdmi);
+		inno_hdmi_config_video_vsi(hdmi);
+	}
+
+	/*
+	 * When IP controller have configured to an accurate video
+	 * timing, then the TMDS clock source would be switched to
+	 * DCLK_LCDC, so we need to init the TMDS rate to mode pixel
+	 * clock rate, and reconfigure the DDC clock.
+	 */
+	hdmi->tmds_rate = mode->clock * 1000;
+	inno_hdmi_i2c_init(hdmi);
+
+	/* Unmute video and audio output */
+	hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
+		  v_AUDIO_MUTE(0) | v_VIDEO_MUTE(0));
+
+	return 0;
+}
+
+static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder,
+				       struct drm_display_mode *mode,
+				       struct drm_display_mode *adj_mode)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+
+	inno_hdmi_setup(hdmi, adj_mode);
+
+	/* Store the display mode for plugin/DKMS poweron events */
+	memcpy(&hdmi->previous_mode, adj_mode, sizeof(hdmi->previous_mode));
+}
+
+static void inno_hdmi_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+
+	if (hdmi->dpms_mode == mode)
+		return;
+
+	switch (mode) {
+	case DRM_MODE_DPMS_ON:
+		inno_hdmi_set_pwr_mode(hdmi, NORMAL);
+		break;
+	case DRM_MODE_DPMS_STANDBY:
+	case DRM_MODE_DPMS_SUSPEND:
+	case DRM_MODE_DPMS_OFF:
+		inno_hdmi_set_pwr_mode(hdmi, LOWER_PWR);
+		break;
+	default:
+		break;
+	}
+
+	hdmi->dpms_mode = mode;
+}
+
+static void inno_hdmi_encoder_commit(struct drm_encoder *encoder)
+{
+}
+
+static void inno_hdmi_encoder_prepare(struct drm_encoder *encoder)
+{
+	rockchip_drm_crtc_mode_config(encoder->crtc, DRM_MODE_CONNECTOR_HDMIA,
+				      ROCKCHIP_OUT_MODE_P888);
+}
+
+static bool inno_hdmi_encoder_mode_fixup(struct drm_encoder *encoder,
+					 const struct drm_display_mode *mode,
+					 struct drm_display_mode *adj_mode)
+{
+	return true;
+}
+
+static struct drm_encoder_helper_funcs inno_hdmi_encoder_helper_funcs = {
+	.dpms       = inno_hdmi_encoder_dpms,
+	.mode_fixup = inno_hdmi_encoder_mode_fixup,
+	.mode_set   = inno_hdmi_encoder_mode_set,
+	.prepare    = inno_hdmi_encoder_prepare,
+	.commit     = inno_hdmi_encoder_commit,
+};
+
+static struct drm_encoder_funcs inno_hdmi_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static enum drm_connector_status
+inno_hdmi_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+
+	return (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
+		connector_status_connected : connector_status_disconnected;
+}
+
+static int inno_hdmi_connector_get_modes(struct drm_connector *connector)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+	struct edid *edid;
+	int ret;
+
+	if (!hdmi->ddc)
+		return 0;
+
+	edid = drm_get_edid(connector, hdmi->ddc);
+	if (edid) {
+		hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
+		hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
+		drm_mode_connector_update_edid_property(connector, edid);
+		ret = drm_add_edid_modes(connector, edid);
+		kfree(edid);
+	}
+
+	return ret;
+}
+
+static enum drm_mode_status
+inno_hdmi_connector_mode_valid(struct drm_connector *connector,
+			       struct drm_display_mode *mode)
+{
+	return MODE_OK;
+}
+
+static struct drm_encoder *
+inno_hdmi_connector_best_encoder(struct drm_connector *connector)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+
+	return &hdmi->encoder;
+}
+
+static int
+inno_hdmi_probe_single_connector_modes(struct drm_connector *connector,
+				       uint32_t maxX, uint32_t maxY)
+{
+	return drm_helper_probe_single_connector_modes(connector, 1920, 1080);
+}
+
+static void inno_hdmi_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs inno_hdmi_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.fill_modes = inno_hdmi_probe_single_connector_modes,
+	.detect = inno_hdmi_connector_detect,
+	.destroy = inno_hdmi_connector_destroy,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static struct drm_connector_helper_funcs inno_hdmi_connector_helper_funcs = {
+	.get_modes = inno_hdmi_connector_get_modes,
+	.mode_valid = inno_hdmi_connector_mode_valid,
+	.best_encoder = inno_hdmi_connector_best_encoder,
+};
+
+static int inno_hdmi_register(struct drm_device *drm, struct inno_hdmi *hdmi)
+{
+	struct drm_encoder *encoder = &hdmi->encoder;
+
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, hdmi->dev->of_node);
+	/*
+	 * If we failed to find the CRTC(s) which this encoder is
+	 * supposed to be connected to, it's because the CRTC has
+	 * not been registered yet.  Defer probing, and hope that
+	 * the required CRTC is added later.
+	 */
+	if (encoder->possible_crtcs == 0)
+		return -EPROBE_DEFER;
+
+	drm_encoder_helper_add(encoder, &inno_hdmi_encoder_helper_funcs);
+	drm_encoder_init(drm, encoder, &inno_hdmi_encoder_funcs,
+			 DRM_MODE_ENCODER_TMDS);
+
+	hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
+
+	drm_connector_helper_add(&hdmi->connector,
+				 &inno_hdmi_connector_helper_funcs);
+	drm_connector_init(drm, &hdmi->connector, &inno_hdmi_connector_funcs,
+			   DRM_MODE_CONNECTOR_HDMIA);
+
+	drm_mode_connector_attach_encoder(&hdmi->connector, encoder);
+
+	return 0;
+}
+
+static irqreturn_t inno_hdmi_i2c_irq(struct inno_hdmi *hdmi)
+{
+	struct inno_hdmi_i2c *i2c = hdmi->i2c;
+	u8 stat;
+
+	stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
+	if (!(stat & m_INT_EDID_READY))
+		return IRQ_NONE;
+
+	/* Clear HDMI EDID interrupt flag */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+
+	complete(&i2c->cmp);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t inno_hdmi_hardirq(int irq, void *dev_id)
+{
+	struct inno_hdmi *hdmi = dev_id;
+	irqreturn_t ret = IRQ_NONE;
+	u8 interrupt;
+
+	if (hdmi->i2c)
+		ret = inno_hdmi_i2c_irq(hdmi);
+
+	interrupt = hdmi_readb(hdmi, HDMI_STATUS);
+	if (interrupt & m_INT_HOTPLUG) {
+		hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG);
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	return ret;
+}
+
+static irqreturn_t inno_hdmi_irq(int irq, void *dev_id)
+{
+	struct inno_hdmi *hdmi = dev_id;
+
+	drm_helper_hpd_irq_event(hdmi->connector.dev);
+
+	return IRQ_HANDLED;
+}
+
+static int inno_hdmi_i2c_wait(struct inno_hdmi *hdmi)
+{
+	struct inno_hdmi_i2c *i2c = hdmi->i2c;
+	int stat;
+
+	stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
+	if (!stat) {
+		stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
+		if (!stat)
+			return -EAGAIN;
+	}
+
+	return 0;
+}
+
+static int inno_hdmi_i2c_read(struct inno_hdmi *hdmi, struct i2c_msg *msgs)
+{
+	int length = msgs->len;
+	u8 *buf = msgs->buf;
+	int ret;
+
+	ret = inno_hdmi_i2c_wait(hdmi);
+	if (ret)
+		return ret;
+
+	while (length--)
+		*buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
+
+	return 0;
+}
+
+static int inno_hdmi_i2c_write(struct inno_hdmi *hdmi, struct i2c_msg *msgs)
+{
+	struct inno_hdmi_i2c *i2c = hdmi->i2c;
+
+	/*
+	 * The DDC module only support read EDID message, so
+	 * we assume that each word write to this i2c adapter
+	 * should be the offset of EDID word address.
+	 */
+	if ((msgs->len != 1) ||
+	    ((msgs->addr != DDC_ADDR) && (msgs->addr != DDC_SEGMENT_ADDR)))
+		return -EINVAL;
+
+	reinit_completion(&i2c->cmp);
+
+	if (msgs->addr == DDC_SEGMENT_ADDR)
+		hdmi->i2c->segment_addr = msgs->buf[0];
+	if (msgs->addr == DDC_ADDR)
+		hdmi->i2c->ddc_addr = msgs->buf[0];
+
+	/* Set edid fifo first addr */
+	hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
+
+	/* Set edid word address 0x00/0x80 */
+	hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
+
+	/* Set edid segment pointer */
+	hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr);
+
+	return 0;
+}
+
+static int inno_hdmi_i2c_xfer(struct i2c_adapter *adap,
+			      struct i2c_msg *msgs, int num)
+{
+	struct inno_hdmi *hdmi = i2c_get_adapdata(adap);
+	struct inno_hdmi_i2c *i2c = hdmi->i2c;
+	int i, ret = 0;
+
+	mutex_lock(&i2c->lock);
+
+	/* Clear the EDID interrupt flag and unmute the interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY);
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+
+	for (i = 0; i < num; i++) {
+		dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n",
+			i + 1, num, msgs[i].len, msgs[i].flags);
+
+		if (msgs[i].flags & I2C_M_RD)
+			ret = inno_hdmi_i2c_read(hdmi, &msgs[i]);
+		else
+			ret = inno_hdmi_i2c_write(hdmi, &msgs[i]);
+
+		if (ret < 0)
+			break;
+	}
+
+	if (!ret)
+		ret = num;
+
+	/* Mute HDMI EDID interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
+
+	mutex_unlock(&i2c->lock);
+
+	return ret;
+}
+
+static u32 inno_hdmi_i2c_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm inno_hdmi_algorithm = {
+	.master_xfer	= inno_hdmi_i2c_xfer,
+	.functionality	= inno_hdmi_i2c_func,
+};
+
+static struct i2c_adapter *inno_hdmi_i2c_adapter(struct inno_hdmi *hdmi)
+{
+	struct i2c_adapter *adap;
+	struct inno_hdmi_i2c *i2c;
+	int ret;
+
+	i2c = devm_kzalloc(hdmi->dev, sizeof(*i2c), GFP_KERNEL);
+	if (!i2c)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_init(&i2c->lock);
+	init_completion(&i2c->cmp);
+
+	adap = &i2c->adap;
+	adap->class = I2C_CLASS_DDC;
+	adap->owner = THIS_MODULE;
+	adap->dev.parent = hdmi->dev;
+	adap->dev.of_node = hdmi->dev->of_node;
+	adap->algo = &inno_hdmi_algorithm;
+	strlcpy(adap->name, "Inno HDMI", sizeof(adap->name));
+	i2c_set_adapdata(adap, hdmi);
+
+	ret = i2c_add_adapter(adap);
+	if (ret) {
+		dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name);
+		devm_kfree(hdmi->dev, i2c);
+		return ERR_PTR(ret);
+	}
+
+	hdmi->i2c = i2c;
+
+	dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name);
+
+	return adap;
+}
+
+static int inno_hdmi_bind(struct device *dev, struct device *master,
+				 void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = data;
+	struct inno_hdmi *hdmi;
+	struct resource *iores;
+	int irq;
+	int ret;
+
+	hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
+	if (!hdmi)
+		return -ENOMEM;
+
+	hdmi->dev = dev;
+	hdmi->drm_dev = drm;
+
+	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!iores)
+		return -ENXIO;
+
+	hdmi->regs = devm_ioremap_resource(dev, iores);
+	if (IS_ERR(hdmi->regs))
+		return PTR_ERR(hdmi->regs);
+
+	hdmi->pclk = devm_clk_get(hdmi->dev, "pclk");
+	if (IS_ERR(hdmi->pclk)) {
+		dev_err(hdmi->dev, "Unable to get HDMI pclk clk\n");
+		return PTR_ERR(hdmi->pclk);
+	}
+
+	ret = clk_prepare_enable(hdmi->pclk);
+	if (ret) {
+		dev_err(hdmi->dev, "Cannot enable HDMI pclk clock: %d\n", ret);
+		return ret;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	inno_hdmi_reset(hdmi);
+
+	hdmi->ddc = inno_hdmi_i2c_adapter(hdmi);
+	if (IS_ERR(hdmi->ddc)) {
+		hdmi->ddc = NULL;
+		return PTR_ERR(hdmi->ddc);
+	}
+
+	/*
+	 * When IP controller haven't configured to an accurate video
+	 * timing, then the TMDS clock source would be switched to
+	 * PCLK_HDMI, so we need to init the TMDS rate to PCLK rate,
+	 * and reconfigure the DDC clock.
+	 */
+	hdmi->tmds_rate = clk_get_rate(hdmi->pclk);
+	inno_hdmi_i2c_init(hdmi);
+
+	ret = inno_hdmi_register(drm, hdmi);
+	if (ret)
+		return ret;
+
+	dev_set_drvdata(dev, hdmi);
+
+	/* Unmute hotplug interrupt */
+	hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
+
+	ret = devm_request_threaded_irq(dev, irq, inno_hdmi_hardirq,
+					inno_hdmi_irq, IRQF_SHARED,
+					dev_name(dev), hdmi);
+
+	return ret;
+}
+
+static void inno_hdmi_unbind(struct device *dev, struct device *master,
+			     void *data)
+{
+	struct inno_hdmi *hdmi = dev_get_drvdata(dev);
+
+	hdmi->connector.funcs->destroy(&hdmi->connector);
+	hdmi->encoder.funcs->destroy(&hdmi->encoder);
+
+	clk_disable_unprepare(hdmi->pclk);
+	i2c_put_adapter(hdmi->ddc);
+}
+
+static const struct component_ops inno_hdmi_ops = {
+	.bind	= inno_hdmi_bind,
+	.unbind	= inno_hdmi_unbind,
+};
+
+static int inno_hdmi_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &inno_hdmi_ops);
+}
+
+static int inno_hdmi_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &inno_hdmi_ops);
+
+	return 0;
+}
+
+static const struct of_device_id inno_hdmi_dt_ids[] = {
+	{ .compatible = "rockchip,rk3036-inno-hdmi",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, inno_hdmi_dt_ids);
+
+static struct platform_driver inno_hdmi_driver = {
+	.probe  = inno_hdmi_probe,
+	.remove = inno_hdmi_remove,
+	.driver = {
+		.name = "innohdmi-rockchip",
+		.of_match_table = inno_hdmi_dt_ids,
+	},
+};
+
+module_platform_driver(inno_hdmi_driver);
+
+MODULE_AUTHOR("Zheng Yang <zhengyang at rock-chips.com>");
+MODULE_AUTHOR("Yakir Yang <ykk at rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip Specific INNO-HDMI Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:innohdmi-rockchip");
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.h b/drivers/gpu/drm/rockchip/inno_hdmi.h
new file mode 100644
index 0000000..4ff17ad
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.h
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ *    Zheng Yang <zhengyang at rock-chips.com>
+ *    Yakir Yang <ykk at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __INNO_HDMI_H__
+#define __INNO_HDMI_H__
+
+#define DDC_SEGMENT_ADDR		0x30
+
+enum PWR_MODE {
+	NORMAL,
+	LOWER_PWR,
+};
+
+#define HDMI_SCL_RATE			(100*1000)
+#define DDC_BUS_FREQ_L			0x4b
+#define DDC_BUS_FREQ_H			0x4c
+
+#define HDMI_SYS_CTRL			0x00
+#define m_RST_ANALOG			(1 << 6)
+#define v_RST_ANALOG			(0 << 6)
+#define v_NOT_RST_ANALOG		(1 << 6)
+#define m_RST_DIGITAL			(1 << 5)
+#define v_RST_DIGITAL			(0 << 5)
+#define v_NOT_RST_DIGITAL		(1 << 5)
+#define m_REG_CLK_INV			(1 << 4)
+#define v_REG_CLK_NOT_INV		(0 << 4)
+#define v_REG_CLK_INV			(1 << 4)
+#define m_VCLK_INV			(1 << 3)
+#define v_VCLK_NOT_INV			(0 << 3)
+#define v_VCLK_INV			(1 << 3)
+#define m_REG_CLK_SOURCE		(1 << 2)
+#define v_REG_CLK_SOURCE_TMDS		(0 << 2)
+#define v_REG_CLK_SOURCE_SYS		(1 << 2)
+#define m_POWER				(1 << 1)
+#define v_PWR_ON			(0 << 1)
+#define v_PWR_OFF			(1 << 1)
+#define m_INT_POL			(1 << 0)
+#define v_INT_POL_HIGH			1
+#define v_INT_POL_LOW			0
+
+#define HDMI_VIDEO_CONTRL1		0x01
+#define m_VIDEO_INPUT_FORMAT		(7 << 1)
+#define m_DE_SOURCE			(1 << 0)
+#define v_VIDEO_INPUT_FORMAT(n)		(n << 1)
+#define v_DE_EXTERNAL			1
+#define v_DE_INTERNAL			0
+enum {
+	VIDEO_INPUT_SDR_RGB444 = 0,
+	VIDEO_INPUT_DDR_RGB444 = 5,
+	VIDEO_INPUT_DDR_YCBCR422 = 6
+};
+
+#define HDMI_VIDEO_CONTRL2		0x02
+#define m_VIDEO_OUTPUT_COLOR		(3 << 6)
+#define m_VIDEO_INPUT_BITS		(3 << 4)
+#define m_VIDEO_INPUT_CSP		(1 << 0)
+#define v_VIDEO_OUTPUT_COLOR(n)		(((n) & 0x3) << 6)
+#define v_VIDEO_INPUT_BITS(n)		(n << 4)
+#define v_VIDEO_INPUT_CSP(n)		(n << 0)
+enum {
+	VIDEO_INPUT_12BITS = 0,
+	VIDEO_INPUT_10BITS = 1,
+	VIDEO_INPUT_REVERT = 2,
+	VIDEO_INPUT_8BITS = 3,
+};
+
+#define HDMI_VIDEO_CONTRL		0x03
+#define m_VIDEO_AUTO_CSC		(1 << 7)
+#define v_VIDEO_AUTO_CSC(n)		(n << 7)
+#define m_VIDEO_C0_C2_SWAP		(1 << 0)
+#define v_VIDEO_C0_C2_SWAP(n)		(n << 0)
+enum {
+	C0_C2_CHANGE_ENABLE = 0,
+	C0_C2_CHANGE_DISABLE = 1,
+	AUTO_CSC_DISABLE = 0,
+	AUTO_CSC_ENABLE = 1,
+};
+
+#define HDMI_VIDEO_CONTRL3		0x04
+#define m_COLOR_DEPTH_NOT_INDICATED	(1 << 4)
+#define m_SOF				(1 << 3)
+#define m_COLOR_RANGE			(1 << 2)
+#define m_CSC				(1 << 0)
+#define v_COLOR_DEPTH_NOT_INDICATED(n)	((n) << 4)
+#define v_SOF_ENABLE			(0 << 3)
+#define v_SOF_DISABLE			(1 << 3)
+#define v_COLOR_RANGE_FULL		(1 << 2)
+#define v_COLOR_RANGE_LIMITED		(0 << 2)
+#define v_CSC_ENABLE			1
+#define v_CSC_DISABLE			0
+
+#define HDMI_AV_MUTE			0x05
+#define m_AVMUTE_CLEAR			(1 << 7)
+#define m_AVMUTE_ENABLE			(1 << 6)
+#define m_AUDIO_MUTE			(1 << 1)
+#define m_VIDEO_BLACK			(1 << 0)
+#define v_AVMUTE_CLEAR(n)		(n << 7)
+#define v_AVMUTE_ENABLE(n)		(n << 6)
+#define v_AUDIO_MUTE(n)			(n << 1)
+#define v_VIDEO_MUTE(n)			(n << 0)
+
+#define HDMI_VIDEO_TIMING_CTL		0x08
+#define v_HSYNC_POLARITY(n)		(n << 3)
+#define v_VSYNC_POLARITY(n)		(n << 2)
+#define v_INETLACE(n)			(n << 1)
+#define v_EXTERANL_VIDEO(n)		(n << 0)
+
+#define HDMI_VIDEO_EXT_HTOTAL_L		0x09
+#define HDMI_VIDEO_EXT_HTOTAL_H		0x0a
+#define HDMI_VIDEO_EXT_HBLANK_L		0x0b
+#define HDMI_VIDEO_EXT_HBLANK_H		0x0c
+#define HDMI_VIDEO_EXT_HDELAY_L		0x0d
+#define HDMI_VIDEO_EXT_HDELAY_H		0x0e
+#define HDMI_VIDEO_EXT_HDURATION_L	0x0f
+#define HDMI_VIDEO_EXT_HDURATION_H	0x10
+#define HDMI_VIDEO_EXT_VTOTAL_L		0x11
+#define HDMI_VIDEO_EXT_VTOTAL_H		0x12
+#define HDMI_VIDEO_EXT_VBLANK		0x13
+#define HDMI_VIDEO_EXT_VDELAY		0x14
+#define HDMI_VIDEO_EXT_VDURATION	0x15
+
+#define HDMI_VIDEO_CSC_COEF		0x18
+
+#define HDMI_AUDIO_CTRL1		0x35
+enum {
+	CTS_SOURCE_INTERNAL = 0,
+	CTS_SOURCE_EXTERNAL = 1,
+};
+#define v_CTS_SOURCE(n)			(n << 7)
+
+enum {
+	DOWNSAMPLE_DISABLE = 0,
+	DOWNSAMPLE_1_2 = 1,
+	DOWNSAMPLE_1_4 = 2,
+};
+#define v_DOWN_SAMPLE(n)		(n << 5)
+
+enum {
+	AUDIO_SOURCE_IIS = 0,
+	AUDIO_SOURCE_SPDIF = 1,
+};
+#define v_AUDIO_SOURCE(n)		(n << 3)
+
+#define v_MCLK_ENABLE(n)		(n << 2)
+enum {
+	MCLK_128FS = 0,
+	MCLK_256FS = 1,
+	MCLK_384FS = 2,
+	MCLK_512FS = 3,
+};
+#define v_MCLK_RATIO(n)			(n)
+
+#define AUDIO_SAMPLE_RATE		0x37
+enum {
+	AUDIO_32K = 0x3,
+	AUDIO_441K = 0x0,
+	AUDIO_48K = 0x2,
+	AUDIO_882K = 0x8,
+	AUDIO_96K = 0xa,
+	AUDIO_1764K = 0xc,
+	AUDIO_192K = 0xe,
+};
+
+#define AUDIO_I2S_MODE			0x38
+enum {
+	I2S_CHANNEL_1_2 = 1,
+	I2S_CHANNEL_3_4 = 3,
+	I2S_CHANNEL_5_6 = 7,
+	I2S_CHANNEL_7_8 = 0xf
+};
+#define v_I2S_CHANNEL(n)		((n) << 2)
+enum {
+	I2S_STANDARD = 0,
+	I2S_LEFT_JUSTIFIED = 1,
+	I2S_RIGHT_JUSTIFIED = 2,
+};
+#define v_I2S_MODE(n)			(n)
+
+#define AUDIO_I2S_MAP			0x39
+#define AUDIO_I2S_SWAPS_SPDIF		0x3a
+#define v_SPIDF_FREQ(n)			(n)
+
+#define N_32K				0x1000
+#define N_441K				0x1880
+#define N_882K				0x3100
+#define N_1764K				0x6200
+#define N_48K				0x1800
+#define N_96K				0x3000
+#define N_192K				0x6000
+
+#define HDMI_AUDIO_CHANNEL_STATUS	0x3e
+#define m_AUDIO_STATUS_NLPCM		(1 << 7)
+#define m_AUDIO_STATUS_USE		(1 << 6)
+#define m_AUDIO_STATUS_COPYRIGHT	(1 << 5)
+#define m_AUDIO_STATUS_ADDITION		(3 << 2)
+#define m_AUDIO_STATUS_CLK_ACCURACY	(2 << 0)
+#define v_AUDIO_STATUS_NLPCM(n)		((n & 1) << 7)
+#define AUDIO_N_H			0x3f
+#define AUDIO_N_M			0x40
+#define AUDIO_N_L			0x41
+
+#define HDMI_AUDIO_CTS_H		0x45
+#define HDMI_AUDIO_CTS_M		0x46
+#define HDMI_AUDIO_CTS_L		0x47
+
+#define HDMI_DDC_CLK_L			0x4b
+#define HDMI_DDC_CLK_H			0x4c
+
+#define HDMI_EDID_SEGMENT_POINTER	0x4d
+#define HDMI_EDID_WORD_ADDR		0x4e
+#define HDMI_EDID_FIFO_OFFSET		0x4f
+#define HDMI_EDID_FIFO_ADDR		0x50
+
+#define HDMI_PACKET_SEND_MANUAL		0x9c
+#define HDMI_PACKET_SEND_AUTO		0x9d
+#define m_PACKET_GCP_EN			(1 << 7)
+#define m_PACKET_MSI_EN			(1 << 6)
+#define m_PACKET_SDI_EN			(1 << 5)
+#define m_PACKET_VSI_EN			(1 << 4)
+#define v_PACKET_GCP_EN(n)		((n & 1) << 7)
+#define v_PACKET_MSI_EN(n)		((n & 1) << 6)
+#define v_PACKET_SDI_EN(n)		((n & 1) << 5)
+#define v_PACKET_VSI_EN(n)		((n & 1) << 4)
+
+#define HDMI_CONTROL_PACKET_BUF_INDEX	0x9f
+enum {
+	INFOFRAME_VSI = 0x05,
+	INFOFRAME_AVI = 0x06,
+	INFOFRAME_AAI = 0x08,
+};
+
+#define HDMI_CONTROL_PACKET_ADDR	0xa0
+#define HDMI_SIZE_VSI_INFOFRAME		0x0A
+#define HDMI_SIZE_AVI_INFOFRAME		0x11
+#define HDMI_SIZE_AUDIO_INFOFRAME	0x0F
+enum {
+	AVI_COLOR_MODE_RGB = 0,
+	AVI_COLOR_MODE_YCBCR422 = 1,
+	AVI_COLOR_MODE_YCBCR444 = 2,
+	AVI_COLORIMETRY_NO_DATA = 0,
+
+	AVI_COLORIMETRY_SMPTE_170M = 1,
+	AVI_COLORIMETRY_ITU709 = 2,
+	AVI_COLORIMETRY_EXTENDED = 3,
+
+	AVI_CODED_FRAME_ASPECT_NO_DATA = 0,
+	AVI_CODED_FRAME_ASPECT_4_3 = 1,
+	AVI_CODED_FRAME_ASPECT_16_9 = 2,
+
+	ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME = 0x08,
+	ACTIVE_ASPECT_RATE_4_3 = 0x09,
+	ACTIVE_ASPECT_RATE_16_9 = 0x0A,
+	ACTIVE_ASPECT_RATE_14_9 = 0x0B,
+};
+
+#define HDMI_HDCP_CTRL			0x52
+#define m_HDMI_DVI			(1 << 1)
+#define v_HDMI_DVI(n)			(n << 1)
+
+#define HDMI_INTERRUPT_MASK1		0xc0
+#define HDMI_INTERRUPT_STATUS1		0xc1
+#define	m_INT_ACTIVE_VSYNC		(1 << 5)
+#define m_INT_EDID_READY		(1 << 2)
+
+#define HDMI_INTERRUPT_MASK2		0xc2
+#define HDMI_INTERRUPT_STATUS2		0xc3
+#define m_INT_HDCP_ERR			(1 << 7)
+#define m_INT_BKSV_FLAG			(1 << 6)
+#define m_INT_HDCP_OK			(1 << 4)
+
+#define HDMI_STATUS			0xc8
+#define m_HOTPLUG			(1 << 7)
+#define m_MASK_INT_HOTPLUG		(1 << 5)
+#define m_INT_HOTPLUG			(1 << 1)
+#define v_MASK_INT_HOTPLUG(n)		((n & 0x1) << 5)
+
+#define HDMI_COLORBAR                   0xc9
+
+#define HDMI_PHY_SYNC			0xce
+#define HDMI_PHY_SYS_CTL		0xe0
+#define m_TMDS_CLK_SOURCE		(1 << 5)
+#define v_TMDS_FROM_PLL			(0 << 5)
+#define v_TMDS_FROM_GEN			(1 << 5)
+#define m_PHASE_CLK			(1 << 4)
+#define v_DEFAULT_PHASE			(0 << 4)
+#define v_SYNC_PHASE			(1 << 4)
+#define m_TMDS_CURRENT_PWR		(1 << 3)
+#define v_TURN_ON_CURRENT		(0 << 3)
+#define v_CAT_OFF_CURRENT		(1 << 3)
+#define m_BANDGAP_PWR			(1 << 2)
+#define v_BANDGAP_PWR_UP		(0 << 2)
+#define v_BANDGAP_PWR_DOWN		(1 << 2)
+#define m_PLL_PWR			(1 << 1)
+#define v_PLL_PWR_UP			(0 << 1)
+#define v_PLL_PWR_DOWN			(1 << 1)
+#define m_TMDS_CHG_PWR			(1 << 0)
+#define v_TMDS_CHG_PWR_UP		(0 << 0)
+#define v_TMDS_CHG_PWR_DOWN		(1 << 0)
+
+#define HDMI_PHY_CHG_PWR		0xe1
+#define v_CLK_CHG_PWR(n)		((n & 1) << 3)
+#define v_DATA_CHG_PWR(n)		((n & 7) << 0)
+
+#define HDMI_PHY_DRIVER			0xe2
+#define v_CLK_MAIN_DRIVER(n)		(n << 4)
+#define v_DATA_MAIN_DRIVER(n)		(n << 0)
+
+#define HDMI_PHY_PRE_EMPHASIS		0xe3
+#define v_PRE_EMPHASIS(n)		((n & 7) << 4)
+#define v_CLK_PRE_DRIVER(n)		((n & 3) << 2)
+#define v_DATA_PRE_DRIVER(n)		((n & 3) << 0)
+
+#define HDMI_PHY_FEEDBACK_DIV_RATIO_LOW		0xe7
+#define v_FEEDBACK_DIV_LOW(n)			(n & 0xff)
+#define HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH	0xe8
+#define v_FEEDBACK_DIV_HIGH(n)			(n & 1)
+
+#define HDMI_PHY_PRE_DIV_RATIO		0xed
+#define v_PRE_DIV_RATIO(n)		(n & 0x1f)
+
+#define HDMI_CEC_CTRL			0xd0
+#define m_ADJUST_FOR_HISENSE		(1 << 6)
+#define m_REJECT_RX_BROADCAST		(1 << 5)
+#define m_BUSFREETIME_ENABLE		(1 << 2)
+#define m_REJECT_RX			(1 << 1)
+#define m_START_TX			(1 << 0)
+
+#define HDMI_CEC_DATA			0xd1
+#define HDMI_CEC_TX_OFFSET		0xd2
+#define HDMI_CEC_RX_OFFSET		0xd3
+#define HDMI_CEC_CLK_H			0xd4
+#define HDMI_CEC_CLK_L			0xd5
+#define HDMI_CEC_TX_LENGTH		0xd6
+#define HDMI_CEC_RX_LENGTH		0xd7
+#define HDMI_CEC_TX_INT_MASK		0xd8
+#define m_TX_DONE			(1 << 3)
+#define m_TX_NOACK			(1 << 2)
+#define m_TX_BROADCAST_REJ		(1 << 1)
+#define m_TX_BUSNOTFREE			(1 << 0)
+
+#define HDMI_CEC_RX_INT_MASK		0xd9
+#define m_RX_LA_ERR			(1 << 4)
+#define m_RX_GLITCH			(1 << 3)
+#define m_RX_DONE			(1 << 0)
+
+#define HDMI_CEC_TX_INT			0xda
+#define HDMI_CEC_RX_INT			0xdb
+#define HDMI_CEC_BUSFREETIME_L		0xdc
+#define HDMI_CEC_BUSFREETIME_H		0xdd
+#define HDMI_CEC_LOGICADDR		0xde
+
+#endif /* __INNO_HDMI_H__ */
-- 
2.1.2




From p.zabel at pengutronix.de  Thu Jan  7 02:04:12 2016
From: p.zabel at pengutronix.de (Philipp Zabel)
Date: Thu, 07 Jan 2016 11:04:12 +0100
Subject: [RFC PATCH v2 3/4] drm: rockchip: hdmi: add RK3229 HDMI support
In-Reply-To: <1452157373-18783-1-git-send-email-ykk@rock-chips.com>
References: <1452156811-18150-1-git-send-email-ykk@rock-chips.com>
 <1452157373-18783-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1452161052.4776.7.camel@pengutronix.de>

Am Donnerstag, den 07.01.2016, 17:02 +0800 schrieb Yakir Yang:
> RK3229 integrate an DesignedWare HDMI2.0 controller and an INNO HDMI2.0 phy,
> the max output resolution is 4K.
> 
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>

It sounds like the INNO HDMI2.0 phy is not necessarily specific to
RK3229 but might also appear in other SoCs? If so, I think this should
be implemented in a separate phy driver and be used by dw_hdmi-rockchip.

regards
Philipp

> ---
> Changes in v2:
> - Split some dw-hdmi driver changes into separate patches [01/04] & [02/04]
> 
>  drivers/gpu/drm/bridge/dw-hdmi.c            |  27 +-
>  drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 367 ++++++++++++++++++++++++++--
>  drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h | 137 +++++++++++
>  include/drm/bridge/dw_hdmi.h                |   3 +
>  4 files changed, 507 insertions(+), 27 deletions(-)
>  create mode 100644 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h
> 
> diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
> index 5ad72ec..5e03d83 100644
> --- a/drivers/gpu/drm/bridge/dw-hdmi.c
> +++ b/drivers/gpu/drm/bridge/dw-hdmi.c
> @@ -735,10 +735,12 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
>  {
>  	unsigned res_idx;
>  	u8 val, msec;
> +	int ret;
>  	const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
>  	const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg;
>  	const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr;
>  	const struct dw_hdmi_phy_config *phy_config = pdata->phy_config;
> +	int mpixelclock = hdmi->hdmi_data.video_mode.mpixelclock;
>  
>  	if (prep)
>  		return -EINVAL;
> @@ -758,27 +760,38 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
>  		return -EINVAL;
>  	}
>  
> +	if (hdmi->plat_data->extphy_config) {
> +		/* gen2 tx power off */
> +		dw_hdmi_phy_gen2_txpwron(hdmi, 0);
> +		dw_hdmi_phy_gen2_pddq(hdmi, 1);
> +
> +		ret = hdmi->plat_data->extphy_config(hdmi->plat_data, res_idx,
> +						     mpixelclock);
> +		/* gen2 tx power on */
> +		dw_hdmi_phy_gen2_txpwron(hdmi, 1);
> +		dw_hdmi_phy_gen2_pddq(hdmi, 0);
> +
> +		return ret;
> +	}
> +
>  	/* PLL/MPLL Cfg - always match on final entry */
>  	for (; mpll_config->mpixelclock != ~0UL; mpll_config++)
> -		if (hdmi->hdmi_data.video_mode.mpixelclock <=
> -		    mpll_config->mpixelclock)
> +		if (mpixelclock <= mpll_config->mpixelclock)
>  			break;
>  
>  	for (; curr_ctrl->mpixelclock != ~0UL; curr_ctrl++)
> -		if (hdmi->hdmi_data.video_mode.mpixelclock <=
> -		    curr_ctrl->mpixelclock)
> +		if (mpixelclock <= curr_ctrl->mpixelclock)
>  			break;
>  
>  	for (; phy_config->mpixelclock != ~0UL; phy_config++)
> -		if (hdmi->hdmi_data.video_mode.mpixelclock <=
> -		    phy_config->mpixelclock)
> +		if (mpixelclock <= phy_config->mpixelclock)
>  			break;
>  
>  	if (mpll_config->mpixelclock == ~0UL ||
>  	    curr_ctrl->mpixelclock == ~0UL ||
>  	    phy_config->mpixelclock == ~0UL) {
>  		dev_err(hdmi->dev, "Pixel clock %d - unsupported by HDMI\n",
> -			hdmi->hdmi_data.video_mode.mpixelclock);
> +			mpixelclock);
>  		return -EINVAL;
>  	}
>  
> diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
> index 8164823..24fffaa 100644
> --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
> +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
> @@ -7,6 +7,7 @@
>   * (at your option) any later version.
>   */
>  
> +#include <linux/clk.h>
>  #include <linux/module.h>
>  #include <linux/platform_device.h>
>  #include <linux/mfd/syscon.h>
> @@ -21,18 +22,134 @@
>  #include "rockchip_drm_drv.h"
>  #include "rockchip_drm_vop.h"
>  
> -#define GRF_SOC_CON6                    0x025c
> -#define HDMI_SEL_VOP_LIT                (1 << 4)
> +#include "dw_hdmi-rockchip.h"
>  
>  struct rockchip_hdmi {
>  	struct device *dev;
>  	struct regmap *regmap;
>  	struct drm_encoder encoder;
>  	struct dw_hdmi_plat_data plat_data;
> +
> +	void __iomem *extphy_regbase;
> +	struct clk *extphy_pclk;
>  };
>  
>  #define to_rockchip_hdmi(x)	container_of(x, struct rockchip_hdmi, x)
>  
> +static const struct extphy_config_tab rockchip_extphy_cfg[] = {
> +	{ .mpixelclock = 165000000,
> +	  .pre_emphasis = 0, .slopeboost = 0, .clk_level = 4,
> +	  .data0_level = 4, 4, 4,
> +	},
> +
> +	{ .mpixelclock = 225000000,
> +	  .pre_emphasis = 0, .slopeboost = 0, .clk_level = 6,
> +	  .data0_level = 6, 6, 6,
> +	},
> +
> +	{ .mpixelclock = 340000000,
> +	  .pre_emphasis = 1, .slopeboost = 0, .clk_level = 6,
> +	  .data0_level = 10, 10, 10,
> +	},
> +
> +	{ .mpixelclock = 594000000,
> +	  .pre_emphasis = 1, .slopeboost = 0, .clk_level = 7,
> +	  .data0_level = 10, 10, 10,
> +	},
> +
> +	{ .mpixelclock = ~0UL},
> +};
> +
> +static const struct extphy_pll_config_tab rockchip_extphy_pll_cfg[] = {
> +	{
> +		.mpixelclock = 27000000, .param = {
> +			{ .pll_nd = 1, .pll_nf = 45,
> +			  .tmsd_divider_a = 3, 1, 1,
> +			  .pclk_divider_a = 1, 3, 3, 4,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
> +			},
> +			{ .pll_nd = 1, .pll_nf = 45,
> +			  .tmsd_divider_a = 0, 3, 3,
> +			  .pclk_divider_a = 1, 3, 3, 4,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
> +			},
> +		},
> +	}, {
> +		.mpixelclock = 59400000, .param = {
> +			{ .pll_nd = 2, .pll_nf = 99,
> +			  .tmsd_divider_a = 3, 1, 1,
> +			  .pclk_divider_a = 1, 3, 2, 2,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
> +			},
> +			{ .pll_nd = 2, .pll_nf = 99,
> +			  .tmsd_divider_a = 1, 1, 1,
> +			  .pclk_divider_a = 1, 3, 2, 2,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
> +			},
> +		},
> +	}, {
> +		.mpixelclock = 74250000, .param = {
> +			{ .pll_nd = 2, .pll_nf = 99,
> +			  .tmsd_divider_a = 1, 1, 1,
> +			  .pclk_divider_a = 1, 2, 2, 2,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
> +			},
> +			{ .pll_nd = 4, .pll_nf = 495,
> +			  .tmsd_divider_a = 1, 2, 2,
> +			  .pclk_divider_a = 1, 3, 3, 4,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 2, .ppll_nf = 40, .ppll_no = 4,
> +			},
> +		},
> +	}, {
> +		.mpixelclock = 148500000, .param = {
> +			{ .pll_nd = 2, .pll_nf = 99,
> +			  .tmsd_divider_a = 1, 0, 0,
> +			  .pclk_divider_a = 1, 2, 1, 1,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 2, .ppll_nf = 40, .ppll_no = 4,
> +			},
> +			{ .pll_nd = 4, .pll_nf = 495,
> +			  .tmsd_divider_a = 0, 2, 2,
> +			  .pclk_divider_a = 1, 3, 2, 2,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 4, .ppll_nf = 40, .ppll_no = 2,
> +			},
> +		},
> +	}, {
> +		.mpixelclock = 297000000, .param = {
> +			{ .pll_nd = 2, .pll_nf = 99,
> +			  .tmsd_divider_a = 0, 0, 0,
> +			  .pclk_divider_a = 1, 0, 1, 1,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 4, .ppll_nf = 40, .ppll_no = 2,
> +			},
> +			{ .pll_nd = 4, .pll_nf = 495,
> +			  .tmsd_divider_a = 1, 2, 0,
> +			  .pclk_divider_a = 1, 3, 1, 1,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 8, .ppll_nf = 40, .ppll_no = 1,
> +			},
> +		},
> +	}, {
> +		.mpixelclock = 594000000, .param = {
> +			{ .pll_nd = 1, .pll_nf = 99,
> +			  .tmsd_divider_a = 0, 2, 0,
> +			  .pclk_divider_a = 1, 0, 1, 1,
> +			  .vco_div_5 = 0,
> +			  .ppll_nd = 8, .ppll_nf = 40, .ppll_no = 1,
> +			},
> +		}
> +	}, {
> +		.mpixelclock = ~0UL,
> +	}
> +};
> +
>  static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
>  	{
>  		27000000, {
> @@ -142,9 +259,164 @@ static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
>  	{ ~0UL,	     0x0000, 0x0000, 0x0000}
>  };
>  
> +static inline void hdmi_extphy_write(struct rockchip_hdmi *hdmi,
> +				     unsigned short data, unsigned char addr)
> +{
> +	writel_relaxed(data, hdmi->extphy_regbase + (addr) * 0x04);
> +}
> +
> +static inline unsigned int hdmi_extphy_read(struct rockchip_hdmi *hdmi,
> +					    unsigned char addr)
> +{
> +	return readl_relaxed(hdmi->extphy_regbase + (addr) * 0x04);
> +}
> +
> +static int rockchip_extphy_config(const struct dw_hdmi_plat_data *plat_data,
> +				  int res, int pixelclock)
> +{
> +	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(plat_data);
> +	const struct extphy_pll_config_tab *mpll = rockchip_extphy_pll_cfg;
> +	const struct extphy_config_tab *ctrl = rockchip_extphy_cfg;
> +	const struct extphy_pll_config_param *param;
> +	unsigned long timeout;
> +	int i, stat;
> +
> +	if (res >= DW_HDMI_RES_MAX) {
> +		dev_err(hdmi->dev, "Extphy can't support res %d\n", res);
> +		return -EINVAL;
> +	}
> +
> +	/* Find out the extphy MPLL configure parameters */
> +	for (i = 0; mpll[i].mpixelclock != ~0UL; i++)
> +		if (pixelclock == mpll[i].mpixelclock)
> +			break;
> +	if (mpll[i].mpixelclock == ~0UL) {
> +		dev_err(hdmi->dev, "Extphy can't support %dHz\n", pixelclock);
> +		return -EINVAL;
> +	}
> +	param = &mpll[i].param[res];
> +
> +	regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2,
> +		     RK3229_PLL_POWER_DOWN | RK3229_PLL_PDATA_DEN);
> +
> +	/*
> +	 * Configure external HDMI PHY PLL registers.
> +	 */
> +	stat = ((param->pll_nf >> 1) & EXT_PHY_PLL_FB_BIT8_MASK) |
> +	       ((param->vco_div_5 & 1) << 5) |
> +	       (param->pll_nd & EXT_PHY_PLL_PRE_DIVIDER_MASK);
> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_PLL_PRE_DIVIDER);
> +
> +	hdmi_extphy_write(hdmi, param->pll_nf, EXT_PHY_PLL_FB_DIVIDER);
> +
> +	stat = (param->pclk_divider_a & EXT_PHY_PCLK_DIVIDERA_MASK) |
> +	       ((param->pclk_divider_b & 3) << 5);
> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_PCLK_DIVIDER1);
> +
> +	stat = (param->pclk_divider_d & EXT_PHY_PCLK_DIVIDERD_MASK) |
> +	       ((param->pclk_divider_c & 3) << 5);
> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_PCLK_DIVIDER2);
> +
> +	stat = ((param->tmsd_divider_c & 3) << 4) |
> +	       ((param->tmsd_divider_a & 3) << 2) |
> +	       (param->tmsd_divider_b & 3);
> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_TMDSCLK_DIVIDER);
> +
> +	hdmi_extphy_write(hdmi, param->ppll_nf, EXT_PHY_PPLL_FB_DIVIDER);
> +
> +	if (param->ppll_no == 1) {
> +		hdmi_extphy_write(hdmi, 0, EXT_PHY_PPLL_POST_DIVIDER);
> +
> +		stat = 0x20 | param->ppll_nd;
> +		hdmi_extphy_write(hdmi, stat, EXT_PHY_PPLL_PRE_DIVIDER);
> +	} else {
> +		stat = ((param->ppll_no / 2) - 1) << 4;
> +		hdmi_extphy_write(hdmi, stat, EXT_PHY_PPLL_POST_DIVIDER);
> +
> +		stat = 0xe0 | param->ppll_nd;
> +		hdmi_extphy_write(hdmi, stat, EXT_PHY_PPLL_PRE_DIVIDER);
> +	}
> +
> +
> +	/* Find out the external HDMI PHY driver configure parameters */
> +	for (i = 0; ctrl[i].mpixelclock != ~0UL; i++)
> +		if (pixelclock <= ctrl[i].mpixelclock)
> +			break;
> +	if (ctrl[i].mpixelclock == ~0UL) {
> +		dev_err(hdmi->dev, "Extphy can't support %dHz\n", pixelclock);
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * Configure the external HDMI PHY driver registers.
> +	 */
> +	if (ctrl[i].slopeboost) {
> +		hdmi_extphy_write(hdmi, 0xff, EXT_PHY_SIGNAL_CTRL);
> +
> +		stat = (ctrl[i].slopeboost - 1) & 3;
> +		stat = (stat << 6) | (stat << 4) | (stat << 2) | stat;
> +		hdmi_extphy_write(hdmi, stat, EXT_PHY_SLOPEBOOST);
> +	} else
> +		hdmi_extphy_write(hdmi, 0x0f, EXT_PHY_SIGNAL_CTRL);
> +
> +	stat = ctrl[i].pre_emphasis & 3;
> +	stat = (stat << 4) | (stat << 2) | stat;
> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_PREEMPHASIS);
> +
> +	stat = ((ctrl[i].clk_level & 0xf) << 4) | (ctrl[i].data2_level & 0xf);
> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_LEVEL1);
> +
> +	stat = ((ctrl[i].data1_level & 0xf) << 4) | (ctrl[i].data0_level & 0xf);
> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_LEVEL2);
> +
> +	hdmi_extphy_write(hdmi, 0x22, 0xf3);
> +
> +	stat = clk_get_rate(hdmi->extphy_pclk) / 100000;
> +	hdmi_extphy_write(hdmi, ((stat >> 8) & 0xff) | 0x80, EXT_PHY_TERM_CAL);
> +	hdmi_extphy_write(hdmi,  stat & 0xff, EXT_PHY_TERM_CAL_DIV_L);
> +
> +	if (pixelclock > 340000000)
> +		stat = EXT_PHY_AUTO_R100_OHMS;
> +	else if (pixelclock > 200000000)
> +		stat = EXT_PHY_AUTO_R50_OHMS;
> +	else
> +		stat = EXT_PHY_AUTO_ROPEN_CIRCUIT;
> +	hdmi_extphy_write(hdmi, stat | 0x20, EXT_PHY_TERM_RESIS_AUTO);
> +	hdmi_extphy_write(hdmi, (stat >> 8) & 0xff, EXT_PHY_TERM_CAL);
> +
> +	stat = (pixelclock > 200000000) ? 0 : 0x11;
> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_PLL_BW);
> +	hdmi_extphy_write(hdmi, 0x27, EXT_PHY_PPLL_BW);
> +
> +	regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2, RK3229_PLL_POWER_UP);
> +
> +	/* Detect whether PLL is lock or not */
> +	timeout = jiffies + msecs_to_jiffies(100);
> +	while (!time_after(jiffies, timeout)) {
> +		usleep_range(1000, 2000);
> +		stat = hdmi_extphy_read(hdmi, EXT_PHY_PPLL_POST_DIVIDER);
> +		if (stat & EXT_PHY_PPLL_LOCK_STATUS_MASK)
> +			break;
> +	}
> +
> +	regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2, RK3229_PLL_PDATA_EN);
> +
> +	if ((stat & EXT_PHY_PPLL_LOCK_STATUS_MASK) == 0) {
> +		dev_err(hdmi->dev, "EXT PHY PLL not locked\n");
> +		return -EBUSY;
> +	}
> +
> +	return 0;
> +}
> +
>  static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
>  {
>  	struct device_node *np = hdmi->dev->of_node;
> +	struct platform_device *pdev;
> +	struct resource *iores;
> +	int ret;
> +
> +	pdev = container_of(hdmi->dev, struct platform_device, dev);
>  
>  	hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
>  	if (IS_ERR(hdmi->regmap)) {
> @@ -152,6 +424,37 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
>  		return PTR_ERR(hdmi->regmap);
>  	}
>  
> +	if (hdmi->plat_data.dev_type == RK3229_HDMI) {
> +		iores = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +		if (!iores)
> +			return -ENXIO;
> +
> +		hdmi->extphy_regbase = devm_ioremap_resource(hdmi->dev, iores);
> +		if (IS_ERR(hdmi->extphy_regbase)) {
> +			dev_err(hdmi->dev, "failed to map extphy regbase\n");
> +			return PTR_ERR(hdmi->extphy_regbase);
> +		}
> +
> +		hdmi->extphy_pclk = devm_clk_get(hdmi->dev, "extphy");
> +		if (IS_ERR(hdmi->extphy_pclk)) {
> +			dev_err(hdmi->dev, "failed to get extphy clock\n");
> +			return PTR_ERR(hdmi->extphy_pclk);
> +		}
> +
> +		ret = clk_prepare_enable(hdmi->extphy_pclk);
> +		if (ret) {
> +			dev_err(hdmi->dev, "failed to enable extphy clk: %d\n",
> +				ret);
> +			return ret;
> +		}
> +
> +		regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON6,
> +			     RK3229_IO_3V_DOMAIN);
> +
> +		regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2,
> +			     RK3229_DDC_MASK_EN);
> +	}
> +
>  	return 0;
>  }
>  
> @@ -159,17 +462,23 @@ static enum drm_mode_status
>  dw_hdmi_rockchip_mode_valid(const struct dw_hdmi_plat_data *plat_data,
>  			    struct drm_display_mode *mode)
>  {
> -	const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg;
>  	int pclk = mode->clock * 1000;
>  	bool valid = false;
>  	int i;
>  
> -	for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) {
> -		if (pclk == mpll_cfg[i].mpixelclock) {
> -			valid = true;
> -			break;
> -		}
> -	}
> +	if (plat_data->dev_type == RK3288_HDMI)
> +		for (i = 0; rockchip_mpll_cfg[i].mpixelclock != ~0UL; i++)
> +			if (pclk == rockchip_mpll_cfg[i].mpixelclock) {
> +				valid = true;
> +				break;
> +			}
> +
> +	if (plat_data->dev_type == RK3229_HDMI)
> +		for (i = 0; rockchip_extphy_pll_cfg[i].mpixelclock != ~0UL; i++)
> +			if (pclk == rockchip_extphy_pll_cfg[i].mpixelclock) {
> +				valid = true;
> +				break;
> +			}
>  
>  	return (valid) ? MODE_OK : MODE_BAD;
>  }
> @@ -199,21 +508,30 @@ static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
>  static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
>  {
>  	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
> +	int out_mode = ROCKCHIP_OUT_MODE_AAAA;
>  	u32 val;
>  	int mux;
>  
> +	if (hdmi->plat_data.dev_type == RK3229_HDMI)
> +		out_mode = ROCKCHIP_OUT_MODE_P888;
> +
>  	rockchip_drm_crtc_mode_config(encoder->crtc, DRM_MODE_CONNECTOR_HDMIA,
> -				      ROCKCHIP_OUT_MODE_AAAA);
> +				      out_mode);
>  
> -	mux = rockchip_drm_encoder_get_mux_id(hdmi->dev->of_node, encoder);
> -	if (mux)
> -		val = HDMI_SEL_VOP_LIT | (HDMI_SEL_VOP_LIT << 16);
> -	else
> -		val = HDMI_SEL_VOP_LIT << 16;
> +	if (hdmi->plat_data.dev_type == RK3288_HDMI) {
> +		mux = rockchip_drm_encoder_get_mux_id(hdmi->dev->of_node,
> +						      encoder);
> +		if (mux)
> +			val = RK3288_HDMI_SEL_VOP_LIT |
> +			      (RK3288_HDMI_SEL_VOP_LIT << 16);
> +		else
> +			val = RK3288_HDMI_SEL_VOP_LIT << 16;
>  
> -	regmap_write(hdmi->regmap, GRF_SOC_CON6, val);
> -	dev_dbg(hdmi->dev, "vop %s output to hdmi\n",
> -		(mux) ? "LIT" : "BIG");
> +		regmap_write(hdmi->regmap, RK3288_GRF_SOC_CON6, val);
> +
> +		dev_dbg(hdmi->dev, "vop %s output to hdmi\n",
> +			(mux) ? "LIT" : "BIG");
> +	}
>  }
>  
>  static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = {
> @@ -223,7 +541,7 @@ static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_fun
>  	.disable    = dw_hdmi_rockchip_encoder_disable,
>  };
>  
> -static const struct dw_hdmi_plat_data rockchip_hdmi_drv_data = {
> +static const struct dw_hdmi_plat_data rk3288_hdmi_drv_data = {
>  	.mode_valid = dw_hdmi_rockchip_mode_valid,
>  	.mpll_cfg   = rockchip_mpll_cfg,
>  	.cur_ctr    = rockchip_cur_ctr,
> @@ -231,9 +549,18 @@ static const struct dw_hdmi_plat_data rockchip_hdmi_drv_data = {
>  	.dev_type   = RK3288_HDMI,
>  };
>  
> +static const struct dw_hdmi_plat_data rk3229_hdmi_drv_data = {
> +	.mode_valid = dw_hdmi_rockchip_mode_valid,
> +	.extphy_config = rockchip_extphy_config,
> +	.dev_type   = RK3229_HDMI,
> +};
> +
>  static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
>  	{ .compatible = "rockchip,rk3288-dw-hdmi",
> -	  .data = &rockchip_hdmi_drv_data
> +	  .data = &rk3288_hdmi_drv_data
> +	},
> +	{ .compatible = "rockchip,rk3229-dw-hdmi",
> +	  .data = &rk3229_hdmi_drv_data
>  	},
>  	{},
>  };
> diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h
> new file mode 100644
> index 0000000..f7ec733
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h
> @@ -0,0 +1,137 @@
> +#ifndef __DW_HDMI_ROCKCHIP__
> +#define __DW_HDMI_ROCKCHIP__
> +
> +struct extphy_config_tab {
> +	u32 mpixelclock;
> +	int pre_emphasis;
> +	int slopeboost;
> +	int clk_level;
> +	int data0_level;
> +	int data1_level;
> +	int data2_level;
> +};
> +
> +struct extphy_pll_config_tab {
> +	unsigned long mpixelclock;
> +	struct extphy_pll_config_param {
> +		u8	pll_nd;
> +		u16	pll_nf;
> +		u8	tmsd_divider_a;
> +		u8	tmsd_divider_b;
> +		u8	tmsd_divider_c;
> +		u8	pclk_divider_a;
> +		u8	pclk_divider_b;
> +		u8	pclk_divider_c;
> +		u8	pclk_divider_d;
> +		u8	vco_div_5;
> +		u8	ppll_nd;
> +		u8	ppll_nf;
> +		u8	ppll_no;
> +	} param[DW_HDMI_RES_MAX];
> +};
> +
> +#define RK3288_GRF_SOC_CON6			0x025c
> +#define RK3288_HDMI_SEL_VOP_LIT			(1 << 4)
> +
> +#define RK3229_GRF_SOC_CON6			0x0418
> +#define RK3229_IO_3V_DOMAIN			((7 << 4) | (7 << (4 + 16)))
> +
> +#define RK3229_GRF_SOC_CON2			0x0408
> +#define RK3229_DDC_MASK_EN			((3 << 13) | (3 << (13 + 16)))
> +
> +#define RK3229_PLL_POWER_DOWN			(BIT(12) | BIT(12 + 16))
> +#define RK3229_PLL_POWER_UP			BIT(12 + 16)
> +#define RK3229_PLL_PDATA_DEN			BIT(11 + 16)
> +#define RK3229_PLL_PDATA_EN			(BIT(11) | BIT(11 + 16))
> +
> +/* PHY Defined for RK322X */
> +#define EXT_PHY_CONTROL				0
> +#define EXT_PHY_ANALOG_RESET_MASK		0x80
> +#define EXT_PHY_DIGITAL_RESET_MASK		0x40
> +#define EXT_PHY_PCLK_INVERT_MASK		0x08
> +#define EXT_PHY_PREPCLK_INVERT_MASK		0x04
> +#define EXT_PHY_TMDSCLK_INVERT_MASK		0x02
> +#define EXT_PHY_SRC_SELECT_MASK			0x01
> +
> +#define EXT_PHY_TERM_CAL			0x03
> +#define EXT_PHY_TERM_CAL_EN_MASK		0x80
> +#define EXT_PHY_TERM_CAL_DIV_H_MASK		0x7f
> +
> +#define EXT_PHY_TERM_CAL_DIV_L			0x04
> +
> +#define EXT_PHY_PLL_PRE_DIVIDER			0xe2
> +#define EXT_PHY_PLL_FB_BIT8_MASK		0x80
> +#define EXT_PHY_PLL_PCLK_DIV5_EN_MASK		0x20
> +#define EXT_PHY_PLL_PRE_DIVIDER_MASK		0x1f
> +
> +#define EXT_PHY_PLL_FB_DIVIDER			0xe3
> +
> +#define EXT_PHY_PCLK_DIVIDER1			0xe4
> +#define EXT_PHY_PCLK_DIVIDERB_MASK		0x60
> +#define EXT_PHY_PCLK_DIVIDERA_MASK		0x1f
> +
> +#define EXT_PHY_PCLK_DIVIDER2			0xe5
> +#define EXT_PHY_PCLK_DIVIDERC_MASK		0x60
> +#define EXT_PHY_PCLK_DIVIDERD_MASK		0x1f
> +
> +#define EXT_PHY_TMDSCLK_DIVIDER			0xe6
> +#define EXT_PHY_TMDSCLK_DIVIDERC_MASK		0x30
> +#define EXT_PHY_TMDSCLK_DIVIDERA_MASK		0x0c
> +#define EXT_PHY_TMDSCLK_DIVIDERB_MASK		0x03
> +
> +#define EXT_PHY_PLL_BW				0xe7
> +
> +#define EXT_PHY_PPLL_PRE_DIVIDER		0xe9
> +#define EXT_PHY_PPLL_ENABLE_MASK		0xc0
> +#define EXT_PHY_PPLL_PRE_DIVIDER_MASK		0x1f
> +
> +#define EXT_PHY_PPLL_FB_DIVIDER			0xea
> +
> +#define EXT_PHY_PPLL_POST_DIVIDER		0xeb
> +#define EXT_PHY_PPLL_FB_DIVIDER_BIT8_MASK	0x80
> +#define EXT_PHY_PPLL_POST_DIVIDER_MASK		0x30
> +#define EXT_PHY_PPLL_LOCK_STATUS_MASK		0x01
> +
> +#define EXT_PHY_PPLL_BW				0xec
> +
> +#define EXT_PHY_SIGNAL_CTRL			0xee
> +#define EXT_PHY_TRANSITION_CLK_EN_MASK		0x80
> +#define EXT_PHY_TRANSITION_D0_EN_MASK		0x40
> +#define EXT_PHY_TRANSITION_D1_EN_MASK		0x20
> +#define EXT_PHY_TRANSITION_D2_EN_MASK		0x10
> +#define EXT_PHY_LEVEL_CLK_EN_MASK		0x08
> +#define EXT_PHY_LEVEL_D0_EN_MASK		0x04
> +#define EXT_PHY_LEVEL_D1_EN_MASK		0x02
> +#define EXT_PHY_LEVEL_D2_EN_MASK		0x01
> +
> +#define EXT_PHY_SLOPEBOOST			0xef
> +#define EXT_PHY_SLOPEBOOST_CLK_MASK		0x03
> +#define EXT_PHY_SLOPEBOOST_D0_MASK		0x0c
> +#define EXT_PHY_SLOPEBOOST_D1_MASK		0x30
> +#define EXT_PHY_SLOPEBOOST_D2_MASK		0xc0
> +
> +#define EXT_PHY_PREEMPHASIS			0xf0
> +#define EXT_PHY_PREEMPHASIS_D0_MASK		0x03
> +#define EXT_PHY_PREEMPHASIS_D1_MASK		0x0c
> +#define EXT_PHY_PREEMPHASIS_D2_MASK		0x30
> +
> +#define EXT_PHY_LEVEL1				0xf1
> +#define EXT_PHY_LEVEL_CLK_MASK			0xf0
> +#define EXT_PHY_LEVEL_D2_MASK			0x0f
> +
> +#define EXT_PHY_LEVEL2				0xf2
> +#define EXT_PHY_LEVEL_D1_MASK			0xf0
> +#define EXT_PHY_LEVEL_D0_MASK			0x0f
> +
> +#define EXT_PHY_TERM_RESIS_AUTO			0xf4
> +#define EXT_PHY_AUTO_R50_OHMS			0
> +#define EXT_PHY_AUTO_R75_OHMS			(1 << 2)
> +#define EXT_PHY_AUTO_R100_OHMS			(2 << 2)
> +#define EXT_PHY_AUTO_ROPEN_CIRCUIT		(3 << 2)
> +
> +#define EXT_PHY_TERM_RESIS_MANUAL_CLK		0xfb
> +#define EXT_PHY_TERM_RESIS_MANUAL_D2		0xfc
> +#define EXT_PHY_TERM_RESIS_MANUAL_D1		0xfd
> +#define EXT_PHY_TERM_RESIS_MANUAL_D0		0xfe
> +
> +#endif /* __DW_HDMI_ROCKCHIP__ */
> diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
> index f8dec64..4e63158 100644
> --- a/include/drm/bridge/dw_hdmi.h
> +++ b/include/drm/bridge/dw_hdmi.h
> @@ -24,6 +24,7 @@ enum {
>  enum dw_hdmi_devtype {
>  	IMX6Q_HDMI,
>  	IMX6DL_HDMI,
> +	RK3229_HDMI,
>  	RK3288_HDMI,
>  };
>  
> @@ -54,6 +55,8 @@ struct dw_hdmi_plat_data {
>  	const struct dw_hdmi_phy_config *phy_config;
>  	enum drm_mode_status (*mode_valid)(const struct dw_hdmi_plat_data *pd,
>  					   struct drm_display_mode *mode);
> +	int (*extphy_config)(const struct dw_hdmi_plat_data *plat_data,
> +			     int res_idx, int pixelclock);
>  };
>  
>  void dw_hdmi_unbind(struct device *dev, struct device *master, void *data);




From heiko at sntech.de  Thu Jan  7 02:05:06 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Thu, 07 Jan 2016 11:05:06 +0100
Subject: [PATCH v2 04/12] clk: rockchip: rk3036: enable the
 CLK_IGNORE_UNUSED flag for sclk_i2s_out
In-Reply-To: <1452155155-16232-5-git-send-email-wxt@rock-chips.com>
References: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
 <1452155155-16232-5-git-send-email-wxt@rock-chips.com>
Message-ID: <290434099.8XFZ9qmNRz@phil>

Hi Caesar,

Am Donnerstag, 7. Januar 2016, 16:25:47 schrieb Caesar Wang:
> SCLk_I2S_OUT is the noc bus clock for i2s module, this clock is used by
> extra codecs.
> 
> Due to it shouldn't belong to any driver, but we need it enabled,
> so just mark it as the CLK_IGNORE_UNUSED flag.

What makes you think it shouldn't belong to any driver?

In most schematics I have, i2s_clkout is going to some clock-input of the 
audio codec - probably the MCLK input on your rt5616. And while the new 
rt5616 driver does not seem to do clock handling, it should just handle the 
clock using the normal APIs.


Heiko


From ykk at rock-chips.com  Thu Jan  7 02:07:10 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Thu,  7 Jan 2016 18:07:10 +0800
Subject: [PATCH v2 2/2] dt-bindings: add document for Innosilicon HDMI on
 Rockchip platform
In-Reply-To: <1452160896-27096-1-git-send-email-ykk@rock-chips.com>
References: <1452160896-27096-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1452161230-27315-1-git-send-email-ykk@rock-chips.com>

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Acked-by: Rob Herring <robh at kernel.org>
---
Changes in v2:
- Correct the misspell "rk3036-dw-hdmi" (Heiko)

 .../display/rockchip/inno_hdmi-rockchip.txt        | 50 ++++++++++++++++++++++
 1 file changed, 50 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt

diff --git a/Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt
new file mode 100644
index 0000000..8096a29
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt
@@ -0,0 +1,50 @@
+Rockchip specific extensions to the Innosilicon HDMI
+================================
+
+Required properties:
+- compatible:
+	"rockchip,rk3036-inno-hdmi";
+- reg:
+	Physical base address and length of the controller's registers.
+- clocks, clock-names:
+	Phandle to hdmi controller clock, name should be "pclk"
+- interrupts:
+	HDMI interrupt number
+- ports:
+	Contain one port node with endpoint definitions as defined in
+	Documentation/devicetree/bindings/graph.txt.
+- pinctrl-0, pinctrl-name:
+	Switch the iomux of HPD/CEC pins to HDMI function.
+
+Example:
+hdmi: hdmi at 20034000 {
+	compatible = "rockchip,rk3036-inno-hdmi";
+	reg = <0x20034000 0x4000>;
+	interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
+	clocks = <&cru  PCLK_HDMI>;
+	clock-names = "pclk";
+	pinctrl-names = "default";
+	pinctrl-0 = <&hdmi_ctl>;
+	status = "disabled";
+
+	hdmi_in: port {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		hdmi_in_lcdc: endpoint at 0 {
+			reg = <0>;
+			remote-endpoint = <&lcdc_out_hdmi>;
+		};
+	};
+};
+
+&pinctrl {
+	hdmi {
+		hdmi_ctl: hdmi-ctl {
+			rockchip,pins = <1 8  RK_FUNC_1 &pcfg_pull_none>,
+					<1 9  RK_FUNC_1 &pcfg_pull_none>,
+					<1 10 RK_FUNC_1 &pcfg_pull_none>,
+					<1 11 RK_FUNC_1 &pcfg_pull_none>;
+		};
+	};
+
+};
-- 
2.1.2




From ykk at rock-chips.com  Thu Jan  7 02:15:44 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Thu, 7 Jan 2016 18:15:44 +0800
Subject: [RFC PATCH v2 3/4] drm: rockchip: hdmi: add RK3229 HDMI support
In-Reply-To: <1452161052.4776.7.camel@pengutronix.de>
References: <1452156811-18150-1-git-send-email-ykk@rock-chips.com>
 <1452157373-18783-1-git-send-email-ykk@rock-chips.com>
 <1452161052.4776.7.camel@pengutronix.de>
Message-ID: <568E3AD0.1020904@rock-chips.com>

Hi Philipp,

Thanks for your fast respond :)

On 01/07/2016 06:04 PM, Philipp Zabel wrote:
> Am Donnerstag, den 07.01.2016, 17:02 +0800 schrieb Yakir Yang:
>> RK3229 integrate an DesignedWare HDMI2.0 controller and an INNO HDMI2.0 phy,
>> the max output resolution is 4K.
>>
>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
> It sounds like the INNO HDMI2.0 phy is not necessarily specific to
> RK3229 but might also appear in other SoCs? If so, I think this should
> be implemented in a separate phy driver and be used by dw_hdmi-rockchip.

Do you mean I should create a new phy driver that place in "driver/phy" 
directly ?

I have think about this idea, and it would make things much clean. But 
INNO PHY
driver need the target pixel clock in drm_display_mode, I didn't find a 
good way
to pass this variable to separate phy driver. Do you have some idea ?

Thanks,
- Yakir

> regards
> Philipp
>
>> ---
>> Changes in v2:
>> - Split some dw-hdmi driver changes into separate patches [01/04] & [02/04]
>>
>>   drivers/gpu/drm/bridge/dw-hdmi.c            |  27 +-
>>   drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 367 ++++++++++++++++++++++++++--
>>   drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h | 137 +++++++++++
>>   include/drm/bridge/dw_hdmi.h                |   3 +
>>   4 files changed, 507 insertions(+), 27 deletions(-)
>>   create mode 100644 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h
>>
>> diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
>> index 5ad72ec..5e03d83 100644
>> --- a/drivers/gpu/drm/bridge/dw-hdmi.c
>> +++ b/drivers/gpu/drm/bridge/dw-hdmi.c
>> @@ -735,10 +735,12 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
>>   {
>>   	unsigned res_idx;
>>   	u8 val, msec;
>> +	int ret;
>>   	const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
>>   	const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg;
>>   	const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr;
>>   	const struct dw_hdmi_phy_config *phy_config = pdata->phy_config;
>> +	int mpixelclock = hdmi->hdmi_data.video_mode.mpixelclock;
>>   
>>   	if (prep)
>>   		return -EINVAL;
>> @@ -758,27 +760,38 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
>>   		return -EINVAL;
>>   	}
>>   
>> +	if (hdmi->plat_data->extphy_config) {
>> +		/* gen2 tx power off */
>> +		dw_hdmi_phy_gen2_txpwron(hdmi, 0);
>> +		dw_hdmi_phy_gen2_pddq(hdmi, 1);
>> +
>> +		ret = hdmi->plat_data->extphy_config(hdmi->plat_data, res_idx,
>> +						     mpixelclock);
>> +		/* gen2 tx power on */
>> +		dw_hdmi_phy_gen2_txpwron(hdmi, 1);
>> +		dw_hdmi_phy_gen2_pddq(hdmi, 0);
>> +
>> +		return ret;
>> +	}
>> +
>>   	/* PLL/MPLL Cfg - always match on final entry */
>>   	for (; mpll_config->mpixelclock != ~0UL; mpll_config++)
>> -		if (hdmi->hdmi_data.video_mode.mpixelclock <=
>> -		    mpll_config->mpixelclock)
>> +		if (mpixelclock <= mpll_config->mpixelclock)
>>   			break;
>>   
>>   	for (; curr_ctrl->mpixelclock != ~0UL; curr_ctrl++)
>> -		if (hdmi->hdmi_data.video_mode.mpixelclock <=
>> -		    curr_ctrl->mpixelclock)
>> +		if (mpixelclock <= curr_ctrl->mpixelclock)
>>   			break;
>>   
>>   	for (; phy_config->mpixelclock != ~0UL; phy_config++)
>> -		if (hdmi->hdmi_data.video_mode.mpixelclock <=
>> -		    phy_config->mpixelclock)
>> +		if (mpixelclock <= phy_config->mpixelclock)
>>   			break;
>>   
>>   	if (mpll_config->mpixelclock == ~0UL ||
>>   	    curr_ctrl->mpixelclock == ~0UL ||
>>   	    phy_config->mpixelclock == ~0UL) {
>>   		dev_err(hdmi->dev, "Pixel clock %d - unsupported by HDMI\n",
>> -			hdmi->hdmi_data.video_mode.mpixelclock);
>> +			mpixelclock);
>>   		return -EINVAL;
>>   	}
>>   
>> diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
>> index 8164823..24fffaa 100644
>> --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
>> +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
>> @@ -7,6 +7,7 @@
>>    * (at your option) any later version.
>>    */
>>   
>> +#include <linux/clk.h>
>>   #include <linux/module.h>
>>   #include <linux/platform_device.h>
>>   #include <linux/mfd/syscon.h>
>> @@ -21,18 +22,134 @@
>>   #include "rockchip_drm_drv.h"
>>   #include "rockchip_drm_vop.h"
>>   
>> -#define GRF_SOC_CON6                    0x025c
>> -#define HDMI_SEL_VOP_LIT                (1 << 4)
>> +#include "dw_hdmi-rockchip.h"
>>   
>>   struct rockchip_hdmi {
>>   	struct device *dev;
>>   	struct regmap *regmap;
>>   	struct drm_encoder encoder;
>>   	struct dw_hdmi_plat_data plat_data;
>> +
>> +	void __iomem *extphy_regbase;
>> +	struct clk *extphy_pclk;
>>   };
>>   
>>   #define to_rockchip_hdmi(x)	container_of(x, struct rockchip_hdmi, x)
>>   
>> +static const struct extphy_config_tab rockchip_extphy_cfg[] = {
>> +	{ .mpixelclock = 165000000,
>> +	  .pre_emphasis = 0, .slopeboost = 0, .clk_level = 4,
>> +	  .data0_level = 4, 4, 4,
>> +	},
>> +
>> +	{ .mpixelclock = 225000000,
>> +	  .pre_emphasis = 0, .slopeboost = 0, .clk_level = 6,
>> +	  .data0_level = 6, 6, 6,
>> +	},
>> +
>> +	{ .mpixelclock = 340000000,
>> +	  .pre_emphasis = 1, .slopeboost = 0, .clk_level = 6,
>> +	  .data0_level = 10, 10, 10,
>> +	},
>> +
>> +	{ .mpixelclock = 594000000,
>> +	  .pre_emphasis = 1, .slopeboost = 0, .clk_level = 7,
>> +	  .data0_level = 10, 10, 10,
>> +	},
>> +
>> +	{ .mpixelclock = ~0UL},
>> +};
>> +
>> +static const struct extphy_pll_config_tab rockchip_extphy_pll_cfg[] = {
>> +	{
>> +		.mpixelclock = 27000000, .param = {
>> +			{ .pll_nd = 1, .pll_nf = 45,
>> +			  .tmsd_divider_a = 3, 1, 1,
>> +			  .pclk_divider_a = 1, 3, 3, 4,
>> +			  .vco_div_5 = 0,
>> +			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
>> +			},
>> +			{ .pll_nd = 1, .pll_nf = 45,
>> +			  .tmsd_divider_a = 0, 3, 3,
>> +			  .pclk_divider_a = 1, 3, 3, 4,
>> +			  .vco_div_5 = 0,
>> +			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
>> +			},
>> +		},
>> +	}, {
>> +		.mpixelclock = 59400000, .param = {
>> +			{ .pll_nd = 2, .pll_nf = 99,
>> +			  .tmsd_divider_a = 3, 1, 1,
>> +			  .pclk_divider_a = 1, 3, 2, 2,
>> +			  .vco_div_5 = 0,
>> +			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
>> +			},
>> +			{ .pll_nd = 2, .pll_nf = 99,
>> +			  .tmsd_divider_a = 1, 1, 1,
>> +			  .pclk_divider_a = 1, 3, 2, 2,
>> +			  .vco_div_5 = 0,
>> +			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
>> +			},
>> +		},
>> +	}, {
>> +		.mpixelclock = 74250000, .param = {
>> +			{ .pll_nd = 2, .pll_nf = 99,
>> +			  .tmsd_divider_a = 1, 1, 1,
>> +			  .pclk_divider_a = 1, 2, 2, 2,
>> +			  .vco_div_5 = 0,
>> +			  .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8,
>> +			},
>> +			{ .pll_nd = 4, .pll_nf = 495,
>> +			  .tmsd_divider_a = 1, 2, 2,
>> +			  .pclk_divider_a = 1, 3, 3, 4,
>> +			  .vco_div_5 = 0,
>> +			  .ppll_nd = 2, .ppll_nf = 40, .ppll_no = 4,
>> +			},
>> +		},
>> +	}, {
>> +		.mpixelclock = 148500000, .param = {
>> +			{ .pll_nd = 2, .pll_nf = 99,
>> +			  .tmsd_divider_a = 1, 0, 0,
>> +			  .pclk_divider_a = 1, 2, 1, 1,
>> +			  .vco_div_5 = 0,
>> +			  .ppll_nd = 2, .ppll_nf = 40, .ppll_no = 4,
>> +			},
>> +			{ .pll_nd = 4, .pll_nf = 495,
>> +			  .tmsd_divider_a = 0, 2, 2,
>> +			  .pclk_divider_a = 1, 3, 2, 2,
>> +			  .vco_div_5 = 0,
>> +			  .ppll_nd = 4, .ppll_nf = 40, .ppll_no = 2,
>> +			},
>> +		},
>> +	}, {
>> +		.mpixelclock = 297000000, .param = {
>> +			{ .pll_nd = 2, .pll_nf = 99,
>> +			  .tmsd_divider_a = 0, 0, 0,
>> +			  .pclk_divider_a = 1, 0, 1, 1,
>> +			  .vco_div_5 = 0,
>> +			  .ppll_nd = 4, .ppll_nf = 40, .ppll_no = 2,
>> +			},
>> +			{ .pll_nd = 4, .pll_nf = 495,
>> +			  .tmsd_divider_a = 1, 2, 0,
>> +			  .pclk_divider_a = 1, 3, 1, 1,
>> +			  .vco_div_5 = 0,
>> +			  .ppll_nd = 8, .ppll_nf = 40, .ppll_no = 1,
>> +			},
>> +		},
>> +	}, {
>> +		.mpixelclock = 594000000, .param = {
>> +			{ .pll_nd = 1, .pll_nf = 99,
>> +			  .tmsd_divider_a = 0, 2, 0,
>> +			  .pclk_divider_a = 1, 0, 1, 1,
>> +			  .vco_div_5 = 0,
>> +			  .ppll_nd = 8, .ppll_nf = 40, .ppll_no = 1,
>> +			},
>> +		}
>> +	}, {
>> +		.mpixelclock = ~0UL,
>> +	}
>> +};
>> +
>>   static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
>>   	{
>>   		27000000, {
>> @@ -142,9 +259,164 @@ static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
>>   	{ ~0UL,	     0x0000, 0x0000, 0x0000}
>>   };
>>   
>> +static inline void hdmi_extphy_write(struct rockchip_hdmi *hdmi,
>> +				     unsigned short data, unsigned char addr)
>> +{
>> +	writel_relaxed(data, hdmi->extphy_regbase + (addr) * 0x04);
>> +}
>> +
>> +static inline unsigned int hdmi_extphy_read(struct rockchip_hdmi *hdmi,
>> +					    unsigned char addr)
>> +{
>> +	return readl_relaxed(hdmi->extphy_regbase + (addr) * 0x04);
>> +}
>> +
>> +static int rockchip_extphy_config(const struct dw_hdmi_plat_data *plat_data,
>> +				  int res, int pixelclock)
>> +{
>> +	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(plat_data);
>> +	const struct extphy_pll_config_tab *mpll = rockchip_extphy_pll_cfg;
>> +	const struct extphy_config_tab *ctrl = rockchip_extphy_cfg;
>> +	const struct extphy_pll_config_param *param;
>> +	unsigned long timeout;
>> +	int i, stat;
>> +
>> +	if (res >= DW_HDMI_RES_MAX) {
>> +		dev_err(hdmi->dev, "Extphy can't support res %d\n", res);
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* Find out the extphy MPLL configure parameters */
>> +	for (i = 0; mpll[i].mpixelclock != ~0UL; i++)
>> +		if (pixelclock == mpll[i].mpixelclock)
>> +			break;
>> +	if (mpll[i].mpixelclock == ~0UL) {
>> +		dev_err(hdmi->dev, "Extphy can't support %dHz\n", pixelclock);
>> +		return -EINVAL;
>> +	}
>> +	param = &mpll[i].param[res];
>> +
>> +	regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2,
>> +		     RK3229_PLL_POWER_DOWN | RK3229_PLL_PDATA_DEN);
>> +
>> +	/*
>> +	 * Configure external HDMI PHY PLL registers.
>> +	 */
>> +	stat = ((param->pll_nf >> 1) & EXT_PHY_PLL_FB_BIT8_MASK) |
>> +	       ((param->vco_div_5 & 1) << 5) |
>> +	       (param->pll_nd & EXT_PHY_PLL_PRE_DIVIDER_MASK);
>> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_PLL_PRE_DIVIDER);
>> +
>> +	hdmi_extphy_write(hdmi, param->pll_nf, EXT_PHY_PLL_FB_DIVIDER);
>> +
>> +	stat = (param->pclk_divider_a & EXT_PHY_PCLK_DIVIDERA_MASK) |
>> +	       ((param->pclk_divider_b & 3) << 5);
>> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_PCLK_DIVIDER1);
>> +
>> +	stat = (param->pclk_divider_d & EXT_PHY_PCLK_DIVIDERD_MASK) |
>> +	       ((param->pclk_divider_c & 3) << 5);
>> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_PCLK_DIVIDER2);
>> +
>> +	stat = ((param->tmsd_divider_c & 3) << 4) |
>> +	       ((param->tmsd_divider_a & 3) << 2) |
>> +	       (param->tmsd_divider_b & 3);
>> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_TMDSCLK_DIVIDER);
>> +
>> +	hdmi_extphy_write(hdmi, param->ppll_nf, EXT_PHY_PPLL_FB_DIVIDER);
>> +
>> +	if (param->ppll_no == 1) {
>> +		hdmi_extphy_write(hdmi, 0, EXT_PHY_PPLL_POST_DIVIDER);
>> +
>> +		stat = 0x20 | param->ppll_nd;
>> +		hdmi_extphy_write(hdmi, stat, EXT_PHY_PPLL_PRE_DIVIDER);
>> +	} else {
>> +		stat = ((param->ppll_no / 2) - 1) << 4;
>> +		hdmi_extphy_write(hdmi, stat, EXT_PHY_PPLL_POST_DIVIDER);
>> +
>> +		stat = 0xe0 | param->ppll_nd;
>> +		hdmi_extphy_write(hdmi, stat, EXT_PHY_PPLL_PRE_DIVIDER);
>> +	}
>> +
>> +
>> +	/* Find out the external HDMI PHY driver configure parameters */
>> +	for (i = 0; ctrl[i].mpixelclock != ~0UL; i++)
>> +		if (pixelclock <= ctrl[i].mpixelclock)
>> +			break;
>> +	if (ctrl[i].mpixelclock == ~0UL) {
>> +		dev_err(hdmi->dev, "Extphy can't support %dHz\n", pixelclock);
>> +		return -EINVAL;
>> +	}
>> +
>> +	/*
>> +	 * Configure the external HDMI PHY driver registers.
>> +	 */
>> +	if (ctrl[i].slopeboost) {
>> +		hdmi_extphy_write(hdmi, 0xff, EXT_PHY_SIGNAL_CTRL);
>> +
>> +		stat = (ctrl[i].slopeboost - 1) & 3;
>> +		stat = (stat << 6) | (stat << 4) | (stat << 2) | stat;
>> +		hdmi_extphy_write(hdmi, stat, EXT_PHY_SLOPEBOOST);
>> +	} else
>> +		hdmi_extphy_write(hdmi, 0x0f, EXT_PHY_SIGNAL_CTRL);
>> +
>> +	stat = ctrl[i].pre_emphasis & 3;
>> +	stat = (stat << 4) | (stat << 2) | stat;
>> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_PREEMPHASIS);
>> +
>> +	stat = ((ctrl[i].clk_level & 0xf) << 4) | (ctrl[i].data2_level & 0xf);
>> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_LEVEL1);
>> +
>> +	stat = ((ctrl[i].data1_level & 0xf) << 4) | (ctrl[i].data0_level & 0xf);
>> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_LEVEL2);
>> +
>> +	hdmi_extphy_write(hdmi, 0x22, 0xf3);
>> +
>> +	stat = clk_get_rate(hdmi->extphy_pclk) / 100000;
>> +	hdmi_extphy_write(hdmi, ((stat >> 8) & 0xff) | 0x80, EXT_PHY_TERM_CAL);
>> +	hdmi_extphy_write(hdmi,  stat & 0xff, EXT_PHY_TERM_CAL_DIV_L);
>> +
>> +	if (pixelclock > 340000000)
>> +		stat = EXT_PHY_AUTO_R100_OHMS;
>> +	else if (pixelclock > 200000000)
>> +		stat = EXT_PHY_AUTO_R50_OHMS;
>> +	else
>> +		stat = EXT_PHY_AUTO_ROPEN_CIRCUIT;
>> +	hdmi_extphy_write(hdmi, stat | 0x20, EXT_PHY_TERM_RESIS_AUTO);
>> +	hdmi_extphy_write(hdmi, (stat >> 8) & 0xff, EXT_PHY_TERM_CAL);
>> +
>> +	stat = (pixelclock > 200000000) ? 0 : 0x11;
>> +	hdmi_extphy_write(hdmi, stat, EXT_PHY_PLL_BW);
>> +	hdmi_extphy_write(hdmi, 0x27, EXT_PHY_PPLL_BW);
>> +
>> +	regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2, RK3229_PLL_POWER_UP);
>> +
>> +	/* Detect whether PLL is lock or not */
>> +	timeout = jiffies + msecs_to_jiffies(100);
>> +	while (!time_after(jiffies, timeout)) {
>> +		usleep_range(1000, 2000);
>> +		stat = hdmi_extphy_read(hdmi, EXT_PHY_PPLL_POST_DIVIDER);
>> +		if (stat & EXT_PHY_PPLL_LOCK_STATUS_MASK)
>> +			break;
>> +	}
>> +
>> +	regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2, RK3229_PLL_PDATA_EN);
>> +
>> +	if ((stat & EXT_PHY_PPLL_LOCK_STATUS_MASK) == 0) {
>> +		dev_err(hdmi->dev, "EXT PHY PLL not locked\n");
>> +		return -EBUSY;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>>   static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
>>   {
>>   	struct device_node *np = hdmi->dev->of_node;
>> +	struct platform_device *pdev;
>> +	struct resource *iores;
>> +	int ret;
>> +
>> +	pdev = container_of(hdmi->dev, struct platform_device, dev);
>>   
>>   	hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
>>   	if (IS_ERR(hdmi->regmap)) {
>> @@ -152,6 +424,37 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
>>   		return PTR_ERR(hdmi->regmap);
>>   	}
>>   
>> +	if (hdmi->plat_data.dev_type == RK3229_HDMI) {
>> +		iores = platform_get_resource(pdev, IORESOURCE_MEM, 1);
>> +		if (!iores)
>> +			return -ENXIO;
>> +
>> +		hdmi->extphy_regbase = devm_ioremap_resource(hdmi->dev, iores);
>> +		if (IS_ERR(hdmi->extphy_regbase)) {
>> +			dev_err(hdmi->dev, "failed to map extphy regbase\n");
>> +			return PTR_ERR(hdmi->extphy_regbase);
>> +		}
>> +
>> +		hdmi->extphy_pclk = devm_clk_get(hdmi->dev, "extphy");
>> +		if (IS_ERR(hdmi->extphy_pclk)) {
>> +			dev_err(hdmi->dev, "failed to get extphy clock\n");
>> +			return PTR_ERR(hdmi->extphy_pclk);
>> +		}
>> +
>> +		ret = clk_prepare_enable(hdmi->extphy_pclk);
>> +		if (ret) {
>> +			dev_err(hdmi->dev, "failed to enable extphy clk: %d\n",
>> +				ret);
>> +			return ret;
>> +		}
>> +
>> +		regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON6,
>> +			     RK3229_IO_3V_DOMAIN);
>> +
>> +		regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2,
>> +			     RK3229_DDC_MASK_EN);
>> +	}
>> +
>>   	return 0;
>>   }
>>   
>> @@ -159,17 +462,23 @@ static enum drm_mode_status
>>   dw_hdmi_rockchip_mode_valid(const struct dw_hdmi_plat_data *plat_data,
>>   			    struct drm_display_mode *mode)
>>   {
>> -	const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg;
>>   	int pclk = mode->clock * 1000;
>>   	bool valid = false;
>>   	int i;
>>   
>> -	for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) {
>> -		if (pclk == mpll_cfg[i].mpixelclock) {
>> -			valid = true;
>> -			break;
>> -		}
>> -	}
>> +	if (plat_data->dev_type == RK3288_HDMI)
>> +		for (i = 0; rockchip_mpll_cfg[i].mpixelclock != ~0UL; i++)
>> +			if (pclk == rockchip_mpll_cfg[i].mpixelclock) {
>> +				valid = true;
>> +				break;
>> +			}
>> +
>> +	if (plat_data->dev_type == RK3229_HDMI)
>> +		for (i = 0; rockchip_extphy_pll_cfg[i].mpixelclock != ~0UL; i++)
>> +			if (pclk == rockchip_extphy_pll_cfg[i].mpixelclock) {
>> +				valid = true;
>> +				break;
>> +			}
>>   
>>   	return (valid) ? MODE_OK : MODE_BAD;
>>   }
>> @@ -199,21 +508,30 @@ static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
>>   static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
>>   {
>>   	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
>> +	int out_mode = ROCKCHIP_OUT_MODE_AAAA;
>>   	u32 val;
>>   	int mux;
>>   
>> +	if (hdmi->plat_data.dev_type == RK3229_HDMI)
>> +		out_mode = ROCKCHIP_OUT_MODE_P888;
>> +
>>   	rockchip_drm_crtc_mode_config(encoder->crtc, DRM_MODE_CONNECTOR_HDMIA,
>> -				      ROCKCHIP_OUT_MODE_AAAA);
>> +				      out_mode);
>>   
>> -	mux = rockchip_drm_encoder_get_mux_id(hdmi->dev->of_node, encoder);
>> -	if (mux)
>> -		val = HDMI_SEL_VOP_LIT | (HDMI_SEL_VOP_LIT << 16);
>> -	else
>> -		val = HDMI_SEL_VOP_LIT << 16;
>> +	if (hdmi->plat_data.dev_type == RK3288_HDMI) {
>> +		mux = rockchip_drm_encoder_get_mux_id(hdmi->dev->of_node,
>> +						      encoder);
>> +		if (mux)
>> +			val = RK3288_HDMI_SEL_VOP_LIT |
>> +			      (RK3288_HDMI_SEL_VOP_LIT << 16);
>> +		else
>> +			val = RK3288_HDMI_SEL_VOP_LIT << 16;
>>   
>> -	regmap_write(hdmi->regmap, GRF_SOC_CON6, val);
>> -	dev_dbg(hdmi->dev, "vop %s output to hdmi\n",
>> -		(mux) ? "LIT" : "BIG");
>> +		regmap_write(hdmi->regmap, RK3288_GRF_SOC_CON6, val);
>> +
>> +		dev_dbg(hdmi->dev, "vop %s output to hdmi\n",
>> +			(mux) ? "LIT" : "BIG");
>> +	}
>>   }
>>   
>>   static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = {
>> @@ -223,7 +541,7 @@ static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_fun
>>   	.disable    = dw_hdmi_rockchip_encoder_disable,
>>   };
>>   
>> -static const struct dw_hdmi_plat_data rockchip_hdmi_drv_data = {
>> +static const struct dw_hdmi_plat_data rk3288_hdmi_drv_data = {
>>   	.mode_valid = dw_hdmi_rockchip_mode_valid,
>>   	.mpll_cfg   = rockchip_mpll_cfg,
>>   	.cur_ctr    = rockchip_cur_ctr,
>> @@ -231,9 +549,18 @@ static const struct dw_hdmi_plat_data rockchip_hdmi_drv_data = {
>>   	.dev_type   = RK3288_HDMI,
>>   };
>>   
>> +static const struct dw_hdmi_plat_data rk3229_hdmi_drv_data = {
>> +	.mode_valid = dw_hdmi_rockchip_mode_valid,
>> +	.extphy_config = rockchip_extphy_config,
>> +	.dev_type   = RK3229_HDMI,
>> +};
>> +
>>   static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
>>   	{ .compatible = "rockchip,rk3288-dw-hdmi",
>> -	  .data = &rockchip_hdmi_drv_data
>> +	  .data = &rk3288_hdmi_drv_data
>> +	},
>> +	{ .compatible = "rockchip,rk3229-dw-hdmi",
>> +	  .data = &rk3229_hdmi_drv_data
>>   	},
>>   	{},
>>   };
>> diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h
>> new file mode 100644
>> index 0000000..f7ec733
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h
>> @@ -0,0 +1,137 @@
>> +#ifndef __DW_HDMI_ROCKCHIP__
>> +#define __DW_HDMI_ROCKCHIP__
>> +
>> +struct extphy_config_tab {
>> +	u32 mpixelclock;
>> +	int pre_emphasis;
>> +	int slopeboost;
>> +	int clk_level;
>> +	int data0_level;
>> +	int data1_level;
>> +	int data2_level;
>> +};
>> +
>> +struct extphy_pll_config_tab {
>> +	unsigned long mpixelclock;
>> +	struct extphy_pll_config_param {
>> +		u8	pll_nd;
>> +		u16	pll_nf;
>> +		u8	tmsd_divider_a;
>> +		u8	tmsd_divider_b;
>> +		u8	tmsd_divider_c;
>> +		u8	pclk_divider_a;
>> +		u8	pclk_divider_b;
>> +		u8	pclk_divider_c;
>> +		u8	pclk_divider_d;
>> +		u8	vco_div_5;
>> +		u8	ppll_nd;
>> +		u8	ppll_nf;
>> +		u8	ppll_no;
>> +	} param[DW_HDMI_RES_MAX];
>> +};
>> +
>> +#define RK3288_GRF_SOC_CON6			0x025c
>> +#define RK3288_HDMI_SEL_VOP_LIT			(1 << 4)
>> +
>> +#define RK3229_GRF_SOC_CON6			0x0418
>> +#define RK3229_IO_3V_DOMAIN			((7 << 4) | (7 << (4 + 16)))
>> +
>> +#define RK3229_GRF_SOC_CON2			0x0408
>> +#define RK3229_DDC_MASK_EN			((3 << 13) | (3 << (13 + 16)))
>> +
>> +#define RK3229_PLL_POWER_DOWN			(BIT(12) | BIT(12 + 16))
>> +#define RK3229_PLL_POWER_UP			BIT(12 + 16)
>> +#define RK3229_PLL_PDATA_DEN			BIT(11 + 16)
>> +#define RK3229_PLL_PDATA_EN			(BIT(11) | BIT(11 + 16))
>> +
>> +/* PHY Defined for RK322X */
>> +#define EXT_PHY_CONTROL				0
>> +#define EXT_PHY_ANALOG_RESET_MASK		0x80
>> +#define EXT_PHY_DIGITAL_RESET_MASK		0x40
>> +#define EXT_PHY_PCLK_INVERT_MASK		0x08
>> +#define EXT_PHY_PREPCLK_INVERT_MASK		0x04
>> +#define EXT_PHY_TMDSCLK_INVERT_MASK		0x02
>> +#define EXT_PHY_SRC_SELECT_MASK			0x01
>> +
>> +#define EXT_PHY_TERM_CAL			0x03
>> +#define EXT_PHY_TERM_CAL_EN_MASK		0x80
>> +#define EXT_PHY_TERM_CAL_DIV_H_MASK		0x7f
>> +
>> +#define EXT_PHY_TERM_CAL_DIV_L			0x04
>> +
>> +#define EXT_PHY_PLL_PRE_DIVIDER			0xe2
>> +#define EXT_PHY_PLL_FB_BIT8_MASK		0x80
>> +#define EXT_PHY_PLL_PCLK_DIV5_EN_MASK		0x20
>> +#define EXT_PHY_PLL_PRE_DIVIDER_MASK		0x1f
>> +
>> +#define EXT_PHY_PLL_FB_DIVIDER			0xe3
>> +
>> +#define EXT_PHY_PCLK_DIVIDER1			0xe4
>> +#define EXT_PHY_PCLK_DIVIDERB_MASK		0x60
>> +#define EXT_PHY_PCLK_DIVIDERA_MASK		0x1f
>> +
>> +#define EXT_PHY_PCLK_DIVIDER2			0xe5
>> +#define EXT_PHY_PCLK_DIVIDERC_MASK		0x60
>> +#define EXT_PHY_PCLK_DIVIDERD_MASK		0x1f
>> +
>> +#define EXT_PHY_TMDSCLK_DIVIDER			0xe6
>> +#define EXT_PHY_TMDSCLK_DIVIDERC_MASK		0x30
>> +#define EXT_PHY_TMDSCLK_DIVIDERA_MASK		0x0c
>> +#define EXT_PHY_TMDSCLK_DIVIDERB_MASK		0x03
>> +
>> +#define EXT_PHY_PLL_BW				0xe7
>> +
>> +#define EXT_PHY_PPLL_PRE_DIVIDER		0xe9
>> +#define EXT_PHY_PPLL_ENABLE_MASK		0xc0
>> +#define EXT_PHY_PPLL_PRE_DIVIDER_MASK		0x1f
>> +
>> +#define EXT_PHY_PPLL_FB_DIVIDER			0xea
>> +
>> +#define EXT_PHY_PPLL_POST_DIVIDER		0xeb
>> +#define EXT_PHY_PPLL_FB_DIVIDER_BIT8_MASK	0x80
>> +#define EXT_PHY_PPLL_POST_DIVIDER_MASK		0x30
>> +#define EXT_PHY_PPLL_LOCK_STATUS_MASK		0x01
>> +
>> +#define EXT_PHY_PPLL_BW				0xec
>> +
>> +#define EXT_PHY_SIGNAL_CTRL			0xee
>> +#define EXT_PHY_TRANSITION_CLK_EN_MASK		0x80
>> +#define EXT_PHY_TRANSITION_D0_EN_MASK		0x40
>> +#define EXT_PHY_TRANSITION_D1_EN_MASK		0x20
>> +#define EXT_PHY_TRANSITION_D2_EN_MASK		0x10
>> +#define EXT_PHY_LEVEL_CLK_EN_MASK		0x08
>> +#define EXT_PHY_LEVEL_D0_EN_MASK		0x04
>> +#define EXT_PHY_LEVEL_D1_EN_MASK		0x02
>> +#define EXT_PHY_LEVEL_D2_EN_MASK		0x01
>> +
>> +#define EXT_PHY_SLOPEBOOST			0xef
>> +#define EXT_PHY_SLOPEBOOST_CLK_MASK		0x03
>> +#define EXT_PHY_SLOPEBOOST_D0_MASK		0x0c
>> +#define EXT_PHY_SLOPEBOOST_D1_MASK		0x30
>> +#define EXT_PHY_SLOPEBOOST_D2_MASK		0xc0
>> +
>> +#define EXT_PHY_PREEMPHASIS			0xf0
>> +#define EXT_PHY_PREEMPHASIS_D0_MASK		0x03
>> +#define EXT_PHY_PREEMPHASIS_D1_MASK		0x0c
>> +#define EXT_PHY_PREEMPHASIS_D2_MASK		0x30
>> +
>> +#define EXT_PHY_LEVEL1				0xf1
>> +#define EXT_PHY_LEVEL_CLK_MASK			0xf0
>> +#define EXT_PHY_LEVEL_D2_MASK			0x0f
>> +
>> +#define EXT_PHY_LEVEL2				0xf2
>> +#define EXT_PHY_LEVEL_D1_MASK			0xf0
>> +#define EXT_PHY_LEVEL_D0_MASK			0x0f
>> +
>> +#define EXT_PHY_TERM_RESIS_AUTO			0xf4
>> +#define EXT_PHY_AUTO_R50_OHMS			0
>> +#define EXT_PHY_AUTO_R75_OHMS			(1 << 2)
>> +#define EXT_PHY_AUTO_R100_OHMS			(2 << 2)
>> +#define EXT_PHY_AUTO_ROPEN_CIRCUIT		(3 << 2)
>> +
>> +#define EXT_PHY_TERM_RESIS_MANUAL_CLK		0xfb
>> +#define EXT_PHY_TERM_RESIS_MANUAL_D2		0xfc
>> +#define EXT_PHY_TERM_RESIS_MANUAL_D1		0xfd
>> +#define EXT_PHY_TERM_RESIS_MANUAL_D0		0xfe
>> +
>> +#endif /* __DW_HDMI_ROCKCHIP__ */
>> diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
>> index f8dec64..4e63158 100644
>> --- a/include/drm/bridge/dw_hdmi.h
>> +++ b/include/drm/bridge/dw_hdmi.h
>> @@ -24,6 +24,7 @@ enum {
>>   enum dw_hdmi_devtype {
>>   	IMX6Q_HDMI,
>>   	IMX6DL_HDMI,
>> +	RK3229_HDMI,
>>   	RK3288_HDMI,
>>   };
>>   
>> @@ -54,6 +55,8 @@ struct dw_hdmi_plat_data {
>>   	const struct dw_hdmi_phy_config *phy_config;
>>   	enum drm_mode_status (*mode_valid)(const struct dw_hdmi_plat_data *pd,
>>   					   struct drm_display_mode *mode);
>> +	int (*extphy_config)(const struct dw_hdmi_plat_data *plat_data,
>> +			     int res_idx, int pixelclock);
>>   };
>>   
>>   void dw_hdmi_unbind(struct device *dev, struct device *master, void *data);
>
>
>
>




From heiko at sntech.de  Thu Jan  7 02:44:22 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Thu, 07 Jan 2016 11:44:22 +0100
Subject: [PATCH v2 00/12] Add the family patches to support for kylin board
In-Reply-To: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
References: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1759945.VO3mCQb2oc@phil>

Hi Caesar,

Am Donnerstag, 7. Januar 2016, 16:25:43 schrieb Caesar Wang:
> Hi all,
> 
> Happy new year! :)
> Let's go on working for happy.

please make sure to split your patch-series at appropriate points for future 
versions to only address relevant people. For example I don't think Ulf 
really cares about getting the audio-related or general devicetree patches.


Heiko

> This series patches are based on kernel version 4.4.0-rc8+
> (Linux version 4.4.0-rc8+ (wxt at ubuntu)...#142 SMP Thu Jan 7 12:38:40 CST
> 2016)
> 
> This series pacthes are verified on the following github:
> https://github.com/Caesar-github/rockchip/commits/kylin-develop4.4
> 
> This series has 12 patches: (12--->1)
> bbd1a01 ARM: dts: Add pl330-broken-no-flushp quirk for rk3036 SoCs
> fb8c1fd ARM: dts: rockchip: add the lcdc and hdmi node for rk3036
> 1aeb49d ARM: dts: rockchip: add the sdmmc for kylin board
> 3e19128 ARM: dts: rockchip: enable the uart0 for kylin board
> e7de154 ARM: dts: rockchip: add the wifi/bt regulator for kylin board
> 8699698 ARM: dts: rockchip: enable the high speed on sdio for kylin board
> 21eded4 mmc: pwrseq: add support for power-on sequencing through DT
> cdf3a7e mmc: pwrseq: Document optional exteral vcc for the simple power
> sequence 131c249 clk: rockchip: rk3036: enable the CLK_IGNORE_UNUSED flag
> for sclk_i2s_out b7ff8e1 ARM: dts: rockchip: override the clocks in i2s
> for kylin board 5f56af4 ARM: dts: rockchip: add the sound codec for kylin
> board
> 914d9c0 ARM: dts: rockchip: set the pinctrl default setting for rk3036 i2s
> ..
> 
> This series patches have the following decriptions:
> 
> PATCH[1/12-4/12]:====> Support rt5616 codec for kylin board
> 914d9c0 ARM: dts: rockchip: set the pinctrl default setting for rk3036 i2s
> 5f56af4 ARM: dts: rockchip: add the sound codec for kylin board
> b7ff8e1 ARM: dts: rockchip: override the clocks in i2s for kylin board
> 131c249 clk: rockchip: rk3036: enable the CLK_IGNORE_UNUSED flag for
> sclk_i2s_out
> 
> This patches is working for codec rt5616 on kylin board. (RK3036 SoCs)
> 
> That's needed the following rt5616 driver from Mark Brown Branch.
> (cherry picked from git.kernel.org broonie/sound.git for-next)
> 
> cf94b7a UPSTREAM: ASoC: rt5616: rename some alsa control names
> 86f561d UPSTREAM: ASoC: rt5616: add devicetree document for rt5616
> 0e0f5f8 UPSTREAM: ASoC: rt5616: add an of_match table
> 263a330 UPSTREAM: ASoC: rt5616: Return error if device ID mismatch
> 0094139 UPSTREAM: ASoC: rt5616: add rt5616 codec driver
> 
> Notes:
> I'm run it with the ubuntu 15.04 to set the configure.
> ./amixer "HP Playback Switch" 1
> ./amixer "HP Playback Volume" 20
> ./amixer "HPO MIX DAC1 Switch" 1
> ./amixer "OUT MIXR DAC R1 Switch" 1
> ./amixer "OUT MIXL DAC L1 Switch" 1
> ./amixer "Stereo DAC MIXR DAC R1 Switch" 1
> ./amixer "Stereo DAC MIXL DAC L1 Switch" 1
> 
> root at linaro-alip:/media/aplay# ./aplay pcm.wav
> Playing sample: 2 ch, 48000 hz, 16 bit
> ..
> That can be normal playing the music.
> ----
> 
> PATCH[5/12-9/12]: ====> Support wifi/bt module for kylin board
> cdf3a7e mmc: pwrseq: Document optional exteral vcc for the simple power
> sequence 21eded4 mmc: pwrseq: add support for power-on sequencing through
> DT 8699698 ARM: dts: rockchip: enable the high speed on sdio for kylin
> board e7de154 ARM: dts: rockchip: add the wifi/bt regulator for kylin
> board 3e19128 ARM: dts: rockchip: enable the uart0 for kylin board
> 
> The wifi/bt driver hasn't land in mainline, the driver is in here:
> https://github.com/Caesar-github/rockchip/commit/1912ef4c0e358677904b0a0bb
> b83f9745066f0ec
> 
> wpa_supplicant -Dnl80211 -c /etc/wifi/wpa_supplicant.conf -i wlan0 -B
> wpa_cli add_network
> wpa_cli set_network 1 ssid \"wxt\"
> wpa_cli set_network 1 psk \"123456789\"
> wpa_cli select_network 1
> wpa_cli save_config
> 
> ifconfig wlan0 192.168.1.105
> route add default gw 192.168.1.1
> 
> root at linaro-alip:/# ping 192.168.1.1
> PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
> 64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=6.83 ms
> 64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=184 ms
> 64 bytes from 192.168.1.1: icmp_seq=3 ttl=64 time=9.21 ms
> 64 bytes from 192.168.1.1: icmp_seq=4 ttl=64 time=6.84 ms
> 64 bytes from 192.168.1.1: icmp_seq=5 ttl=64 time=11.2 ms
> 64 bytes from 192.168.1.1: icmp_seq=6 ttl=64 time=9.24 ms
> 64 bytes from 192.168.1.1: icmp_seq=7 ttl=64 time=12.9 ms
> 64 bytes from 192.168.1.1: icmp_seq=8 ttl=64 time=7.02 ms
> 64 bytes from 192.168.1.1: icmp_seq=9 ttl=64 time=24.3 ms
> 64 bytes from 192.168.1.1: icmp_seq=10 ttl=64 time=13.6 ms
> 64 bytes from 192.168.1.1: icmp_seq=11 ttl=64 time=3.87 m
> ...
> 
> The wifi throughput:
> 
> iperf -s -i 1
> ...
> [  4] 116.0-117.0 sec  2.88 MBytes  24.1 Mbits/sec
> [  4] 117.0-118.0 sec  2.99 MBytes  25.0 Mbits/sec
> [  4] 118.0-119.0 sec  3.01 MBytes  25.2 Mbits/sec
> [  4] 119.0-120.0 sec  2.89 MBytes  24.2 Mbits/sec
> [  4]  0.0-120.4 sec   332 MBytes  23.1 Mbits/sec
> ----
> 
> PATCH[10/12]: ====> sdmmc patch for kylin board
> ARM: dts: rockchip: add the sdmmc for kylin board
> 
> You should mark it "oaky" if you want to use the sdmmc.
> This patch default is disabing the sdmmc since the sdmmc and
> uart2(debug port) is reusing pin.
> ----
> 
> PATCH[11/12]: ====> hdmi/lcdc diaply patch for rk3036 dts
> ARM: dts: rockchip: add the lcdc and hdmi node for rk3036
> 
> We can add this patch to support it since the Mark Yao has land
> your vop support.
> https://github.com/markyzq/kernel-drm-rockchip/commits/drm-rockchip-next-2
> 015-12-28 ----
> 
> PATCH[12/12]:====> DMA Patchs
> ARM: dts: Add pl330-broken-no-flushp quirk for rk3036 SoCs
> 
> In order to be better read/write for emmc.
> This patch is based on the following:(Fix broken DMAFLUSHP on Rockchips
> platform)
> 
> 3055695 FROMLIST: DMA: dmaengine: expose max burst capability to clientmZ
> (am from https://patchwork.kernel.org/patch/7399361/)
> ed56bee FROMLIST: DMA: pl330: add quirk for broken no flushp
> (am from https://patchwork.kernel.org/patch/7399371/)
> 0c43bc3 FROMLIST: Documentation: arm-pl330: add description of arm,
> pl330-broken-no-flushp (am from
> https://patchwork.kernel.org/patch/7399371/)
> 623a699 FROMLIST: DMA: pl330: support burst mode for dev-to-mem and
> mem-to-dev transmit (am from https://patchwork.kernel.org/patch/7399381/)
> ----
> 
> 
> Changes in v2:
> - modify the commit.
> - fix the mclk-fs as the 256.
> - This fix inmmc-power-sequences, as Heiko comment on
>   https://patchwork.kernel.org/patch/7903161/
> - As Heiko comment, modify it to fit the simple-mmc-sequence.
> - Add the uart0 commit content.
> 
> Caesar Wang (12):
>   ARM: dts: rockchip: set the pinctrl default setting for rk3036 i2s
>   ARM: dts: rockchip: add the sound codec for kylin board
>   ARM: dts: rockchip: override the clocks in i2s for kylin board
>   clk: rockchip: rk3036: enable the CLK_IGNORE_UNUSED flag for
>     sclk_i2s_out
>   mmc: pwrseq: Document optional exteral vcc for the simple power
>     sequence
>   mmc: pwrseq: add support for power-on sequencing through DT
>   ARM: dts: rockchip: enable the high speed on sdio for kylin board
>   ARM: dts: rockchip: add the wifi/bt regulator for kylin board
>   ARM: dts: rockchip: enable the uart0 for kylin board
>   ARM: dts: rockchip: add the sdmmc for kylin board
>   ARM: dts: rockchip: add the lcdc and hdmi node for rk3036
>   ARM: dts: Add pl330-broken-no-flushp quirk for rk3036 SoCs
> 
>  .../devicetree/bindings/mmc/mmc-pwrseq-simple.txt  |   9 ++
>  arch/arm/boot/dts/rk3036-kylin.dts                 | 119
> +++++++++++++++++++++ arch/arm/boot/dts/rk3036.dtsi                     
> |  74 +++++++++++-- drivers/clk/rockchip/clk-rk3036.c                  | 
>  2 +-
>  drivers/mmc/core/pwrseq_simple.c                   |  16 +++
>  5 files changed, 213 insertions(+), 7 deletions(-)



From zhengxing at rock-chips.com  Thu Jan  7 04:17:34 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Thu,  7 Jan 2016 20:17:34 +0800
Subject: [PATCH v2 2/5] clk: rockchip: rk3036: fix uarts clock error
In-Reply-To: <1452169057-24467-1-git-send-email-zhengxing@rock-chips.com>
References: <1452169057-24467-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <1452169057-24467-3-git-send-email-zhengxing@rock-chips.com>

Due to a copy-paste error the uart1 and uart2 clock div set
incorrect, we should to fix it.

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
---

 drivers/clk/rockchip/clk-rk3036.c |    8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/clk/rockchip/clk-rk3036.c b/drivers/clk/rockchip/clk-rk3036.c
index 7e3b41c..04b5249 100644
--- a/drivers/clk/rockchip/clk-rk3036.c
+++ b/drivers/clk/rockchip/clk-rk3036.c
@@ -242,11 +242,11 @@ static struct rockchip_clk_branch rk3036_clk_branches[] __initdata = {
 			RK2928_CLKSEL_CON(13), 0, 7, DFLAGS,
 			RK2928_CLKGATE_CON(1), 8, GFLAGS),
 	COMPOSITE_NOMUX(0, "uart1_src", "uart_pll_clk", 0,
-			RK2928_CLKSEL_CON(13), 0, 7, DFLAGS,
-			RK2928_CLKGATE_CON(1), 8, GFLAGS),
+			RK2928_CLKSEL_CON(14), 0, 7, DFLAGS,
+			RK2928_CLKGATE_CON(1), 10, GFLAGS),
 	COMPOSITE_NOMUX(0, "uart2_src", "uart_pll_clk", 0,
-			RK2928_CLKSEL_CON(13), 0, 7, DFLAGS,
-			RK2928_CLKGATE_CON(1), 8, GFLAGS),
+			RK2928_CLKSEL_CON(15), 0, 7, DFLAGS,
+			RK2928_CLKGATE_CON(1), 12, GFLAGS),
 	COMPOSITE_FRACMUX(0, "uart0_frac", "uart0_src", CLK_SET_RATE_PARENT,
 			RK2928_CLKSEL_CON(17), 0,
 			RK2928_CLKGATE_CON(1), 9, GFLAGS,
-- 
1.7.9.5




From zhengxing at rock-chips.com  Thu Jan  7 04:17:32 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Thu,  7 Jan 2016 20:17:32 +0800
Subject: [PATCH v2 0/5] fix some clock configuration for the RK3036 platform
Message-ID: <1452169057-24467-1-git-send-email-zhengxing@rock-chips.com>


Hi:
  In the development work, we found that some of the previous
incorrect clock configuration on the RK3036 platform, we should
fix them.



Xing Zheng (5):
  clk: rockchip: rk3036: fix the FLAGs for clock mux
  clk: rockchip: rk3036: fix uarts clock error
  clk: rockchip: rk3036: fix the div offset for emac clock
  clk: rockchip: rk3036: rename emac ext source clock
  clk: rockchip: rk3036: add HCLK_MAC id for emac

 drivers/clk/rockchip/clk-rk3036.c      |   28 ++++++++++++++--------------
 include/dt-bindings/clock/rk3036-cru.h |    1 +
 2 files changed, 15 insertions(+), 14 deletions(-)

-- 
1.7.9.5




From zhengxing at rock-chips.com  Thu Jan  7 04:17:35 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Thu,  7 Jan 2016 20:17:35 +0800
Subject: [PATCH v2 3/5] clk: rockchip: rk3036: fix the div offset for emac
 clock
In-Reply-To: <1452169057-24467-1-git-send-email-zhengxing@rock-chips.com>
References: <1452169057-24467-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <1452169057-24467-4-git-send-email-zhengxing@rock-chips.com>

Due to reference to old version TRM, there are incorrect emac clock node.
The SEL_21_9 is used for the parent div, the SEL_21_4 is used for the
child div.

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
---

 drivers/clk/rockchip/clk-rk3036.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/rockchip/clk-rk3036.c b/drivers/clk/rockchip/clk-rk3036.c
index 04b5249..1f00fab 100644
--- a/drivers/clk/rockchip/clk-rk3036.c
+++ b/drivers/clk/rockchip/clk-rk3036.c
@@ -344,12 +344,12 @@ static struct rockchip_clk_branch rk3036_clk_branches[] __initdata = {
 			RK2928_CLKGATE_CON(10), 5, GFLAGS),
 
 	COMPOSITE_NOGATE(0, "mac_pll_src", mux_pll_src_3plls_p, 0,
-			RK2928_CLKSEL_CON(21), 0, 2, MFLAGS, 4, 5, DFLAGS),
+			RK2928_CLKSEL_CON(21), 0, 2, MFLAGS, 9, 5, DFLAGS),
 	MUX(SCLK_MACREF, "mac_clk_ref", mux_mac_p, CLK_SET_RATE_PARENT,
 			RK2928_CLKSEL_CON(21), 3, 1, MFLAGS),
 
 	COMPOSITE_NOMUX(SCLK_MAC, "mac_clk", "mac_clk_ref", 0,
-			RK2928_CLKSEL_CON(21), 9, 5, DFLAGS,
+			RK2928_CLKSEL_CON(21), 4, 5, DFLAGS,
 			RK2928_CLKGATE_CON(2), 6, GFLAGS),
 
 	MUX(SCLK_HDMI, "dclk_hdmi", mux_dclk_p, 0,
-- 
1.7.9.5




From zhengxing at rock-chips.com  Thu Jan  7 04:17:36 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Thu,  7 Jan 2016 20:17:36 +0800
Subject: [PATCH v2 4/5] clk: rockchip: rk3036: rename emac ext source clock
In-Reply-To: <1452169057-24467-1-git-send-email-zhengxing@rock-chips.com>
References: <1452169057-24467-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <1452169057-24467-5-git-send-email-zhengxing@rock-chips.com>

There is only support rmii in the RK3036, so we should use the correct
ext clock name.

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
---

 drivers/clk/rockchip/clk-rk3036.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/clk/rockchip/clk-rk3036.c b/drivers/clk/rockchip/clk-rk3036.c
index 1f00fab..bc7fbac 100644
--- a/drivers/clk/rockchip/clk-rk3036.c
+++ b/drivers/clk/rockchip/clk-rk3036.c
@@ -133,7 +133,7 @@ PNAME(mux_spdif_p)	= { "spdif_src", "spdif_frac", "xin12m" };
 PNAME(mux_uart0_p)	= { "uart0_src", "uart0_frac", "xin24m" };
 PNAME(mux_uart1_p)	= { "uart1_src", "uart1_frac", "xin24m" };
 PNAME(mux_uart2_p)	= { "uart2_src", "uart2_frac", "xin24m" };
-PNAME(mux_mac_p)	= { "mac_pll_src", "ext_gmac" };
+PNAME(mux_mac_p)	= { "mac_pll_src", "rmii_clkin" };
 PNAME(mux_dclk_p)	= { "dclk_lcdc", "dclk_cru" };
 
 static struct rockchip_pll_clock rk3036_pll_clks[] __initdata = {
-- 
1.7.9.5




From zhengxing at rock-chips.com  Thu Jan  7 04:17:33 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Thu,  7 Jan 2016 20:17:33 +0800
Subject: [PATCH v2 1/5] clk: rockchip: rk3036: fix the FLAGs for clock mux
In-Reply-To: <1452169057-24467-1-git-send-email-zhengxing@rock-chips.com>
References: <1452169057-24467-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <1452169057-24467-2-git-send-email-zhengxing@rock-chips.com>

The DFLAGS are used for the clock dividers, the CLKSEL_CON flags
of COMPOSITE_NODIV type should be MFLAGS.

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
---

 drivers/clk/rockchip/clk-rk3036.c |   12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/drivers/clk/rockchip/clk-rk3036.c b/drivers/clk/rockchip/clk-rk3036.c
index ebce980..7e3b41c 100644
--- a/drivers/clk/rockchip/clk-rk3036.c
+++ b/drivers/clk/rockchip/clk-rk3036.c
@@ -224,16 +224,16 @@ static struct rockchip_clk_branch rk3036_clk_branches[] __initdata = {
 			RK2928_CLKGATE_CON(2), 2, GFLAGS),
 
 	COMPOSITE_NODIV(SCLK_TIMER0, "sclk_timer0", mux_timer_p, CLK_IGNORE_UNUSED,
-			RK2928_CLKSEL_CON(2), 4, 1, DFLAGS,
+			RK2928_CLKSEL_CON(2), 4, 1, MFLAGS,
 			RK2928_CLKGATE_CON(1), 0, GFLAGS),
 	COMPOSITE_NODIV(SCLK_TIMER1, "sclk_timer1", mux_timer_p, CLK_IGNORE_UNUSED,
-			RK2928_CLKSEL_CON(2), 5, 1, DFLAGS,
+			RK2928_CLKSEL_CON(2), 5, 1, MFLAGS,
 			RK2928_CLKGATE_CON(1), 1, GFLAGS),
 	COMPOSITE_NODIV(SCLK_TIMER2, "sclk_timer2", mux_timer_p, CLK_IGNORE_UNUSED,
-			RK2928_CLKSEL_CON(2), 6, 1, DFLAGS,
+			RK2928_CLKSEL_CON(2), 6, 1, MFLAGS,
 			RK2928_CLKGATE_CON(2), 4, GFLAGS),
 	COMPOSITE_NODIV(SCLK_TIMER3, "sclk_timer3", mux_timer_p, CLK_IGNORE_UNUSED,
-			RK2928_CLKSEL_CON(2), 7, 1, DFLAGS,
+			RK2928_CLKSEL_CON(2), 7, 1, MFLAGS,
 			RK2928_CLKGATE_CON(2), 5, GFLAGS),
 
 	MUX(0, "uart_pll_clk", mux_pll_src_apll_dpll_gpll_usb480m_p, 0,
@@ -279,13 +279,13 @@ static struct rockchip_clk_branch rk3036_clk_branches[] __initdata = {
 			RK2928_CLKGATE_CON(3), 2, GFLAGS),
 
 	COMPOSITE_NODIV(0, "sclk_sdmmc_src", mux_mmc_src_p, 0,
-			RK2928_CLKSEL_CON(12), 8, 2, DFLAGS,
+			RK2928_CLKSEL_CON(12), 8, 2, MFLAGS,
 			RK2928_CLKGATE_CON(2), 11, GFLAGS),
 	DIV(SCLK_SDMMC, "sclk_sdmmc", "sclk_sdmmc_src", 0,
 			RK2928_CLKSEL_CON(11), 0, 7, DFLAGS),
 
 	COMPOSITE_NODIV(0, "sclk_sdio_src", mux_mmc_src_p, 0,
-			RK2928_CLKSEL_CON(12), 10, 2, DFLAGS,
+			RK2928_CLKSEL_CON(12), 10, 2, MFLAGS,
 			RK2928_CLKGATE_CON(2), 13, GFLAGS),
 	DIV(SCLK_SDIO, "sclk_sdio", "sclk_sdio_src", 0,
 			RK2928_CLKSEL_CON(11), 8, 7, DFLAGS),
-- 
1.7.9.5




From zhengxing at rock-chips.com  Thu Jan  7 04:26:18 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Thu,  7 Jan 2016 20:26:18 +0800
Subject: [PATCH v2 5/5] clk: rockchip: rk3036: add HCLK_MAC id for emac
In-Reply-To: <1452169057-24467-1-git-send-email-zhengxing@rock-chips.com>
References: <1452169057-24467-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <1452169578-24565-1-git-send-email-zhengxing@rock-chips.com>

We need to add HCLK_MAC id explicitly because that it is referred
by hclk in the emac driver.

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
---

 drivers/clk/rockchip/clk-rk3036.c      |    2 +-
 include/dt-bindings/clock/rk3036-cru.h |    1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/rockchip/clk-rk3036.c b/drivers/clk/rockchip/clk-rk3036.c
index bc7fbac..15cd29a 100644
--- a/drivers/clk/rockchip/clk-rk3036.c
+++ b/drivers/clk/rockchip/clk-rk3036.c
@@ -404,7 +404,7 @@ static struct rockchip_clk_branch rk3036_clk_branches[] __initdata = {
 	GATE(HCLK_OTG1, "hclk_otg1", "hclk_peri", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(7), 3, GFLAGS),
 	GATE(HCLK_I2S, "hclk_i2s", "hclk_peri", 0, RK2928_CLKGATE_CON(7), 2, GFLAGS),
 	GATE(0, "hclk_sfc", "hclk_peri", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(3), 14, GFLAGS),
-	GATE(0, "hclk_mac", "hclk_peri", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(3), 15, GFLAGS),
+	GATE(HCLK_MAC, "hclk_mac", "hclk_peri", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(3), 15, GFLAGS),
 
 	/* pclk_peri gates */
 	GATE(0, "pclk_peri_matrix", "pclk_peri", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(4), 1, GFLAGS),
diff --git a/include/dt-bindings/clock/rk3036-cru.h b/include/dt-bindings/clock/rk3036-cru.h
index ebc7a7b..3396591 100644
--- a/include/dt-bindings/clock/rk3036-cru.h
+++ b/include/dt-bindings/clock/rk3036-cru.h
@@ -92,6 +92,7 @@
 #define HCLK_SDMMC		456
 #define HCLK_SDIO		457
 #define HCLK_EMMC		459
+#define HCLK_MAC		460
 #define HCLK_I2S		462
 #define HCLK_LCDC		465
 #define HCLK_ROM		467
-- 
1.7.9.5




From p.zabel at pengutronix.de  Thu Jan  7 08:50:24 2016
From: p.zabel at pengutronix.de (Philipp Zabel)
Date: Thu, 07 Jan 2016 17:50:24 +0100
Subject: [RFC PATCH v2 3/4] drm: rockchip: hdmi: add RK3229 HDMI support
In-Reply-To: <568E3AD0.1020904@rock-chips.com>
References: <1452156811-18150-1-git-send-email-ykk@rock-chips.com>
 <1452157373-18783-1-git-send-email-ykk@rock-chips.com>
 <1452161052.4776.7.camel@pengutronix.de> <568E3AD0.1020904@rock-chips.com>
Message-ID: <1452185424.4776.36.camel@pengutronix.de>

Hi Yakir,

Am Donnerstag, den 07.01.2016, 18:15 +0800 schrieb Yakir Yang:
> Hi Philipp,
> 
> Thanks for your fast respond :)
> 
> On 01/07/2016 06:04 PM, Philipp Zabel wrote:
> > Am Donnerstag, den 07.01.2016, 17:02 +0800 schrieb Yakir Yang:
> >> RK3229 integrate an DesignedWare HDMI2.0 controller and an INNO HDMI2.0 phy,
> >> the max output resolution is 4K.
> >>
> >> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
> > It sounds like the INNO HDMI2.0 phy is not necessarily specific to
> > RK3229 but might also appear in other SoCs? If so, I think this should
> > be implemented in a separate phy driver and be used by dw_hdmi-rockchip.
> 
> Do you mean I should create a new phy driver that place in "driver/phy" 
> directly ?

Possibly, yes. The exynos video phys are already there. I have kept the
mediatek dsi/hdmi phys together with the DRM driver, but I suppose I
could move them there, too.

> I have think about this idea, and it would make things much clean. But 
> INNO PHY
> driver need the target pixel clock in drm_display_mode, I didn't find a 
> good way
> to pass this variable to separate phy driver. Do you have some idea ?

We'd need to extend the PHY API for this. For the mediatek phys we have
side-stepped the issue by wiring up the PLL output to the common clock
framework.
I expect besides the pixel clock frequency, it might also be necessary
to inform the PHY about cycles per pixel for deep color modes.

regards
Philipp



From f.fainelli at gmail.com  Thu Jan  7 11:45:54 2016
From: f.fainelli at gmail.com (Florian Fainelli)
Date: Thu, 07 Jan 2016 11:45:54 -0800
Subject: [PATCH] stmmac: Don't exit mdio registration when mdio subnode
 is not found in the DTS
In-Reply-To: <CABgxDo+EOVW-RJ1WVY+oHt1tbW7qq9Q_+wK3ywiexh+auEebaA@mail.gmail.com>
References: <1451397935-23643-1-git-send-email-romain.perier@gmail.com>	<0E6F98A1-B9F1-48B0-B5CA-A215803E827F@gmail.com>	<CABgxDoLNWEtYtRTrYhp7T=tk3n-aPMCgOP2SL7+DoxFqTk7GuQ@mail.gmail.com>	<56880E46.4050206@gmail.com>
 <CABgxDo+EOVW-RJ1WVY+oHt1tbW7qq9Q_+wK3ywiexh+auEebaA@mail.gmail.com>
Message-ID: <568EC072.6050803@gmail.com>

On 03/01/16 06:36, Romain Perier wrote:
> Hi,
> 
> 2016-01-02 18:52 GMT+01:00 Florian Fainelli <f.fainelli at gmail.com>:
>>
>>>> This looks reasonable, does it work if we just assign mdio_node to the pdev->dev.of_node to be backwards compatible with DTSes which have not yet been updated?
>>>>
>>>
>>> I don't understand what you mean. Instead of defining mdio_node with
>>> NULL, you propose to assign it to pdev->dev.of_node as its default
>>> value... then it would be reassigned with "child_nod" if a subnode
>>> with the compatible string "snps,dwmac-mdio" is found, or kept with
>>> its default value otherwise... and in this case we could call
>>> of_mdiobus_register(new_bus, mdio_node) all the time ? (it removes the
>>> conditonnal branch)
>>
>> Your understanding is correct, this is what I was suggesting, thanks
> 
> So in this case, I already tested this yesterday, it does not work,
> basically because the for loop just after of_mdiobus_register is never
> executed, probably because new_bus->phy_map is not populated, which is
> not the case by calling mdiobus_register for example.
> of_mdiobus_register seems to populate this array of phys only for
> subnodes found in the devicetree...

Fair enough, then you initial patch with dropping IS_ENABLED() and
adding a "Fixes" tag should be resubmitted.

Thank you
-- 
Florian


From romain.perier at gmail.com  Thu Jan  7 12:13:28 2016
From: romain.perier at gmail.com (Romain Perier)
Date: Thu,  7 Jan 2016 21:13:28 +0100
Subject: [PATCH v2] stmmac: Don't exit mdio registration when mdio subnode is
 not found in the DTS
Message-ID: <1452197608-29060-1-git-send-email-romain.perier@gmail.com>

Originally, most of the platforms using this driver did not define an mdio subnode
in the devicetree. Commit e34d65 ("stmmac: create of compatible mdio bus for stmmac driver")
introduced a backward compatibily issue by using of_mdiobus_register explicitly
with an mdio subnode. This patch fixes the issue by calling the function
mdiobus_register, when mdio subnode is not found. The driver is now compatible
with both modes.

Fixes: e34d65696d2e ("stmmac: create of compatible mdio bus for stmmac driver")
Signed-off-by: Romain Perier <romain.perier at gmail.com>
---

Changes in v2:
	- Removed IS_ENABLED macro from the if statement before calling
	  of_mdiobus_register.
	- Added Fixes tag in commit message

 drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
index 16c85cc..648488d 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
@@ -218,8 +218,7 @@ int stmmac_mdio_register(struct net_device *ndev)
 		if (mdio_node) {
 			netdev_dbg(ndev, "FOUND MDIO subnode\n");
 		} else {
-			netdev_err(ndev, "NO MDIO subnode\n");
-			return 0;
+			netdev_warn(ndev, "No MDIO subnode found\n");
 		}
 	}
 
@@ -251,7 +250,10 @@ int stmmac_mdio_register(struct net_device *ndev)
 	new_bus->phy_mask = mdio_bus_data->phy_mask;
 	new_bus->parent = priv->device;
 
-	err = of_mdiobus_register(new_bus, mdio_node);
+	if (mdio_node)
+		err = of_mdiobus_register(new_bus, mdio_node);
+	else
+		err = mdiobus_register(new_bus);
 	if (err != 0) {
 		pr_err("%s: Cannot register as MDIO bus\n", new_bus->name);
 		goto bus_register_fail;
-- 
2.5.0



From preid at electromag.com.au  Thu Jan  7 17:00:09 2016
From: preid at electromag.com.au (Phil Reid)
Date: Fri, 8 Jan 2016 09:00:09 +0800
Subject: [PATCH] stmmac: Don't exit mdio registration when mdio subnode is
 not found in the DTS
In-Reply-To: <568EC072.6050803@gmail.com>
References: <1451397935-23643-1-git-send-email-romain.perier@gmail.com>
 <0E6F98A1-B9F1-48B0-B5CA-A215803E827F@gmail.com>
 <CABgxDoLNWEtYtRTrYhp7T=tk3n-aPMCgOP2SL7+DoxFqTk7GuQ@mail.gmail.com>
 <56880E46.4050206@gmail.com>
 <CABgxDo+EOVW-RJ1WVY+oHt1tbW7qq9Q_+wK3ywiexh+auEebaA@mail.gmail.com>
 <568EC072.6050803@gmail.com>
Message-ID: <568F0A19.7010208@electromag.com.au>

On 8/01/2016 3:45 AM, Florian Fainelli wrote:
> On 03/01/16 06:36, Romain Perier wrote:
>> Hi,
>>
>> 2016-01-02 18:52 GMT+01:00 Florian Fainelli <f.fainelli at gmail.com>:
>>>
>>>>> This looks reasonable, does it work if we just assign mdio_node to the pdev->dev.of_node to be backwards compatible with DTSes which have not yet been updated?
>>>>>
>>>>
>>>> I don't understand what you mean. Instead of defining mdio_node with
>>>> NULL, you propose to assign it to pdev->dev.of_node as its default
>>>> value... then it would be reassigned with "child_nod" if a subnode
>>>> with the compatible string "snps,dwmac-mdio" is found, or kept with
>>>> its default value otherwise... and in this case we could call
>>>> of_mdiobus_register(new_bus, mdio_node) all the time ? (it removes the
>>>> conditonnal branch)
>>>
>>> Your understanding is correct, this is what I was suggesting, thanks
>>
>> So in this case, I already tested this yesterday, it does not work,
>> basically because the for loop just after of_mdiobus_register is never
>> executed, probably because new_bus->phy_map is not populated, which is
>> not the case by calling mdiobus_register for example.
>> of_mdiobus_register seems to populate this array of phys only for
>> subnodes found in the devicetree...
>
> Fair enough, then you initial patch with dropping IS_ENABLED() and
> adding a "Fixes" tag should be resubmitted.
>
Looks reasonable.



-- 
Regards
Phil Reid



From zhengxing at rock-chips.com  Thu Jan  7 17:34:59 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Fri,  8 Jan 2016 09:34:59 +0800
Subject: [PATCH v2 0/3] Add support emac for the RK3036 SoC platform
Message-ID: <1452216902-21063-1-git-send-email-zhengxing@rock-chips.com>


Hi,
  We have supported the emac for RK3066/RK3188, but the RK3036 have
some configuration different with them. We should let the driver of
emac_rockchip compatible with other Rockchip SoCs.

Changes in v2:
- Separate DTS from patch series.

Xing Zheng (3):
  net: ethernet: arc: Probe emac after set RMII clock
  net: ethernet: arc: Keep emac compatibility for more Rockchip SoCs
  net: ethernet: arc: Add support emac for RK3036

 drivers/net/ethernet/arc/Kconfig         |    4 +-
 drivers/net/ethernet/arc/emac_rockchip.c |   88 ++++++++++++++++++++----------
 2 files changed, 61 insertions(+), 31 deletions(-)

-- 
1.7.9.5




From zhengxing at rock-chips.com  Thu Jan  7 17:35:00 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Fri,  8 Jan 2016 09:35:00 +0800
Subject: [PATCH v2 1/3] net: ethernet: arc: Probe emac after set RMII clock
In-Reply-To: <1452216902-21063-1-git-send-email-zhengxing@rock-chips.com>
References: <1452216902-21063-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <1452216902-21063-2-git-send-email-zhengxing@rock-chips.com>

After enter arc_emac_probe, emac will get_phy_id, phy_poll_reset and
other connecting PHY via mdiobus_read, so we need to set correct
ref clock rate for emac before probe emac.

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
---

Changes in v2: None

 drivers/net/ethernet/arc/emac_rockchip.c |   11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ethernet/arc/emac_rockchip.c b/drivers/net/ethernet/arc/emac_rockchip.c
index c31c740..36e9eb1 100644
--- a/drivers/net/ethernet/arc/emac_rockchip.c
+++ b/drivers/net/ethernet/arc/emac_rockchip.c
@@ -164,10 +164,6 @@ static int emac_rockchip_probe(struct platform_device *pdev)
 		}
 	}
 
-	err = arc_emac_probe(ndev, interface);
-	if (err)
-		goto out_regulator_disable;
-
 	/* write-enable bits */
 	data = GRF_MODE_ENABLE_BIT | GRF_SPEED_ENABLE_BIT;
 
@@ -184,6 +180,13 @@ static int emac_rockchip_probe(struct platform_device *pdev)
 	err = clk_set_rate(priv->refclk, 50000000);
 	if (err)
 		dev_err(dev, "failed to change reference clock rate (%d)\n", err);
+
+	err = arc_emac_probe(ndev, interface);
+	if (err) {
+		dev_err(dev, "failed to probe arc emac (%d)\n", err);
+		goto out_regulator_disable;
+	}
+
 	return 0;
 
 out_regulator_disable:
-- 
1.7.9.5




From zhengxing at rock-chips.com  Thu Jan  7 17:35:01 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Fri,  8 Jan 2016 09:35:01 +0800
Subject: [PATCH v2 2/3] net: ethernet: arc: Keep emac compatibility for more
 Rockchip SoCs
In-Reply-To: <1452216902-21063-1-git-send-email-zhengxing@rock-chips.com>
References: <1452216902-21063-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <1452216902-21063-3-git-send-email-zhengxing@rock-chips.com>

On the RK3066/RK3188, there was fixed GRF offset configuration to set emac
and fixed DIV2 mac TX/RX clock. So, we need to easily set and fit to other
SoCs (RK3036) which maybe have different GRF offset, and need adjust mac
TX/RX clock.

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
---

Changes in v2: None

 drivers/net/ethernet/arc/emac_rockchip.c |   69 +++++++++++++++++++-----------
 1 file changed, 45 insertions(+), 24 deletions(-)

diff --git a/drivers/net/ethernet/arc/emac_rockchip.c b/drivers/net/ethernet/arc/emac_rockchip.c
index 36e9eb1..e951c8f 100644
--- a/drivers/net/ethernet/arc/emac_rockchip.c
+++ b/drivers/net/ethernet/arc/emac_rockchip.c
@@ -25,17 +25,13 @@
 #include "emac.h"
 
 #define DRV_NAME        "rockchip_emac"
-#define DRV_VERSION     "1.0"
-
-#define GRF_MODE_MII		(1UL << 0)
-#define GRF_MODE_RMII		(0UL << 0)
-#define GRF_SPEED_10M		(0UL << 1)
-#define GRF_SPEED_100M		(1UL << 1)
-#define GRF_SPEED_ENABLE_BIT	(1UL << 17)
-#define GRF_MODE_ENABLE_BIT	(1UL << 16)
+#define DRV_VERSION     "1.1"
 
 struct emac_rockchip_soc_data {
-	int grf_offset;
+	unsigned int grf_offset;
+	unsigned int grf_mode_offset;
+	unsigned int grf_speed_offset;
+	bool need_div_macclk;
 };
 
 struct rockchip_priv_data {
@@ -44,23 +40,22 @@ struct rockchip_priv_data {
 	const struct emac_rockchip_soc_data *soc_data;
 	struct regulator *regulator;
 	struct clk *refclk;
+	struct clk *macclk;
 };
 
 static void emac_rockchip_set_mac_speed(void *priv, unsigned int speed)
 {
 	struct rockchip_priv_data *emac = priv;
+	u32 speed_offset = emac->soc_data->grf_speed_offset;
 	u32 data;
 	int err = 0;
 
-	/* write-enable bits */
-	data = GRF_SPEED_ENABLE_BIT;
-
 	switch(speed) {
 	case 10:
-		data |= GRF_SPEED_10M;
+		data = (1 << (speed_offset + 16)) | (0 << speed_offset);
 		break;
 	case 100:
-		data |= GRF_SPEED_100M;
+		data = (1 << (speed_offset + 16)) | (1 << speed_offset);
 		break;
 	default:
 		pr_err("speed %u not supported\n", speed);
@@ -72,14 +67,19 @@ static void emac_rockchip_set_mac_speed(void *priv, unsigned int speed)
 		pr_err("unable to apply speed %u to grf (%d)\n", speed, err);
 }
 
-static const struct emac_rockchip_soc_data emac_rockchip_dt_data[] = {
-	{ .grf_offset = 0x154 }, /* rk3066 */
-	{ .grf_offset = 0x0a4 }, /* rk3188 */
+static const struct emac_rockchip_soc_data emac_rk3066_emac_data = {
+	.grf_offset = 0x154,   .grf_mode_offset = 0,
+	.grf_speed_offset = 1, .need_div_macclk = 0,
+};
+
+static const struct emac_rockchip_soc_data emac_rk3188_emac_data = {
+	.grf_offset = 0x0a4,   .grf_mode_offset = 0,
+	.grf_speed_offset = 1, .need_div_macclk = 0,
 };
 
 static const struct of_device_id emac_rockchip_dt_ids[] = {
-	{ .compatible = "rockchip,rk3066-emac", .data = &emac_rockchip_dt_data[0] },
-	{ .compatible = "rockchip,rk3188-emac", .data = &emac_rockchip_dt_data[1] },
+	{ .compatible = "rockchip,rk3066-emac", .data = &emac_rk3066_emac_data },
+	{ .compatible = "rockchip,rk3188-emac", .data = &emac_rk3188_emac_data },
 	{ /* Sentinel */ }
 };
 
@@ -164,11 +164,12 @@ static int emac_rockchip_probe(struct platform_device *pdev)
 		}
 	}
 
-	/* write-enable bits */
-	data = GRF_MODE_ENABLE_BIT | GRF_SPEED_ENABLE_BIT;
-
-	data |= GRF_SPEED_100M;
-	data |= GRF_MODE_RMII;
+	/* Set speed 100M */
+	data = (1 << (priv->soc_data->grf_speed_offset + 16)) |
+	       (1 << priv->soc_data->grf_speed_offset);
+	/* Set RMII mode */
+	data |= (1 << (priv->soc_data->grf_mode_offset + 16)) |
+		(0 << priv->soc_data->grf_mode_offset);
 
 	err = regmap_write(priv->grf, priv->soc_data->grf_offset, data);
 	if (err) {
@@ -181,6 +182,26 @@ static int emac_rockchip_probe(struct platform_device *pdev)
 	if (err)
 		dev_err(dev, "failed to change reference clock rate (%d)\n", err);
 
+	if (priv->soc_data->need_div_macclk) {
+		priv->macclk = devm_clk_get(dev, "macclk");
+		if (IS_ERR(priv->macclk)) {
+			dev_err(dev, "failed to retrieve mac clock (%ld)\n", PTR_ERR(priv->macclk));
+			err = PTR_ERR(priv->macclk);
+			goto out_regulator_disable;
+		}
+
+		err = clk_prepare_enable(priv->macclk);
+		if (err) {
+			dev_err(dev, "failed to enable mac clock (%d)\n", err);
+			goto out_regulator_disable;
+		}
+
+		/* RMII TX/RX needs always a rate of 25MHz */
+		err = clk_set_rate(priv->macclk, 25000000);
+		if (err)
+			dev_err(dev, "failed to change mac clock rate (%d)\n", err);
+	}
+
 	err = arc_emac_probe(ndev, interface);
 	if (err) {
 		dev_err(dev, "failed to probe arc emac (%d)\n", err);
-- 
1.7.9.5




From zhengxing at rock-chips.com  Thu Jan  7 17:35:02 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Fri,  8 Jan 2016 09:35:02 +0800
Subject: [PATCH v2 3/3] net: ethernet: arc: Add support emac for RK3036
In-Reply-To: <1452216902-21063-1-git-send-email-zhengxing@rock-chips.com>
References: <1452216902-21063-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <1452216902-21063-4-git-send-email-zhengxing@rock-chips.com>

The RK3036's GRFs offset are different with RK3066/RK3188, and need to set
mac TX/RX clock before probe emac.

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
---

Changes in v2:
- Separate DTS from patch series.

 drivers/net/ethernet/arc/Kconfig         |    4 ++--
 drivers/net/ethernet/arc/emac_rockchip.c |    8 +++++++-
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/drivers/net/ethernet/arc/Kconfig b/drivers/net/ethernet/arc/Kconfig
index 52a6b16..6890451 100644
--- a/drivers/net/ethernet/arc/Kconfig
+++ b/drivers/net/ethernet/arc/Kconfig
@@ -34,9 +34,9 @@ config EMAC_ROCKCHIP
 	select ARC_EMAC_CORE
 	depends on OF_IRQ && OF_NET && REGULATOR && HAS_DMA
 	---help---
-	  Support for Rockchip RK3066/RK3188 EMAC ethernet controllers.
+	  Support for Rockchip RK3036/RK3066/RK3188 EMAC ethernet controllers.
 	  This selects Rockchip SoC glue layer support for the
-	  emac device driver. This driver is used for RK3066/RK3188
+	  emac device driver. This driver is used for RK3036/RK3066/RK3188
 	  EMAC ethernet controller.
 
 endif # NET_VENDOR_ARC
diff --git a/drivers/net/ethernet/arc/emac_rockchip.c b/drivers/net/ethernet/arc/emac_rockchip.c
index e951c8f..85e821c 100644
--- a/drivers/net/ethernet/arc/emac_rockchip.c
+++ b/drivers/net/ethernet/arc/emac_rockchip.c
@@ -67,6 +67,11 @@ static void emac_rockchip_set_mac_speed(void *priv, unsigned int speed)
 		pr_err("unable to apply speed %u to grf (%d)\n", speed, err);
 }
 
+static const struct emac_rockchip_soc_data emac_rk3036_emac_data = {
+	.grf_offset = 0x140,   .grf_mode_offset = 8,
+	.grf_speed_offset = 9, .need_div_macclk = 1,
+};
+
 static const struct emac_rockchip_soc_data emac_rk3066_emac_data = {
 	.grf_offset = 0x154,   .grf_mode_offset = 0,
 	.grf_speed_offset = 1, .need_div_macclk = 0,
@@ -78,6 +83,7 @@ static const struct emac_rockchip_soc_data emac_rk3188_emac_data = {
 };
 
 static const struct of_device_id emac_rockchip_dt_ids[] = {
+	{ .compatible = "rockchip,rk3036-emac", .data = &emac_rk3036_emac_data },
 	{ .compatible = "rockchip,rk3066-emac", .data = &emac_rk3066_emac_data },
 	{ .compatible = "rockchip,rk3188-emac", .data = &emac_rk3188_emac_data },
 	{ /* Sentinel */ }
@@ -110,7 +116,7 @@ static int emac_rockchip_probe(struct platform_device *pdev)
 
 	interface = of_get_phy_mode(dev->of_node);
 
-	/* RK3066 and RK3188 SoCs only support RMII */
+	/* RK3036/RK3066/RK3188 SoCs only support RMII */
 	if (interface != PHY_INTERFACE_MODE_RMII) {
 		dev_err(dev, "unsupported phy interface mode %d\n", interface);
 		err = -ENOTSUPP;
-- 
1.7.9.5




From zhengxing at rock-chips.com  Thu Jan  7 17:37:06 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Fri,  8 Jan 2016 09:37:06 +0800
Subject: [PATCH] ARM: dts: rockchip: Add support emac for RK3036
Message-ID: <1452217026-21155-1-git-send-email-zhengxing@rock-chips.com>

This patch describe the emac, and we need to let mac clock under
the APLL which is able to provide the accurate 50MHz what mac_ref
need.

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
---

 arch/arm/boot/dts/rk3036-evb.dts   |   23 +++++++++++++++++++++++
 arch/arm/boot/dts/rk3036-kylin.dts |   21 +++++++++++++++++++++
 arch/arm/boot/dts/rk3036.dtsi      |   32 ++++++++++++++++++++++++++++++++
 3 files changed, 76 insertions(+)

diff --git a/arch/arm/boot/dts/rk3036-evb.dts b/arch/arm/boot/dts/rk3036-evb.dts
index 0a2367e..2505b9a 100644
--- a/arch/arm/boot/dts/rk3036-evb.dts
+++ b/arch/arm/boot/dts/rk3036-evb.dts
@@ -74,3 +74,26 @@
 &uart2 {
 	status = "okay";
 };
+
+&emac {
+	pinctrl-names = "default";
+	pinctrl-0 = <&emac_xfer>, <&emac_mdio>, <&rmii_rst>;
+	phy = <&phy0>;
+	status = "okay";
+
+	phy0: ethernet-phy at 0 {
+		reg = <0>;
+	};
+};
+
+&pinctrl {
+	pcfg_output_high: pcfg-output-high {
+		output-high;
+	};
+
+	emac {
+		rmii_rst: rmii-rst {
+			rockchip,pins = <2 22 RK_FUNC_GPIO &pcfg_output_high>;
+		};
+	};
+};
diff --git a/arch/arm/boot/dts/rk3036-kylin.dts b/arch/arm/boot/dts/rk3036-kylin.dts
index 992f9ca..b6ba852 100644
--- a/arch/arm/boot/dts/rk3036-kylin.dts
+++ b/arch/arm/boot/dts/rk3036-kylin.dts
@@ -285,7 +285,22 @@
 	status = "okay";
 };
 
+&emac {
+	pinctrl-names = "default";
+	pinctrl-0 = <&emac_xfer>, <&emac_mdio>, <&rmii_rst>;
+	phy = <&phy0>;
+	status = "okay";
+
+	phy0: ethernet-phy at 0 {
+		reg = <0>;
+	};
+};
+
 &pinctrl {
+	pcfg_output_high: pcfg-output-high {
+		output-high;
+	};
+
 	pmic {
 		pmic_int: pmic-int {
 			rockchip,pins = <2 2 RK_FUNC_GPIO &pcfg_pull_default>;
@@ -297,4 +312,10 @@
 			rockchip,pins = <2 7 RK_FUNC_1 &pcfg_pull_none>;
 		};
 	};
+
+	emac {
+		rmii_rst: rmii-rst {
+			rockchip,pins = <2 22 RK_FUNC_GPIO &pcfg_output_high>;
+		};
+	};
 };
diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
index b9567c1..1cb5877 100644
--- a/arch/arm/boot/dts/rk3036.dtsi
+++ b/arch/arm/boot/dts/rk3036.dtsi
@@ -186,6 +186,20 @@
 		status = "disabled";
 	};
 
+	emac: ethernet at 10200000 {
+		compatible = "rockchip,rk3036-emac", "snps,arc-emac";
+		reg = <0x10200000 0x4000>;
+		interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		rockchip,grf = <&grf>;
+		clocks = <&cru HCLK_MAC>, <&cru SCLK_MACREF>, <&cru SCLK_MAC>;
+		clock-names = "hclk", "macref", "macclk";
+		max-speed = <100>;
+		phy-mode = "rmii";
+		status = "disabled";
+	};
+
 	sdmmc: dwmmc at 10214000 {
 		compatible = "rockchip,rk3036-dw-mshc", "rockchip,rk3288-dw-mshc";
 		reg = <0x10214000 0x4000>;
@@ -556,6 +570,24 @@
 			};
 		};
 
+		emac {
+			emac_xfer: emac-xfer {
+				rockchip,pins = <2 10 RK_FUNC_1 &pcfg_pull_default>, /* crs_dvalid */
+						<2 13 RK_FUNC_1 &pcfg_pull_default>, /* tx_en */
+						<2 14 RK_FUNC_1 &pcfg_pull_default>, /* mac_clk */
+						<2 15 RK_FUNC_1 &pcfg_pull_default>, /* rx_err */
+						<2 16 RK_FUNC_1 &pcfg_pull_default>, /* rxd1 */
+						<2 17 RK_FUNC_1 &pcfg_pull_default>, /* rxd0 */
+						<2 18 RK_FUNC_1 &pcfg_pull_default>, /* txd1 */
+						<2 19 RK_FUNC_1 &pcfg_pull_default>; /* txd0 */
+			};
+
+			emac_mdio: emac-mdio {
+				rockchip,pins = <2 12 RK_FUNC_1 &pcfg_pull_default>, /* mac_md */
+						<2 25 RK_FUNC_1 &pcfg_pull_default>; /* mac_mdclk */
+			};
+		};
+
 		i2c0 {
 			i2c0_xfer: i2c0-xfer {
 				rockchip,pins = <0 0 RK_FUNC_1 &pcfg_pull_none>,
-- 
1.7.9.5




From sjg at chromium.org  Thu Jan  7 19:33:58 2016
From: sjg at chromium.org (Simon Glass)
Date: Thu, 7 Jan 2016 20:33:58 -0700
Subject: [PATCH v1 1/6] common/image-fdt.c: Make boot_get_fdt() perform a
 check for Android images
In-Reply-To: <1451893823-20309-2-git-send-email-jeffy.chen@rock-chips.com>
References: <1451893823-20309-1-git-send-email-jeffy.chen@rock-chips.com>
 <1451893823-20309-2-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <CAPnjgZ23onM40qLaOZ7H07j=LeYLbqX5w0CXh7XC15UJk2VUtw@mail.gmail.com>

On 4 January 2016 at 00:50, Jeffy Chen <jeffy.chen at rock-chips.com> wrote:
> Android images don't have a fdt.
>
> Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
> ---
>
>  common/image-fdt.c | 4 ++++
>  1 file changed, 4 insertions(+)

Acked-by: Simon Glass <sjg at chromium.org>


From sjg at chromium.org  Thu Jan  7 19:34:01 2016
From: sjg at chromium.org (Simon Glass)
Date: Thu, 7 Jan 2016 20:34:01 -0700
Subject: [PATCH v1 2/6] ARM: bootm: Try to use relocated ramdisk
In-Reply-To: <1451893823-20309-3-git-send-email-jeffy.chen@rock-chips.com>
References: <1451893823-20309-1-git-send-email-jeffy.chen@rock-chips.com>
 <1451893823-20309-3-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <CAPnjgZ0GMwkHesVSYzNYfp0O8TifZgQbC71LYWjMqEHDEn3OEQ@mail.gmail.com>

Hi Jeffy,

On 4 January 2016 at 00:50, Jeffy Chen <jeffy.chen at rock-chips.com> wrote:
> After boot_ramdisk_high(), ramdisk would be relocated to
> initrd_start & initrd_end, so use them instead of rd_start & rd_end.
>
> Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
> ---
>
>  arch/arm/lib/bootm.c | 5 ++++-
>  1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c
> index a477cae..ac94995 100644
> --- a/arch/arm/lib/bootm.c
> +++ b/arch/arm/lib/bootm.c
> @@ -225,7 +225,10 @@ static void boot_prep_linux(bootm_headers_t *images)
>                 if (BOOTM_ENABLE_MEMORY_TAGS)
>                         setup_memory_tags(gd->bd);
>                 if (BOOTM_ENABLE_INITRD_TAG) {
> -                       if (images->rd_start && images->rd_end) {
> +                       if (images->initrd_start && images->initrd_end) {
> +                               setup_initrd_tag(gd->bd, images->initrd_start,
> +                                                images->initrd_end);
> +                       } else if (images->rd_start && images->rd_end) {

Can you please add comments to these members (initrd_start/end) in
image.h? At present it is not clear what they are, compared to
rd_start/end.

>                                 setup_initrd_tag(gd->bd, images->rd_start,
>                                                  images->rd_end);
>                         }
> --
> 2.1.4
>
>

Regards,
Simon


From sjg at chromium.org  Thu Jan  7 19:34:04 2016
From: sjg at chromium.org (Simon Glass)
Date: Thu, 7 Jan 2016 20:34:04 -0700
Subject: [PATCH v1 3/6] rockchip: rk3036: Bind GPIO banks
In-Reply-To: <1451893823-20309-4-git-send-email-jeffy.chen@rock-chips.com>
References: <1451893823-20309-1-git-send-email-jeffy.chen@rock-chips.com>
 <1451893823-20309-4-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <CAPnjgZ3_GVtZ7Wt71t_rjAMAemXVsTD40LWAKP9Fo52+HNpZBg@mail.gmail.com>

On 4 January 2016 at 00:50, Jeffy Chen <jeffy.chen at rock-chips.com> wrote:
> Call dm_scan_fdt_node() in rk3036 pinctrl uclass binding.
>
> Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
> ---
>
>  drivers/pinctrl/rockchip/pinctrl_rk3036.c | 8 ++++++++
>  1 file changed, 8 insertions(+)

Acked-by: Simon Glass <sjg at chromium.org>


From sjg at chromium.org  Thu Jan  7 19:34:06 2016
From: sjg at chromium.org (Simon Glass)
Date: Thu, 7 Jan 2016 20:34:06 -0700
Subject: [PATCH v1 4/6] rockchip: kylin: Add default gpt partition table
In-Reply-To: <1451893823-20309-5-git-send-email-jeffy.chen@rock-chips.com>
References: <1451893823-20309-1-git-send-email-jeffy.chen@rock-chips.com>
 <1451893823-20309-5-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <CAPnjgZ22Ardxpz1JCZPHt6BVNca0QbPURu2ykiODbckEoL2dPQ@mail.gmail.com>

On 4 January 2016 at 00:50, Jeffy Chen <jeffy.chen at rock-chips.com> wrote:
> Add default android gpt partition table for kylin board.
>
> Use "gpt write mmc 0 $partitions" to apply.
>
> Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
> ---
>
>  include/configs/kylin_rk3036.h | 29 +++++++++++++++++++++++++++++
>  1 file changed, 29 insertions(+)

Acked-by: Simon Glass <sjg at chromium.org>


From sjg at chromium.org  Thu Jan  7 19:34:09 2016
From: sjg at chromium.org (Simon Glass)
Date: Thu, 7 Jan 2016 20:34:09 -0700
Subject: [PATCH v1 5/6] rockchip: kylin: Enable boot with android boot
 image
In-Reply-To: <1451893823-20309-6-git-send-email-jeffy.chen@rock-chips.com>
References: <1451893823-20309-1-git-send-email-jeffy.chen@rock-chips.com>
 <1451893823-20309-6-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <CAPnjgZ1G=5=gqy6yGV1NZ13Zv0BACRYMPpdAoFAi+sv2TPXknw@mail.gmail.com>

Hi Jeffy,

On 4 January 2016 at 00:50, Jeffy Chen <jeffy.chen at rock-chips.com> wrote:
> The android kernel is using appended dtb by default, and store
> ramdisk right after kernel & dtb.
> So we needs to relocate ramdisk, and use atags to pass params.
>
> Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
> ---
>
>  include/configs/kylin_rk3036.h | 23 +++++++++++++++++++++++
>  1 file changed, 23 insertions(+)

Acked-by: Simon Glass <sjg at chromium.org>

What kernel version are you using?

Regards,
Simon


From sjg at chromium.org  Thu Jan  7 19:34:12 2016
From: sjg at chromium.org (Simon Glass)
Date: Thu, 7 Jan 2016 20:34:12 -0700
Subject: [PATCH v1 6/6] rockchip: kylin: Check fastboot request
In-Reply-To: <1451893823-20309-7-git-send-email-jeffy.chen@rock-chips.com>
References: <1451893823-20309-1-git-send-email-jeffy.chen@rock-chips.com>
 <1451893823-20309-7-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <CAPnjgZ0oUT0zULMeoiLNFBmh-sFebRmjXN=nXsYhEX4qUr1jgw@mail.gmail.com>

On 4 January 2016 at 00:50, Jeffy Chen <jeffy.chen at rock-chips.com> wrote:
> We will save boot mode flag in grf's os_reg[0], if fastboot
> requested or fastboot key pressed, try to enter fastboot mode
> at preboot stage.
>
> Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
> ---
>
>  board/kylin/kylin_rk3036/kylin_rk3036.c | 32 ++++++++++++++++++++++++++++++++
>  include/configs/kylin_rk3036.h          |  3 +++
>  2 files changed, 35 insertions(+)

Acked-by: Simon Glass <sjg at chromium.org>


From preid at electromag.com.au  Fri Jan  8 00:04:58 2016
From: preid at electromag.com.au (Phil Reid)
Date: Fri, 8 Jan 2016 16:04:58 +0800
Subject: [PATCH v2] stmmac: Don't exit mdio registration when mdio subnode
 is not found in the DTS
In-Reply-To: <1452197608-29060-1-git-send-email-romain.perier@gmail.com>
References: <1452197608-29060-1-git-send-email-romain.perier@gmail.com>
Message-ID: <568F6DAA.9000205@electromag.com.au>

On 8/01/2016 4:13 AM, Romain Perier wrote:
> Originally, most of the platforms using this driver did not define an mdio subnode
> in the devicetree. Commit e34d65 ("stmmac: create of compatible mdio bus for stmmac driver")
> introduced a backward compatibily issue by using of_mdiobus_register explicitly
> with an mdio subnode. This patch fixes the issue by calling the function
> mdiobus_register, when mdio subnode is not found. The driver is now compatible
> with both modes.
>
> Fixes: e34d65696d2e ("stmmac: create of compatible mdio bus for stmmac driver")
> Signed-off-by: Romain Perier <romain.perier at gmail.com>
> ---
>
> Changes in v2:
> 	- Removed IS_ENABLED macro from the if statement before calling
> 	  of_mdiobus_register.
> 	- Added Fixes tag in commit message
>
>   drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c | 8 +++++---
>   1 file changed, 5 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
> index 16c85cc..648488d 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
> @@ -218,8 +218,7 @@ int stmmac_mdio_register(struct net_device *ndev)
>   		if (mdio_node) {
>   			netdev_dbg(ndev, "FOUND MDIO subnode\n");
>   		} else {
> -			netdev_err(ndev, "NO MDIO subnode\n");
> -			return 0;
> +			netdev_warn(ndev, "No MDIO subnode found\n");
>   		}
>   	}
>
> @@ -251,7 +250,10 @@ int stmmac_mdio_register(struct net_device *ndev)
>   	new_bus->phy_mask = mdio_bus_data->phy_mask;
>   	new_bus->parent = priv->device;
>
> -	err = of_mdiobus_register(new_bus, mdio_node);
> +	if (mdio_node)
> +		err = of_mdiobus_register(new_bus, mdio_node);
> +	else
> +		err = mdiobus_register(new_bus);
>   	if (err != 0) {
>   		pr_err("%s: Cannot register as MDIO bus\n", new_bus->name);
>   		goto bus_register_fail;
>
Tested-by: Phil Reid <preid at electromag.com.au>

-- 
Regards
Phil Reid



From zhangqing at rock-chips.com  Fri Jan  8 08:05:44 2016
From: zhangqing at rock-chips.com (zhangqing)
Date: Fri,  8 Jan 2016 08:05:44 -0800
Subject: [PATCH] soc: rockchip: power-domain: Modify power domain driver for
 rk3368
Message-ID: <1452269144-15389-1-git-send-email-zhangqing@rock-chips.com>

This driver is modified to support RK3368 SoC.

Signed-off-by: zhangqing <zhangqing at rock-chips.com>
---
 drivers/soc/rockchip/pm_domains.c        | 33 ++++++++++++++++++++++++++++++++
 include/dt-bindings/power/rk3368-power.h | 28 +++++++++++++++++++++++++++
 2 files changed, 61 insertions(+)
 create mode 100644 include/dt-bindings/power/rk3368-power.h

diff --git a/drivers/soc/rockchip/pm_domains.c b/drivers/soc/rockchip/pm_domains.c
index 534c589..6cdffb1 100644
--- a/drivers/soc/rockchip/pm_domains.c
+++ b/drivers/soc/rockchip/pm_domains.c
@@ -18,6 +18,7 @@
 #include <linux/regmap.h>
 #include <linux/mfd/syscon.h>
 #include <dt-bindings/power/rk3288-power.h>
+#include <dt-bindings/power/rk3368-power.h>
 
 struct rockchip_domain_info {
 	int pwr_mask;
@@ -75,6 +76,9 @@ struct rockchip_pmu {
 #define DOMAIN_RK3288(pwr, status, req)		\
 	DOMAIN(pwr, status, req, req, (req) + 16)
 
+#define DOMAIN_RK3368(pwr, status, req)		\
+	DOMAIN(pwr, status, req, (req) + 16, req)
+
 static bool rockchip_pmu_domain_is_idle(struct rockchip_pm_domain *pd)
 {
 	struct rockchip_pmu *pmu = pd->pmu;
@@ -444,6 +448,14 @@ static const struct rockchip_domain_info rk3288_pm_domains[] = {
 	[RK3288_PD_GPU]		= DOMAIN_RK3288(9, 9, 2),
 };
 
+static const struct rockchip_domain_info rk3368_pm_domains[] = {
+	[RK3368_PD_PERI]	= DOMAIN_RK3368(13, 12, 6),
+	[RK3368_PD_VIO]		= DOMAIN_RK3368(15, 14, 8),
+	[RK3368_PD_VIDEO]	= DOMAIN_RK3368(14, 13, 7),
+	[RK3368_PD_GPU_0]	= DOMAIN_RK3368(16, 15, 2),
+	[RK3368_PD_GPU_1]	= DOMAIN_RK3368(17, 16, 2),
+};
+
 static const struct rockchip_pmu_info rk3288_pmu = {
 	.pwr_offset = 0x08,
 	.status_offset = 0x0c,
@@ -461,11 +473,32 @@ static const struct rockchip_pmu_info rk3288_pmu = {
 	.domain_info = rk3288_pm_domains,
 };
 
+static const struct rockchip_pmu_info rk3368_pmu = {
+	.pwr_offset = 0x0c,
+	.status_offset = 0x10,
+	.req_offset = 0x3c,
+	.idle_offset = 0x40,
+	.ack_offset = 0x40,
+
+	.core_pwrcnt_offset = 0x48,
+	.gpu_pwrcnt_offset = 0x50,
+
+	.core_power_transition_time = 24,
+	.gpu_power_transition_time = 24,
+
+	.num_domains = ARRAY_SIZE(rk3368_pm_domains),
+	.domain_info = rk3368_pm_domains,
+};
+
 static const struct of_device_id rockchip_pm_domain_dt_match[] = {
 	{
 		.compatible = "rockchip,rk3288-power-controller",
 		.data = (void *)&rk3288_pmu,
 	},
+	{
+		.compatible = "rockchip,rk3368-power-controller",
+		.data = (void *)&rk3368_pmu,
+	},
 	{ /* sentinel */ },
 };
 
diff --git a/include/dt-bindings/power/rk3368-power.h b/include/dt-bindings/power/rk3368-power.h
new file mode 100644
index 0000000..93633d5
--- /dev/null
+++ b/include/dt-bindings/power/rk3368-power.h
@@ -0,0 +1,28 @@
+#ifndef __DT_BINDINGS_POWER_RK3368_POWER_H__
+#define __DT_BINDINGS_POWER_RK3368_POWER_H__
+
+/* VD_CORE */
+#define RK3368_PD_A53_L0	0
+#define RK3368_PD_A53_L1	1
+#define RK3368_PD_A53_L2	2
+#define RK3368_PD_A53_L3	3
+#define RK3368_PD_SCU_L		4
+#define RK3368_PD_A53_B0	5
+#define RK3368_PD_A53_B1	6
+#define RK3368_PD_A53_B2	7
+#define RK3368_PD_A53_B3	8
+#define RK3368_PD_SCU_B		9
+
+/* VD_LOGIC */
+#define RK3368_PD_BUS		10
+#define RK3368_PD_PERI		11
+#define RK3368_PD_VIO		12
+#define RK3368_PD_ALIVE		13
+#define RK3368_PD_VIDEO		14
+#define RK3368_PD_GPU_0		15
+#define RK3368_PD_GPU_1		16
+
+/* VD_PMU */
+#define RK3368_PD_PMU		17
+
+#endif
-- 
1.9.1




From heiko at sntech.de  Fri Jan  8 00:25:46 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Fri, 08 Jan 2016 09:25:46 +0100
Subject: [PATCH] soc: rockchip: power-domain: Modify power domain driver
 for rk3368
In-Reply-To: <1452269144-15389-1-git-send-email-zhangqing@rock-chips.com>
References: <1452269144-15389-1-git-send-email-zhangqing@rock-chips.com>
Message-ID: <3979294.oVXyhiFhfI@phil>

Hi Elaine,

Am Freitag, 8. Januar 2016, 08:05:44 schrieb zhangqing:
> This driver is modified to support RK3368 SoC.
> 
> Signed-off-by: zhangqing <zhangqing at rock-chips.com>
> ---
>  drivers/soc/rockchip/pm_domains.c        | 33
> ++++++++++++++++++++++++++++++++ include/dt-bindings/power/rk3368-power.h
> | 28 +++++++++++++++++++++++++++ 2 files changed, 61 insertions(+)
>  create mode 100644 include/dt-bindings/power/rk3368-power.h
> 
> diff --git a/drivers/soc/rockchip/pm_domains.c
> b/drivers/soc/rockchip/pm_domains.c index 534c589..6cdffb1 100644
> --- a/drivers/soc/rockchip/pm_domains.c
> +++ b/drivers/soc/rockchip/pm_domains.c
> @@ -18,6 +18,7 @@
>  #include <linux/regmap.h>
>  #include <linux/mfd/syscon.h>
>  #include <dt-bindings/power/rk3288-power.h>
> +#include <dt-bindings/power/rk3368-power.h>
> 
>  struct rockchip_domain_info {
>  	int pwr_mask;
> @@ -75,6 +76,9 @@ struct rockchip_pmu {
>  #define DOMAIN_RK3288(pwr, status, req)		\
>  	DOMAIN(pwr, status, req, req, (req) + 16)
> 
> +#define DOMAIN_RK3368(pwr, status, req)		\
> +	DOMAIN(pwr, status, req, (req) + 16, req)
> +
>  static bool rockchip_pmu_domain_is_idle(struct rockchip_pm_domain *pd)
>  {
>  	struct rockchip_pmu *pmu = pd->pmu;
> @@ -444,6 +448,14 @@ static const struct rockchip_domain_info
> rk3288_pm_domains[] = { [RK3288_PD_GPU]		= DOMAIN_RK3288(9, 9, 2),
>  };
> 
> +static const struct rockchip_domain_info rk3368_pm_domains[] = {
> +	[RK3368_PD_PERI]	= DOMAIN_RK3368(13, 12, 6),
> +	[RK3368_PD_VIO]		= DOMAIN_RK3368(15, 14, 8),
> +	[RK3368_PD_VIDEO]	= DOMAIN_RK3368(14, 13, 7),
> +	[RK3368_PD_GPU_0]	= DOMAIN_RK3368(16, 15, 2),
> +	[RK3368_PD_GPU_1]	= DOMAIN_RK3368(17, 16, 2),
> +};

The structure in the registers (and thus the macros you define above) is the 
same between rk3288 and rk3368, so there is no need to define that a second 
time. It is perfectly fine to re-use the DOMAIN_RK3288 macro here and saves 
double-definitions. [see our clock-drivers which already do this for the 
common parts].

> +
>  static const struct rockchip_pmu_info rk3288_pmu = {
>  	.pwr_offset = 0x08,
>  	.status_offset = 0x0c,
> @@ -461,11 +473,32 @@ static const struct rockchip_pmu_info rk3288_pmu = {
> .domain_info = rk3288_pm_domains,
>  };
> 
> +static const struct rockchip_pmu_info rk3368_pmu = {
> +	.pwr_offset = 0x0c,
> +	.status_offset = 0x10,
> +	.req_offset = 0x3c,
> +	.idle_offset = 0x40,
> +	.ack_offset = 0x40,
> +
> +	.core_pwrcnt_offset = 0x48,
> +	.gpu_pwrcnt_offset = 0x50,
> +
> +	.core_power_transition_time = 24,
> +	.gpu_power_transition_time = 24,
> +
> +	.num_domains = ARRAY_SIZE(rk3368_pm_domains),
> +	.domain_info = rk3368_pm_domains,
> +};
> +
>  static const struct of_device_id rockchip_pm_domain_dt_match[] = {
>  	{
>  		.compatible = "rockchip,rk3288-power-controller",
>  		.data = (void *)&rk3288_pmu,
>  	},
> +	{
> +		.compatible = "rockchip,rk3368-power-controller",
> +		.data = (void *)&rk3368_pmu,
> +	},
>  	{ /* sentinel */ },
>  };
> 
> diff --git a/include/dt-bindings/power/rk3368-power.h
> b/include/dt-bindings/power/rk3368-power.h new file mode 100644
> index 0000000..93633d5
> --- /dev/null
> +++ b/include/dt-bindings/power/rk3368-power.h

Please split the addition of the binding-header into a separate patch. We 
will need the header in both the soc-specific code as well as the devicetree 
side, which requires the header being in a shared branch.

Otherwise patch looks great.


Thanks
Heiko

> @@ -0,0 +1,28 @@
> +#ifndef __DT_BINDINGS_POWER_RK3368_POWER_H__
> +#define __DT_BINDINGS_POWER_RK3368_POWER_H__
> +
> +/* VD_CORE */
> +#define RK3368_PD_A53_L0	0
> +#define RK3368_PD_A53_L1	1
> +#define RK3368_PD_A53_L2	2
> +#define RK3368_PD_A53_L3	3
> +#define RK3368_PD_SCU_L		4
> +#define RK3368_PD_A53_B0	5
> +#define RK3368_PD_A53_B1	6
> +#define RK3368_PD_A53_B2	7
> +#define RK3368_PD_A53_B3	8
> +#define RK3368_PD_SCU_B		9
> +
> +/* VD_LOGIC */
> +#define RK3368_PD_BUS		10
> +#define RK3368_PD_PERI		11
> +#define RK3368_PD_VIO		12
> +#define RK3368_PD_ALIVE		13
> +#define RK3368_PD_VIDEO		14
> +#define RK3368_PD_GPU_0		15
> +#define RK3368_PD_GPU_1		16
> +
> +/* VD_PMU */
> +#define RK3368_PD_PMU		17
> +
> +#endif



From ykk at rock-chips.com  Fri Jan  8 01:13:52 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 8 Jan 2016 17:13:52 +0800
Subject: [RFC PATCH v2 3/4] drm: rockchip: hdmi: add RK3229 HDMI support
In-Reply-To: <1452185424.4776.36.camel@pengutronix.de>
References: <1452156811-18150-1-git-send-email-ykk@rock-chips.com>
 <1452157373-18783-1-git-send-email-ykk@rock-chips.com>
 <1452161052.4776.7.camel@pengutronix.de> <568E3AD0.1020904@rock-chips.com>
 <1452185424.4776.36.camel@pengutronix.de>
Message-ID: <568F7DD0.7090707@rock-chips.com>

Hi Philipp,

On 01/08/2016 12:50 AM, Philipp Zabel wrote:
> Hi Yakir,
>
> Am Donnerstag, den 07.01.2016, 18:15 +0800 schrieb Yakir Yang:
>> Hi Philipp,
>>
>> Thanks for your fast respond :)
>>
>> On 01/07/2016 06:04 PM, Philipp Zabel wrote:
>>> Am Donnerstag, den 07.01.2016, 17:02 +0800 schrieb Yakir Yang:
>>>> RK3229 integrate an DesignedWare HDMI2.0 controller and an INNO HDMI2.0 phy,
>>>> the max output resolution is 4K.
>>>>
>>>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
>>> It sounds like the INNO HDMI2.0 phy is not necessarily specific to
>>> RK3229 but might also appear in other SoCs? If so, I think this should
>>> be implemented in a separate phy driver and be used by dw_hdmi-rockchip.
>> Do you mean I should create a new phy driver that place in "driver/phy"
>> directly ?
> Possibly, yes. The exynos video phys are already there. I have kept the
> mediatek dsi/hdmi phys together with the DRM driver, but I suppose I
> could move them there, too.
>
>> I have think about this idea, and it would make things much clean. But
>> INNO PHY
>> driver need the target pixel clock in drm_display_mode, I didn't find a
>> good way
>> to pass this variable to separate phy driver. Do you have some idea ?
> We'd need to extend the PHY API for this. For the mediatek phys we have
> side-stepped the issue by wiring up the PLL output to the common clock
> framework.

Wow, I have look at your "drm/mediatek: Add HDMI support" patch, it's 
great to
registers a common clock framework.

> I expect besides the pixel clock frequency, it might also be necessary
> to inform the PHY about cycles per pixel for deep color modes.

INNO PHY didn't need the color depth directly, driver could get the 
input pixel
clock rate, and then hdmi core driver could set TMDS rate though common
clock framework.

Anyway it's great material, I would update the new version out.

Thanks a lot
- Yakir

> regards
> Philipp
>
>
>
>




From zhangqing at rock-chips.com  Fri Jan  8 09:15:07 2016
From: zhangqing at rock-chips.com (zhangqing)
Date: Fri, 08 Jan 2016 09:15:07 -0800
Subject: [PATCH] soc: rockchip: power-domain: Modify power domain driver
 for rk3368
In-Reply-To: <3979294.oVXyhiFhfI@phil>
References: <1452269144-15389-1-git-send-email-zhangqing@rock-chips.com>
 <3979294.oVXyhiFhfI@phil>
Message-ID: <568FEE9B.3000909@rock-chips.com>



On 01/08/2016 12:25 AM, Heiko Stuebner wrote:
> Hi Elaine,
>
> Am Freitag, 8. Januar 2016, 08:05:44 schrieb zhangqing:
>> This driver is modified to support RK3368 SoC.
>>
>> Signed-off-by: zhangqing <zhangqing at rock-chips.com>
>> ---
>>   drivers/soc/rockchip/pm_domains.c        | 33
>> ++++++++++++++++++++++++++++++++ include/dt-bindings/power/rk3368-power.h
>> | 28 +++++++++++++++++++++++++++ 2 files changed, 61 insertions(+)
>>   create mode 100644 include/dt-bindings/power/rk3368-power.h
>>
>> diff --git a/drivers/soc/rockchip/pm_domains.c
>> b/drivers/soc/rockchip/pm_domains.c index 534c589..6cdffb1 100644
>> --- a/drivers/soc/rockchip/pm_domains.c
>> +++ b/drivers/soc/rockchip/pm_domains.c
>> @@ -18,6 +18,7 @@
>>   #include <linux/regmap.h>
>>   #include <linux/mfd/syscon.h>
>>   #include <dt-bindings/power/rk3288-power.h>
>> +#include <dt-bindings/power/rk3368-power.h>
>>
>>   struct rockchip_domain_info {
>>   	int pwr_mask;
>> @@ -75,6 +76,9 @@ struct rockchip_pmu {
>>   #define DOMAIN_RK3288(pwr, status, req)		\
>>   	DOMAIN(pwr, status, req, req, (req) + 16)
>>
>> +#define DOMAIN_RK3368(pwr, status, req)		\
>> +	DOMAIN(pwr, status, req, (req) + 16, req)
>> +
>>   static bool rockchip_pmu_domain_is_idle(struct rockchip_pm_domain *pd)
>>   {
>>   	struct rockchip_pmu *pmu = pd->pmu;
>> @@ -444,6 +448,14 @@ static const struct rockchip_domain_info
>> rk3288_pm_domains[] = { [RK3288_PD_GPU]		= DOMAIN_RK3288(9, 9, 2),
>>   };
>>
>> +static const struct rockchip_domain_info rk3368_pm_domains[] = {
>> +	[RK3368_PD_PERI]	= DOMAIN_RK3368(13, 12, 6),
>> +	[RK3368_PD_VIO]		= DOMAIN_RK3368(15, 14, 8),
>> +	[RK3368_PD_VIDEO]	= DOMAIN_RK3368(14, 13, 7),
>> +	[RK3368_PD_GPU_0]	= DOMAIN_RK3368(16, 15, 2),
>> +	[RK3368_PD_GPU_1]	= DOMAIN_RK3368(17, 16, 2),
>> +};
>
> The structure in the registers (and thus the macros you define above) is the
> same between rk3288 and rk3368, so there is no need to define that a second
> time. It is perfectly fine to re-use the DOMAIN_RK3288 macro here and saves
> double-definitions. [see our clock-drivers which already do this for the
> common parts].

The ack_mask bit and idle_mask bit is different in rk3368.
you can check:
+#define DOMAIN_RK3368(pwr, status, req)		\
+	DOMAIN(pwr, status, req, (req) + 16, req)
+

But the rk3288 is :
#define DOMAIN_RK3288(pwr, status, req)		\
  	DOMAIN(pwr, status, req, req, (req) + 16)

>
>> +
>>   static const struct rockchip_pmu_info rk3288_pmu = {
>>   	.pwr_offset = 0x08,
>>   	.status_offset = 0x0c,
>> @@ -461,11 +473,32 @@ static const struct rockchip_pmu_info rk3288_pmu = {
>> .domain_info = rk3288_pm_domains,
>>   };
>>
>> +static const struct rockchip_pmu_info rk3368_pmu = {
>> +	.pwr_offset = 0x0c,
>> +	.status_offset = 0x10,
>> +	.req_offset = 0x3c,
>> +	.idle_offset = 0x40,
>> +	.ack_offset = 0x40,
>> +
>> +	.core_pwrcnt_offset = 0x48,
>> +	.gpu_pwrcnt_offset = 0x50,
>> +
>> +	.core_power_transition_time = 24,
>> +	.gpu_power_transition_time = 24,
>> +
>> +	.num_domains = ARRAY_SIZE(rk3368_pm_domains),
>> +	.domain_info = rk3368_pm_domains,
>> +};
>> +
>>   static const struct of_device_id rockchip_pm_domain_dt_match[] = {
>>   	{
>>   		.compatible = "rockchip,rk3288-power-controller",
>>   		.data = (void *)&rk3288_pmu,
>>   	},
>> +	{
>> +		.compatible = "rockchip,rk3368-power-controller",
>> +		.data = (void *)&rk3368_pmu,
>> +	},
>>   	{ /* sentinel */ },
>>   };
>>
>> diff --git a/include/dt-bindings/power/rk3368-power.h
>> b/include/dt-bindings/power/rk3368-power.h new file mode 100644
>> index 0000000..93633d5
>> --- /dev/null
>> +++ b/include/dt-bindings/power/rk3368-power.h
>
> Please split the addition of the binding-header into a separate patch. We
> will need the header in both the soc-specific code as well as the devicetree
> side, which requires the header being in a shared branch.
>
> Otherwise patch looks great.
>
>
> Thanks
> Heiko
>
>> @@ -0,0 +1,28 @@
>> +#ifndef __DT_BINDINGS_POWER_RK3368_POWER_H__
>> +#define __DT_BINDINGS_POWER_RK3368_POWER_H__
>> +
>> +/* VD_CORE */
>> +#define RK3368_PD_A53_L0	0
>> +#define RK3368_PD_A53_L1	1
>> +#define RK3368_PD_A53_L2	2
>> +#define RK3368_PD_A53_L3	3
>> +#define RK3368_PD_SCU_L		4
>> +#define RK3368_PD_A53_B0	5
>> +#define RK3368_PD_A53_B1	6
>> +#define RK3368_PD_A53_B2	7
>> +#define RK3368_PD_A53_B3	8
>> +#define RK3368_PD_SCU_B		9
>> +
>> +/* VD_LOGIC */
>> +#define RK3368_PD_BUS		10
>> +#define RK3368_PD_PERI		11
>> +#define RK3368_PD_VIO		12
>> +#define RK3368_PD_ALIVE		13
>> +#define RK3368_PD_VIDEO		14
>> +#define RK3368_PD_GPU_0		15
>> +#define RK3368_PD_GPU_1		16
>> +
>> +/* VD_PMU */
>> +#define RK3368_PD_PMU		17
>> +
>> +#endif
>
>
>
>



From heiko at sntech.de  Fri Jan  8 01:18:55 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Fri, 08 Jan 2016 10:18:55 +0100
Subject: [PATCH] soc: rockchip: power-domain: Modify power domain driver
 for rk3368
In-Reply-To: <568FECFE.8060107@rock-chips.com>
References: <1452269144-15389-1-git-send-email-zhangqing@rock-chips.com>
 <3979294.oVXyhiFhfI@phil> <568FECFE.8060107@rock-chips.com>
Message-ID: <4607132.KdVH8UGEg4@phil>

Am Freitag, 8. Januar 2016, 09:08:14 schrieb zhangqing:
> On 01/08/2016 12:25 AM, Heiko Stuebner wrote:
> > Hi Elaine,
> > 
> > Am Freitag, 8. Januar 2016, 08:05:44 schrieb zhangqing:
> >> This driver is modified to support RK3368 SoC.
> >> 
> >> Signed-off-by: zhangqing <zhangqing at rock-chips.com>
> >> ---
> >> 
> >>   drivers/soc/rockchip/pm_domains.c        | 33
> >> 
> >> ++++++++++++++++++++++++++++++++
> >> include/dt-bindings/power/rk3368-power.h
> >> 
> >> | 28 +++++++++++++++++++++++++++ 2 files changed, 61 insertions(+)
> >> | 
> >>   create mode 100644 include/dt-bindings/power/rk3368-power.h
> >> 
> >> diff --git a/drivers/soc/rockchip/pm_domains.c
> >> b/drivers/soc/rockchip/pm_domains.c index 534c589..6cdffb1 100644
> >> --- a/drivers/soc/rockchip/pm_domains.c
> >> +++ b/drivers/soc/rockchip/pm_domains.c
> >> @@ -18,6 +18,7 @@
> >> 
> >>   #include <linux/regmap.h>
> >>   #include <linux/mfd/syscon.h>
> >>   #include <dt-bindings/power/rk3288-power.h>
> >> 
> >> +#include <dt-bindings/power/rk3368-power.h>
> >> 
> >>   struct rockchip_domain_info {
> >>   
> >>   	int pwr_mask;
> >> 
> >> @@ -75,6 +76,9 @@ struct rockchip_pmu {
> >> 
> >>   #define DOMAIN_RK3288(pwr, status, req)		\
> >>   
> >>   	DOMAIN(pwr, status, req, req, (req) + 16)
> >> 
> >> +#define DOMAIN_RK3368(pwr, status, req)		\
> >> +	DOMAIN(pwr, status, req, (req) + 16, req)
> >> +
> >> 
> >>   static bool rockchip_pmu_domain_is_idle(struct rockchip_pm_domain
> >>   *pd)
> >>   {
> >>   
> >>   	struct rockchip_pmu *pmu = pd->pmu;
> >> 
> >> @@ -444,6 +448,14 @@ static const struct rockchip_domain_info
> >> rk3288_pm_domains[] = { [RK3288_PD_GPU]		= DOMAIN_RK3288(9, 9, 2),
> >> 
> >>   };
> >> 
> >> +static const struct rockchip_domain_info rk3368_pm_domains[] = {
> >> +	[RK3368_PD_PERI]	= DOMAIN_RK3368(13, 12, 6),
> >> +	[RK3368_PD_VIO]		= DOMAIN_RK3368(15, 14, 8),
> >> +	[RK3368_PD_VIDEO]	= DOMAIN_RK3368(14, 13, 7),
> >> +	[RK3368_PD_GPU_0]	= DOMAIN_RK3368(16, 15, 2),
> >> +	[RK3368_PD_GPU_1]	= DOMAIN_RK3368(17, 16, 2),
> >> +};
> > 
> > The structure in the registers (and thus the macros you define above) is
> > the same between rk3288 and rk3368, so there is no need to define that
> > a second time. It is perfectly fine to re-use the DOMAIN_RK3288 macro
> > here and saves double-definitions. [see our clock-drivers which already
> > do this for the common parts].
> 
> The ack_mask bit and idle_mask bit is different in rk3368.
> you can check the :
> 
> +#define DOMAIN_RK3368(pwr, status, req)		\
> +	DOMAIN(pwr, status, req,(req) + 16, req)
> +
> But the rk3288 is :
> #define DOMAIN_RK3288(pwr, status, req)		\
>   	DOMAIN(pwr, status, req, req, (req) + 16)

ah sorry, misread that then ... so please forget I said anything ;-)


Heiko

> 
> >> +
> >> 
> >>   static const struct rockchip_pmu_info rk3288_pmu = {
> >>   
> >>   	.pwr_offset = 0x08,
> >>   	.status_offset = 0x0c,
> >> 
> >> @@ -461,11 +473,32 @@ static const struct rockchip_pmu_info rk3288_pmu
> >> = { .domain_info = rk3288_pm_domains,
> >> 
> >>   };
> >> 
> >> +static const struct rockchip_pmu_info rk3368_pmu = {
> >> +	.pwr_offset = 0x0c,
> >> +	.status_offset = 0x10,
> >> +	.req_offset = 0x3c,
> >> +	.idle_offset = 0x40,
> >> +	.ack_offset = 0x40,
> >> +
> >> +	.core_pwrcnt_offset = 0x48,
> >> +	.gpu_pwrcnt_offset = 0x50,
> >> +
> >> +	.core_power_transition_time = 24,
> >> +	.gpu_power_transition_time = 24,
> >> +
> >> +	.num_domains = ARRAY_SIZE(rk3368_pm_domains),
> >> +	.domain_info = rk3368_pm_domains,
> >> +};
> >> +
> >> 
> >>   static const struct of_device_id rockchip_pm_domain_dt_match[] = {
> >>   
> >>   	{
> >>   	
> >>   		.compatible = "rockchip,rk3288-power-controller",
> >>   		.data = (void *)&rk3288_pmu,
> >>   	
> >>   	},
> >> 
> >> +	{
> >> +		.compatible = "rockchip,rk3368-power-controller",
> >> +		.data = (void *)&rk3368_pmu,
> >> +	},
> >> 
> >>   	{ /* sentinel */ },
> >>   
> >>   };
> >> 
> >> diff --git a/include/dt-bindings/power/rk3368-power.h
> >> b/include/dt-bindings/power/rk3368-power.h new file mode 100644
> >> index 0000000..93633d5
> >> --- /dev/null
> >> +++ b/include/dt-bindings/power/rk3368-power.h
> > 
> > Please split the addition of the binding-header into a separate patch.
> > We
> > will need the header in both the soc-specific code as well as the
> > devicetree side, which requires the header being in a shared branch.
> > 
> > Otherwise patch looks great.
> > 
> > 
> > Thanks
> > Heiko
> > 
> >> @@ -0,0 +1,28 @@
> >> +#ifndef __DT_BINDINGS_POWER_RK3368_POWER_H__
> >> +#define __DT_BINDINGS_POWER_RK3368_POWER_H__
> >> +
> >> +/* VD_CORE */
> >> +#define RK3368_PD_A53_L0	0
> >> +#define RK3368_PD_A53_L1	1
> >> +#define RK3368_PD_A53_L2	2
> >> +#define RK3368_PD_A53_L3	3
> >> +#define RK3368_PD_SCU_L		4
> >> +#define RK3368_PD_A53_B0	5
> >> +#define RK3368_PD_A53_B1	6
> >> +#define RK3368_PD_A53_B2	7
> >> +#define RK3368_PD_A53_B3	8
> >> +#define RK3368_PD_SCU_B		9
> >> +
> >> +/* VD_LOGIC */
> >> +#define RK3368_PD_BUS		10
> >> +#define RK3368_PD_PERI		11
> >> +#define RK3368_PD_VIO		12
> >> +#define RK3368_PD_ALIVE		13
> >> +#define RK3368_PD_VIDEO		14
> >> +#define RK3368_PD_GPU_0		15
> >> +#define RK3368_PD_GPU_1		16
> >> +
> >> +/* VD_PMU */
> >> +#define RK3368_PD_PMU		17
> >> +
> >> +#endif



From caesar.upstream at gmail.com  Fri Jan  8 01:26:18 2016
From: caesar.upstream at gmail.com (Caesar Wang)
Date: Fri, 8 Jan 2016 17:26:18 +0800
Subject: [PATCH v2 04/12] clk: rockchip: rk3036: enable the
 CLK_IGNORE_UNUSED flag for sclk_i2s_out
In-Reply-To: <290434099.8XFZ9qmNRz@phil>
References: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
 <1452155155-16232-5-git-send-email-wxt@rock-chips.com>
 <290434099.8XFZ9qmNRz@phil>
Message-ID: <568F80BA.6060209@gmail.com>

Heiko,

? 2016?01?07? 18:05, Heiko Stuebner ??:
> Hi Caesar,
>
> Am Donnerstag, 7. Januar 2016, 16:25:47 schrieb Caesar Wang:
>> SCLk_I2S_OUT is the noc bus clock for i2s module, this clock is used by
>> extra codecs.
>>
>> Due to it shouldn't belong to any driver, but we need it enabled,
>> so just mark it as the CLK_IGNORE_UNUSED flag.
> What makes you think it shouldn't belong to any driver?
>
> In most schematics I have, i2s_clkout is going to some clock-input of the
> audio codec - probably the MCLK input on your rt5616. And while the new
> rt5616 driver does not seem to do clock handling, it should just handle the
> clock using the normal APIs.

You are right,  that's seem has to find the root cause.
I'm wonder that the codec/i2s is working in kernel v4.1 patch without it.

In general,  the MLCK will be handled in i2s driver if the cpu side is 
master.

>
>
> Heiko
>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip


-- 
Thanks,
Caesar



From heiko at sntech.de  Fri Jan  8 01:46:26 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Fri, 08 Jan 2016 10:46:26 +0100
Subject: [PATCH v2 04/12] clk: rockchip: rk3036: enable the
 CLK_IGNORE_UNUSED flag for sclk_i2s_out
In-Reply-To: <568F80BA.6060209@gmail.com>
References: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
 <290434099.8XFZ9qmNRz@phil> <568F80BA.6060209@gmail.com>
Message-ID: <20757797.djuaztrzGE@phil>

Hi Caesar,

Am Freitag, 8. Januar 2016, 17:26:18 schrieb Caesar Wang:
> ? 2016?01?07? 18:05, Heiko Stuebner ??:
> > Am Donnerstag, 7. Januar 2016, 16:25:47 schrieb Caesar Wang:
> >> SCLk_I2S_OUT is the noc bus clock for i2s module, this clock is used by
> >> extra codecs.
> >> 
> >> Due to it shouldn't belong to any driver, but we need it enabled,
> >> so just mark it as the CLK_IGNORE_UNUSED flag.
> > 
> > What makes you think it shouldn't belong to any driver?
> > 
> > In most schematics I have, i2s_clkout is going to some clock-input of
> > the
> > audio codec - probably the MCLK input on your rt5616. And while the new
> > rt5616 driver does not seem to do clock handling, it should just handle
> > the clock using the normal APIs.
> 
> You are right,  that's seem has to find the root cause.
> I'm wonder that the codec/i2s is working in kernel v4.1 patch without it.
> 
> In general,  the MLCK will be handled in i2s driver if the cpu side is
> master.

looking at the rt5616 datasheet, that would be BCLK1 instead I think. This 
one does differentiate between being master/slave.

The master/sysclk (mclk) is also handled in other drivers in all cases. 
(da7213.c, da7219.c, max98090.c, max98095.c, probably more)


Heiko


From jeffy.chen at rock-chips.com  Fri Jan  8 02:33:26 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Fri,  8 Jan 2016 18:33:26 +0800
Subject: [PATCH v2 0/6] rockchip: kylin: Boot with android boot image
Message-ID: <1452249212-25183-1-git-send-email-jeffy.chen@rock-chips.com>

We are porting android to kylin board now.
This series could let it boot up with android's boot image.

Changes in v2:
Add comments.

Jeffy Chen (6):
  common/image-fdt.c: Make boot_get_fdt() perform a check for Android
    images
  ARM: bootm: Try to use relocated ramdisk
  rockchip: rk3036: Bind GPIO banks
  rockchip: kylin: Add default gpt partition table
  rockchip: kylin: Enable boot with android boot image
  rockchip: kylin: Check fastboot request

 arch/arm/lib/bootm.c                      | 12 ++++++-
 board/kylin/kylin_rk3036/kylin_rk3036.c   | 32 ++++++++++++++++++
 common/image-fdt.c                        |  4 +++
 drivers/pinctrl/rockchip/pinctrl_rk3036.c |  8 +++++
 include/configs/kylin_rk3036.h            | 55 +++++++++++++++++++++++++++++++
 5 files changed, 110 insertions(+), 1 deletion(-)

-- 
2.1.4





From jeffy.chen at rock-chips.com  Fri Jan  8 02:33:29 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Fri,  8 Jan 2016 18:33:29 +0800
Subject: [PATCH v2 3/6] rockchip: rk3036: Bind GPIO banks
In-Reply-To: <1452249212-25183-1-git-send-email-jeffy.chen@rock-chips.com>
References: <1452249212-25183-1-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <1452249212-25183-4-git-send-email-jeffy.chen@rock-chips.com>

Call dm_scan_fdt_node() in rk3036 pinctrl uclass binding.

Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
Acked-by: Simon Glass <sjg at chromium.org>
---

Changes in v2: None

 drivers/pinctrl/rockchip/pinctrl_rk3036.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/pinctrl/rockchip/pinctrl_rk3036.c b/drivers/pinctrl/rockchip/pinctrl_rk3036.c
index 581b096..1f78bf8 100644
--- a/drivers/pinctrl/rockchip/pinctrl_rk3036.c
+++ b/drivers/pinctrl/rockchip/pinctrl_rk3036.c
@@ -15,6 +15,7 @@
 #include <asm/arch/hardware.h>
 #include <asm/arch/periph.h>
 #include <dm/pinctrl.h>
+#include <dm/root.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -252,6 +253,12 @@ static struct pinctrl_ops rk3036_pinctrl_ops = {
 	.get_periph_id	= rk3036_pinctrl_get_periph_id,
 };
 
+static int rk3036_pinctrl_bind(struct udevice *dev)
+{
+	/* scan child GPIO banks */
+	return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false);
+}
+
 static int rk3036_pinctrl_probe(struct udevice *dev)
 {
 	struct rk3036_pinctrl_priv *priv = dev_get_priv(dev);
@@ -272,5 +279,6 @@ U_BOOT_DRIVER(pinctrl_rk3036) = {
 	.of_match	= rk3036_pinctrl_ids,
 	.priv_auto_alloc_size = sizeof(struct rk3036_pinctrl_priv),
 	.ops		= &rk3036_pinctrl_ops,
+	.bind		= rk3036_pinctrl_bind,
 	.probe		= rk3036_pinctrl_probe,
 };
-- 
2.1.4





From jeffy.chen at rock-chips.com  Fri Jan  8 02:33:27 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Fri,  8 Jan 2016 18:33:27 +0800
Subject: [PATCH v2 1/6] common/image-fdt.c: Make boot_get_fdt() perform a
 check for Android images
In-Reply-To: <1452249212-25183-1-git-send-email-jeffy.chen@rock-chips.com>
References: <1452249212-25183-1-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <1452249212-25183-2-git-send-email-jeffy.chen@rock-chips.com>

Android images don't have a fdt.

Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
Acked-by: Simon Glass <sjg at chromium.org>
---

Changes in v2: None

 common/image-fdt.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/common/image-fdt.c b/common/image-fdt.c
index 5e4e5bd..41aaa0d 100644
--- a/common/image-fdt.c
+++ b/common/image-fdt.c
@@ -379,6 +379,10 @@ int boot_get_fdt(int flag, int argc, char * const argv[], uint8_t arch,
 				       (long)fdt_addr);
 			}
 			break;
+#ifdef CONFIG_ANDROID_BOOT_IMAGE
+		case IMAGE_FORMAT_ANDROID:
+			goto no_fdt;
+#endif
 		default:
 			puts("ERROR: Did not find a cmdline Flattened Device Tree\n");
 			goto no_fdt;
-- 
2.1.4





From jeffy.chen at rock-chips.com  Fri Jan  8 02:33:28 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Fri,  8 Jan 2016 18:33:28 +0800
Subject: [PATCH v2 2/6] ARM: bootm: Try to use relocated ramdisk
In-Reply-To: <1452249212-25183-1-git-send-email-jeffy.chen@rock-chips.com>
References: <1452249212-25183-1-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <1452249212-25183-3-git-send-email-jeffy.chen@rock-chips.com>

After boot_ramdisk_high(), ramdisk would be relocated to
initrd_start & initrd_end, so use them instead of rd_start & rd_end.

Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>

---

Changes in v2:
Add comments.

 arch/arm/lib/bootm.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c
index a477cae..0838d89 100644
--- a/arch/arm/lib/bootm.c
+++ b/arch/arm/lib/bootm.c
@@ -225,7 +225,17 @@ static void boot_prep_linux(bootm_headers_t *images)
 		if (BOOTM_ENABLE_MEMORY_TAGS)
 			setup_memory_tags(gd->bd);
 		if (BOOTM_ENABLE_INITRD_TAG) {
-			if (images->rd_start && images->rd_end) {
+			/*
+			 * In boot_ramdisk_high(), it may relocate ramdisk to
+			 * a specified location. And set images->initrd_start &
+			 * images->initrd_end to relocated ramdisk's start/end
+			 * addresses. So use them instead of images->rd_start &
+			 * images->rd_end when possible.
+			 */
+			if (images->initrd_start && images->initrd_end) {
+				setup_initrd_tag(gd->bd, images->initrd_start,
+						 images->initrd_end);
+			} else if (images->rd_start && images->rd_end) {
 				setup_initrd_tag(gd->bd, images->rd_start,
 						 images->rd_end);
 			}
-- 
2.1.4





From jeffy.chen at rock-chips.com  Fri Jan  8 02:33:30 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Fri,  8 Jan 2016 18:33:30 +0800
Subject: [PATCH v2 4/6] rockchip: kylin: Add default gpt partition table
In-Reply-To: <1452249212-25183-1-git-send-email-jeffy.chen@rock-chips.com>
References: <1452249212-25183-1-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <1452249212-25183-5-git-send-email-jeffy.chen@rock-chips.com>

Add default android gpt partition table for kylin board.

Use "gpt write mmc 0 $partitions" to apply.

Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
Acked-by: Simon Glass <sjg at chromium.org>
---

Changes in v2: None

 include/configs/kylin_rk3036.h | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/include/configs/kylin_rk3036.h b/include/configs/kylin_rk3036.h
index aa07889..b750b26 100644
--- a/include/configs/kylin_rk3036.h
+++ b/include/configs/kylin_rk3036.h
@@ -9,4 +9,33 @@
 
 #include <configs/rk3036_common.h>
 
+#ifndef CONFIG_SPL_BUILD
+
+/* Enable gpt partition table */
+#define CONFIG_CMD_GPT
+#define CONFIG_RANDOM_UUID
+#define CONFIG_EFI_PARTITION
+#define PARTS_DEFAULT \
+	"uuid_disk=${uuid_gpt_disk};" \
+	"name=loader,start=32K,size=4000K,uuid=${uuid_gpt_loader};" \
+	"name=reserved,size=64K,uuid=${uuid_gpt_reserved};" \
+	"name=misc,size=4M,uuid=${uuid_gpt_misc};" \
+	"name=recovery,size=32M,uuid=${uuid_gpt_recovery};" \
+	"name=boot_a,size=32M,uuid=${uuid_gpt_boot_a};" \
+	"name=boot_b,size=32M,uuid=${uuid_gpt_boot_b};" \
+	"name=system_a,size=818M,uuid=${uuid_gpt_system_a};" \
+	"name=system_b,size=818M,uuid=${uuid_gpt_system_b};" \
+	"name=vendor_a,size=50M,uuid=${uuid_gpt_vendor_a};" \
+	"name=vendor_b,size=50M,uuid=${uuid_gpt_vendor_b};" \
+	"name=cache,size=100M,uuid=${uuid_gpt_cache};" \
+	"name=metadata,size=16M,uuid=${uuid_gpt_metadata};" \
+	"name=persist,size=4M,uuid=${uuid_gpt_persist};" \
+	"name=userdata,size=-,uuid=${uuid_gpt_userdata};\0" \
+
+#undef CONFIG_EXTRA_ENV_SETTINGS
+#define CONFIG_EXTRA_ENV_SETTINGS \
+	"partitions=" PARTS_DEFAULT \
+
+#endif
+
 #endif
-- 
2.1.4





From jeffy.chen at rock-chips.com  Fri Jan  8 02:33:31 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Fri,  8 Jan 2016 18:33:31 +0800
Subject: [PATCH v2 5/6] rockchip: kylin: Enable boot with android boot image
In-Reply-To: <1452249212-25183-1-git-send-email-jeffy.chen@rock-chips.com>
References: <1452249212-25183-1-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <1452249212-25183-6-git-send-email-jeffy.chen@rock-chips.com>

The android kernel is using appended dtb by default, and store
ramdisk right after kernel & dtb.
So we needs to relocate ramdisk, and use atags to pass params.

Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
Acked-by: Simon Glass <sjg at chromium.org>
---

Changes in v2: None

 include/configs/kylin_rk3036.h | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/include/configs/kylin_rk3036.h b/include/configs/kylin_rk3036.h
index b750b26..49997ec 100644
--- a/include/configs/kylin_rk3036.h
+++ b/include/configs/kylin_rk3036.h
@@ -35,6 +35,29 @@
 #undef CONFIG_EXTRA_ENV_SETTINGS
 #define CONFIG_EXTRA_ENV_SETTINGS \
 	"partitions=" PARTS_DEFAULT \
+	"mmcdev=0\0" \
+	"mmcpart=5\0" \
+	"loadaddr=" __stringify(CONFIG_SYS_LOAD_ADDR) "\0" \
+
+#define CONFIG_ANDROID_BOOT_IMAGE
+#define CONFIG_SYS_BOOT_RAMDISK_HIGH
+#define CONFIG_SYS_HUSH_PARSER
+
+#undef CONFIG_BOOTCOMMAND
+#define CONFIG_BOOTCOMMAND \
+	"mmc dev ${mmcdev}; if mmc rescan; then " \
+		"part start mmc ${mmcdev} ${mmcpart} boot_start;" \
+		"part size mmc ${mmcdev} ${mmcpart} boot_size;" \
+		"mmc read ${loadaddr} ${boot_start} ${boot_size};" \
+		"bootm start ${loadaddr}; bootm ramdisk;" \
+		"bootm prep; bootm go;" \
+	"fi;" \
+
+/* Enable atags */
+#define CONFIG_SYS_BOOTPARAMS_LEN	(64*1024)
+#define CONFIG_INITRD_TAG
+#define CONFIG_SETUP_MEMORY_TAGS
+#define CONFIG_CMDLINE_TAG
 
 #endif
 
-- 
2.1.4





From jeffy.chen at rock-chips.com  Fri Jan  8 02:33:32 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Fri,  8 Jan 2016 18:33:32 +0800
Subject: [PATCH v2 6/6] rockchip: kylin: Check fastboot request
In-Reply-To: <1452249212-25183-1-git-send-email-jeffy.chen@rock-chips.com>
References: <1452249212-25183-1-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <1452249212-25183-7-git-send-email-jeffy.chen@rock-chips.com>

We will save boot mode flag in grf's os_reg[0], if fastboot
requested or fastboot key pressed, try to enter fastboot mode
at preboot stage.

Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
Acked-by: Simon Glass <sjg at chromium.org>
---

Changes in v2: None

 board/kylin/kylin_rk3036/kylin_rk3036.c | 32 ++++++++++++++++++++++++++++++++
 include/configs/kylin_rk3036.h          |  3 +++
 2 files changed, 35 insertions(+)

diff --git a/board/kylin/kylin_rk3036/kylin_rk3036.c b/board/kylin/kylin_rk3036/kylin_rk3036.c
index 40d6b52..df2c123 100644
--- a/board/kylin/kylin_rk3036/kylin_rk3036.c
+++ b/board/kylin/kylin_rk3036/kylin_rk3036.c
@@ -8,10 +8,15 @@
 #include <dm.h>
 #include <asm/io.h>
 #include <asm/arch/uart.h>
+#include <asm/arch-rockchip/grf_rk3036.h>
 #include <asm/arch/sdram_rk3036.h>
+#include <asm/gpio.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
+#define GRF_BASE	0x20008000
+static struct rk3036_grf * const grf = (void *)GRF_BASE;
+
 void get_ddr_config(struct rk3036_ddr_config *config)
 {
 	/* K4B4G1646Q config */
@@ -28,6 +33,33 @@ void get_ddr_config(struct rk3036_ddr_config *config)
 	config->bw = 1;
 }
 
+#define FASTBOOT_KEY_GPIO 93
+#define ROCKCHIP_BOOT_MODE_NORMAL	0
+#define ROCKCHIP_BOOT_MODE_FASTBOOT	1
+
+int fastboot_key_pressed(void)
+{
+	gpio_request(FASTBOOT_KEY_GPIO, "fastboot_key");
+	gpio_direction_input(FASTBOOT_KEY_GPIO);
+	return !gpio_get_value(FASTBOOT_KEY_GPIO);
+}
+
+int board_late_init(void)
+{
+	int boot_mode = readl(&grf->os_reg[0]);
+
+	/* Clear boot mode */
+	writel(ROCKCHIP_BOOT_MODE_NORMAL, &grf->os_reg[0]);
+
+	if (boot_mode == ROCKCHIP_BOOT_MODE_FASTBOOT ||
+	    fastboot_key_pressed()) {
+		printf("enter fastboot!\n");
+		setenv("preboot", "setenv preboot; fastboot usb0");
+	}
+
+	return 0;
+}
+
 int board_init(void)
 {
 	return 0;
diff --git a/include/configs/kylin_rk3036.h b/include/configs/kylin_rk3036.h
index 49997ec..424e81b 100644
--- a/include/configs/kylin_rk3036.h
+++ b/include/configs/kylin_rk3036.h
@@ -39,6 +39,9 @@
 	"mmcpart=5\0" \
 	"loadaddr=" __stringify(CONFIG_SYS_LOAD_ADDR) "\0" \
 
+#define CONFIG_BOARD_LATE_INIT
+#define CONFIG_PREBOOT
+
 #define CONFIG_ANDROID_BOOT_IMAGE
 #define CONFIG_SYS_BOOT_RAMDISK_HIGH
 #define CONFIG_SYS_HUSH_PARSER
-- 
2.1.4





From jeffy.chen at rock-chips.com  Fri Jan  8 02:51:36 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Fri, 8 Jan 2016 18:51:36 +0800
Subject: [PATCH v1 5/6] rockchip: kylin: Enable boot with android boot
 image
In-Reply-To: <CAPnjgZ1G=5=gqy6yGV1NZ13Zv0BACRYMPpdAoFAi+sv2TPXknw@mail.gmail.com>
References: <1451893823-20309-1-git-send-email-jeffy.chen@rock-chips.com>
 <1451893823-20309-6-git-send-email-jeffy.chen@rock-chips.com>
 <CAPnjgZ1G=5=gqy6yGV1NZ13Zv0BACRYMPpdAoFAi+sv2TPXknw@mail.gmail.com>
Message-ID: <568F94B8.60101@rock-chips.com>

Hi Simon,

On 2016-1-8 11:34, Simon Glass wrote:
> Hi Jeffy,
>
> On 4 January 2016 at 00:50, Jeffy Chen <jeffy.chen at rock-chips.com> wrote:
>> The android kernel is using appended dtb by default, and store
>> ramdisk right after kernel & dtb.
>> So we needs to relocate ramdisk, and use atags to pass params.
>>
>> Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
>> ---
>>
>>   include/configs/kylin_rk3036.h | 23 +++++++++++++++++++++++
>>   1 file changed, 23 insertions(+)
> Acked-by: Simon Glass <sjg at chromium.org>
>
> What kernel version are you using?
It's kernel v4.1 for android project:
git fetch 
https://android.googlesource.com/platform/hardware/bsp/kernel/common/v4.1 refs/changes/03/188603/5 
&& git checkout FETCH_HEAD :)
>
> Regards,
> Simon
>

On 2016-1-8 11:34, Simon Glass wrote:
> Hi Jeffy,
>
> On 4 January 2016 at 00:50, Jeffy Chen <jeffy.chen at rock-chips.com> wrote:
>> The android kernel is using appended dtb by default, and store
>> ramdisk right after kernel & dtb.
>> So we needs to relocate ramdisk, and use atags to pass params.
>>
>> Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
>> ---
>>
>>   include/configs/kylin_rk3036.h | 23 +++++++++++++++++++++++
>>   1 file changed, 23 insertions(+)
> Acked-by: Simon Glass <sjg at chromium.org>
>
> What kernel version are you using?
>
> Regards,
> Simon
>



From zhangqing at rock-chips.com  Fri Jan  8 10:37:39 2016
From: zhangqing at rock-chips.com (zhangqing)
Date: Fri,  8 Jan 2016 10:37:39 -0800
Subject: [PATCH v2 0/2] SoC: rockchip: support power domain for RK3368
Message-ID: <1452278261-16864-1-git-send-email-zhangqing@rock-chips.com>

add pd id in rk3368-power.h
modify pm_domains.c to support RK3368 SoC.

zhangqing (2):
  dt-bindings: add power-domain header for RK3368 SoCs
  soc: rockchip: power-domain: Modify power domain driver for rk3368

 drivers/soc/rockchip/pm_domains.c        | 33 ++++++++++++++++++++++++++++++++
 include/dt-bindings/power/rk3368-power.h | 28 +++++++++++++++++++++++++++
 2 files changed, 61 insertions(+)
 create mode 100644 include/dt-bindings/power/rk3368-power.h

-- 
1.9.1




From zhangqing at rock-chips.com  Fri Jan  8 10:37:40 2016
From: zhangqing at rock-chips.com (zhangqing)
Date: Fri,  8 Jan 2016 10:37:40 -0800
Subject: [PATCH v2 1/2] dt-bindings: add power-domain header for RK3368 SoCs
In-Reply-To: <1452278261-16864-1-git-send-email-zhangqing@rock-chips.com>
References: <1452278261-16864-1-git-send-email-zhangqing@rock-chips.com>
Message-ID: <1452278261-16864-2-git-send-email-zhangqing@rock-chips.com>

According to a description from TRM, add all the power domains.

Signed-off-by: zhangqing <zhangqing at rock-chips.com>
---
 include/dt-bindings/power/rk3368-power.h | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)
 create mode 100644 include/dt-bindings/power/rk3368-power.h

diff --git a/include/dt-bindings/power/rk3368-power.h b/include/dt-bindings/power/rk3368-power.h
new file mode 100644
index 0000000..93633d5
--- /dev/null
+++ b/include/dt-bindings/power/rk3368-power.h
@@ -0,0 +1,28 @@
+#ifndef __DT_BINDINGS_POWER_RK3368_POWER_H__
+#define __DT_BINDINGS_POWER_RK3368_POWER_H__
+
+/* VD_CORE */
+#define RK3368_PD_A53_L0	0
+#define RK3368_PD_A53_L1	1
+#define RK3368_PD_A53_L2	2
+#define RK3368_PD_A53_L3	3
+#define RK3368_PD_SCU_L		4
+#define RK3368_PD_A53_B0	5
+#define RK3368_PD_A53_B1	6
+#define RK3368_PD_A53_B2	7
+#define RK3368_PD_A53_B3	8
+#define RK3368_PD_SCU_B		9
+
+/* VD_LOGIC */
+#define RK3368_PD_BUS		10
+#define RK3368_PD_PERI		11
+#define RK3368_PD_VIO		12
+#define RK3368_PD_ALIVE		13
+#define RK3368_PD_VIDEO		14
+#define RK3368_PD_GPU_0		15
+#define RK3368_PD_GPU_1		16
+
+/* VD_PMU */
+#define RK3368_PD_PMU		17
+
+#endif
-- 
1.9.1




From zhangqing at rock-chips.com  Fri Jan  8 10:37:41 2016
From: zhangqing at rock-chips.com (zhangqing)
Date: Fri,  8 Jan 2016 10:37:41 -0800
Subject: [PATCH v2 2/2] soc: rockchip: power-domain: Modify power domain
 driver for rk3368
In-Reply-To: <1452278261-16864-1-git-send-email-zhangqing@rock-chips.com>
References: <1452278261-16864-1-git-send-email-zhangqing@rock-chips.com>
Message-ID: <1452278261-16864-3-git-send-email-zhangqing@rock-chips.com>

This driver is modified to support RK3368 SoC.

Signed-off-by: zhangqing <zhangqing at rock-chips.com>
---
 drivers/soc/rockchip/pm_domains.c | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/drivers/soc/rockchip/pm_domains.c b/drivers/soc/rockchip/pm_domains.c
index 534c589..6cdffb1 100644
--- a/drivers/soc/rockchip/pm_domains.c
+++ b/drivers/soc/rockchip/pm_domains.c
@@ -18,6 +18,7 @@
 #include <linux/regmap.h>
 #include <linux/mfd/syscon.h>
 #include <dt-bindings/power/rk3288-power.h>
+#include <dt-bindings/power/rk3368-power.h>
 
 struct rockchip_domain_info {
 	int pwr_mask;
@@ -75,6 +76,9 @@ struct rockchip_pmu {
 #define DOMAIN_RK3288(pwr, status, req)		\
 	DOMAIN(pwr, status, req, req, (req) + 16)
 
+#define DOMAIN_RK3368(pwr, status, req)		\
+	DOMAIN(pwr, status, req, (req) + 16, req)
+
 static bool rockchip_pmu_domain_is_idle(struct rockchip_pm_domain *pd)
 {
 	struct rockchip_pmu *pmu = pd->pmu;
@@ -444,6 +448,14 @@ static const struct rockchip_domain_info rk3288_pm_domains[] = {
 	[RK3288_PD_GPU]		= DOMAIN_RK3288(9, 9, 2),
 };
 
+static const struct rockchip_domain_info rk3368_pm_domains[] = {
+	[RK3368_PD_PERI]	= DOMAIN_RK3368(13, 12, 6),
+	[RK3368_PD_VIO]		= DOMAIN_RK3368(15, 14, 8),
+	[RK3368_PD_VIDEO]	= DOMAIN_RK3368(14, 13, 7),
+	[RK3368_PD_GPU_0]	= DOMAIN_RK3368(16, 15, 2),
+	[RK3368_PD_GPU_1]	= DOMAIN_RK3368(17, 16, 2),
+};
+
 static const struct rockchip_pmu_info rk3288_pmu = {
 	.pwr_offset = 0x08,
 	.status_offset = 0x0c,
@@ -461,11 +473,32 @@ static const struct rockchip_pmu_info rk3288_pmu = {
 	.domain_info = rk3288_pm_domains,
 };
 
+static const struct rockchip_pmu_info rk3368_pmu = {
+	.pwr_offset = 0x0c,
+	.status_offset = 0x10,
+	.req_offset = 0x3c,
+	.idle_offset = 0x40,
+	.ack_offset = 0x40,
+
+	.core_pwrcnt_offset = 0x48,
+	.gpu_pwrcnt_offset = 0x50,
+
+	.core_power_transition_time = 24,
+	.gpu_power_transition_time = 24,
+
+	.num_domains = ARRAY_SIZE(rk3368_pm_domains),
+	.domain_info = rk3368_pm_domains,
+};
+
 static const struct of_device_id rockchip_pm_domain_dt_match[] = {
 	{
 		.compatible = "rockchip,rk3288-power-controller",
 		.data = (void *)&rk3288_pmu,
 	},
+	{
+		.compatible = "rockchip,rk3368-power-controller",
+		.data = (void *)&rk3368_pmu,
+	},
 	{ /* sentinel */ },
 };
 
-- 
1.9.1




From zhengxing at rock-chips.com  Fri Jan  8 03:25:10 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Fri,  8 Jan 2016 19:25:10 +0800
Subject: [RFC PATCH 2/2] ARM: dts: rockchip: Update the cpu freqs table
In-Reply-To: <1452252311-13908-1-git-send-email-zhengxing@rock-chips.com>
References: <1452252311-13908-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <1452252311-13908-2-git-send-email-zhengxing@rock-chips.com>

Actually, we need more cpu freqs which are 50MHz integer multiples.





From zhengxing at rock-chips.com  Fri Jan  8 03:25:09 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Fri,  8 Jan 2016 19:25:09 +0800
Subject: [RFC PATCH 1/2] clk: rockchip: rk3036: Add to 100MHz integer
 multiples of cpu freq table
Message-ID: <1452252311-13908-1-git-send-email-zhengxing@rock-chips.com>

We need the cpu changing freqs capability, and considering that emac ref
which is need 50MHz might is under the APLL, meanwhile, we don't need
too many freqs because it may spend more time to find the correct freq.
Therefore, we should add 100MHz integer multiples cpu freqs for emac.

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
---

 drivers/clk/rockchip/clk-rk3036.c |   27 ++++++++++++++++++++++++---
 1 file changed, 24 insertions(+), 3 deletions(-)

diff --git a/drivers/clk/rockchip/clk-rk3036.c b/drivers/clk/rockchip/clk-rk3036.c
index bf9dd9b..f60273c 100644
--- a/drivers/clk/rockchip/clk-rk3036.c
+++ b/drivers/clk/rockchip/clk-rk3036.c
@@ -32,18 +32,22 @@ enum rk3036_plls {
 static struct rockchip_pll_rate_table rk3036_pll_rates[] = {
 	/* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */
 	RK3036_PLL_RATE(1608000000, 1, 67, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1600000000, 6, 400, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1584000000, 1, 66, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1560000000, 1, 65, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1536000000, 1, 64, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1512000000, 1, 63, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1500000000, 6, 375, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1488000000, 1, 62, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1464000000, 1, 61, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1440000000, 1, 60, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1416000000, 1, 59, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1400000000, 6, 350, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1392000000, 1, 58, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1368000000, 1, 57, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1344000000, 1, 56, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1320000000, 1, 55, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1300000000, 6, 325, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1296000000, 1, 54, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1272000000, 1, 53, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1248000000, 1, 52, 1, 1, 1, 0),
@@ -70,8 +74,12 @@ static struct rockchip_pll_rate_table rk3036_pll_rates[] = {
 	RK3036_PLL_RATE( 504000000, 1, 63, 3, 1, 1, 0),
 	RK3036_PLL_RATE( 500000000, 6, 250, 2, 1, 1, 0),
 	RK3036_PLL_RATE( 408000000, 1, 68, 2, 2, 1, 0),
+	RK3036_PLL_RATE( 400000000, 1, 100, 3, 2, 1, 0),
 	RK3036_PLL_RATE( 312000000, 1, 52, 2, 2, 1, 0),
+	RK3036_PLL_RATE( 300000000, 1, 50, 2, 2, 1, 0),
 	RK3036_PLL_RATE( 216000000, 1, 72, 4, 2, 1, 0),
+	RK3036_PLL_RATE( 200000000, 1, 50, 3, 2, 1, 0),
+	RK3036_PLL_RATE( 100000000, 6, 400, 4, 4, 1, 0),
 	RK3036_PLL_RATE(  96000000, 1, 64, 4, 4, 1, 0),
 	{ /* sentinel */ },
 };
@@ -104,9 +112,22 @@ static struct rockchip_pll_rate_table rk3036_pll_rates[] = {
 	}
 
 static struct rockchip_cpuclk_rate_table rk3036_cpuclk_rates[] __initdata = {
-	RK3036_CPUCLK_RATE(816000000, 4),
-	RK3036_CPUCLK_RATE(600000000, 4),
-	RK3036_CPUCLK_RATE(312000000, 4),
+	RK3036_CPUCLK_RATE(1600000000, 8),
+	RK3036_CPUCLK_RATE(1500000000, 7),
+	RK3036_CPUCLK_RATE(1400000000, 7),
+	RK3036_CPUCLK_RATE(1300000000, 6),
+	RK3036_CPUCLK_RATE(1200000000, 6),
+	RK3036_CPUCLK_RATE(1100000000, 5),
+	RK3036_CPUCLK_RATE(1000000000, 5),
+	RK3036_CPUCLK_RATE( 900000000, 4),
+	RK3036_CPUCLK_RATE( 800000000, 4),
+	RK3036_CPUCLK_RATE( 700000000, 3),
+	RK3036_CPUCLK_RATE( 600000000, 3),
+	RK3036_CPUCLK_RATE( 500000000, 3),
+	RK3036_CPUCLK_RATE( 400000000, 3),
+	RK3036_CPUCLK_RATE( 300000000, 2),
+	RK3036_CPUCLK_RATE( 200000000, 1),
+	RK3036_CPUCLK_RATE( 100000000, 1),
 };
 
 static const struct rockchip_cpuclk_reg_data rk3036_cpuclk_data = {
-- 
1.7.9.5




From zhengxing at rock-chips.com  Fri Jan  8 03:31:36 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Fri,  8 Jan 2016 19:31:36 +0800
Subject: [RESEND RFC PATCH 0/2] Add 100MHz integer multiples of cpu freq table
 for the RK3036
Message-ID: <1452252698-16765-1-git-send-email-zhengxing@rock-chips.com>


Hi,
  We need the cpu changing freqs capability, and considering that emac
ref which is need 50MHz might is under the APLL, meanwhile, we don't
need too many freqs because it may spend more time to find the correct
freq.
  Therefore, we should add 100MHz integer multiples cpu freqs for
emac.


Xing Zheng (2):
  clk: rockchip: rk3036: Add to 100MHz integer multiples of cpu freq
    table
  ARM: dts: rockchip: Update the cpu freqs table

 arch/arm/boot/dts/rk3036.dtsi     |   13 ++++++++++++-
 drivers/clk/rockchip/clk-rk3036.c |   27 ++++++++++++++++++++++++---
 2 files changed, 36 insertions(+), 4 deletions(-)

-- 
1.7.9.5




From zhengxing at rock-chips.com  Fri Jan  8 03:31:37 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Fri,  8 Jan 2016 19:31:37 +0800
Subject: [RESEND RFC PATCH 1/2] clk: rockchip: rk3036: Add to 100MHz integer
 multiples of cpu freq table
In-Reply-To: <1452252698-16765-1-git-send-email-zhengxing@rock-chips.com>
References: <1452252698-16765-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <1452252698-16765-2-git-send-email-zhengxing@rock-chips.com>

We need the cpu changing freqs capability, and considering that emac ref
which is need 50MHz might is under the APLL, meanwhile, we don't need
too many freqs because it may spend more time to find the correct freq.
Therefore, we should add 100MHz integer multiples cpu freqs for emac.

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
---

 drivers/clk/rockchip/clk-rk3036.c |   27 ++++++++++++++++++++++++---
 1 file changed, 24 insertions(+), 3 deletions(-)

diff --git a/drivers/clk/rockchip/clk-rk3036.c b/drivers/clk/rockchip/clk-rk3036.c
index bf9dd9b..f60273c 100644
--- a/drivers/clk/rockchip/clk-rk3036.c
+++ b/drivers/clk/rockchip/clk-rk3036.c
@@ -32,18 +32,22 @@ enum rk3036_plls {
 static struct rockchip_pll_rate_table rk3036_pll_rates[] = {
 	/* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */
 	RK3036_PLL_RATE(1608000000, 1, 67, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1600000000, 6, 400, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1584000000, 1, 66, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1560000000, 1, 65, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1536000000, 1, 64, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1512000000, 1, 63, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1500000000, 6, 375, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1488000000, 1, 62, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1464000000, 1, 61, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1440000000, 1, 60, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1416000000, 1, 59, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1400000000, 6, 350, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1392000000, 1, 58, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1368000000, 1, 57, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1344000000, 1, 56, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1320000000, 1, 55, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1300000000, 6, 325, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1296000000, 1, 54, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1272000000, 1, 53, 1, 1, 1, 0),
 	RK3036_PLL_RATE(1248000000, 1, 52, 1, 1, 1, 0),
@@ -70,8 +74,12 @@ static struct rockchip_pll_rate_table rk3036_pll_rates[] = {
 	RK3036_PLL_RATE( 504000000, 1, 63, 3, 1, 1, 0),
 	RK3036_PLL_RATE( 500000000, 6, 250, 2, 1, 1, 0),
 	RK3036_PLL_RATE( 408000000, 1, 68, 2, 2, 1, 0),
+	RK3036_PLL_RATE( 400000000, 1, 100, 3, 2, 1, 0),
 	RK3036_PLL_RATE( 312000000, 1, 52, 2, 2, 1, 0),
+	RK3036_PLL_RATE( 300000000, 1, 50, 2, 2, 1, 0),
 	RK3036_PLL_RATE( 216000000, 1, 72, 4, 2, 1, 0),
+	RK3036_PLL_RATE( 200000000, 1, 50, 3, 2, 1, 0),
+	RK3036_PLL_RATE( 100000000, 6, 400, 4, 4, 1, 0),
 	RK3036_PLL_RATE(  96000000, 1, 64, 4, 4, 1, 0),
 	{ /* sentinel */ },
 };
@@ -104,9 +112,22 @@ static struct rockchip_pll_rate_table rk3036_pll_rates[] = {
 	}
 
 static struct rockchip_cpuclk_rate_table rk3036_cpuclk_rates[] __initdata = {
-	RK3036_CPUCLK_RATE(816000000, 4),
-	RK3036_CPUCLK_RATE(600000000, 4),
-	RK3036_CPUCLK_RATE(312000000, 4),
+	RK3036_CPUCLK_RATE(1600000000, 8),
+	RK3036_CPUCLK_RATE(1500000000, 7),
+	RK3036_CPUCLK_RATE(1400000000, 7),
+	RK3036_CPUCLK_RATE(1300000000, 6),
+	RK3036_CPUCLK_RATE(1200000000, 6),
+	RK3036_CPUCLK_RATE(1100000000, 5),
+	RK3036_CPUCLK_RATE(1000000000, 5),
+	RK3036_CPUCLK_RATE( 900000000, 4),
+	RK3036_CPUCLK_RATE( 800000000, 4),
+	RK3036_CPUCLK_RATE( 700000000, 3),
+	RK3036_CPUCLK_RATE( 600000000, 3),
+	RK3036_CPUCLK_RATE( 500000000, 3),
+	RK3036_CPUCLK_RATE( 400000000, 3),
+	RK3036_CPUCLK_RATE( 300000000, 2),
+	RK3036_CPUCLK_RATE( 200000000, 1),
+	RK3036_CPUCLK_RATE( 100000000, 1),
 };
 
 static const struct rockchip_cpuclk_reg_data rk3036_cpuclk_data = {
-- 
1.7.9.5




From zhengxing at rock-chips.com  Fri Jan  8 03:31:38 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Fri,  8 Jan 2016 19:31:38 +0800
Subject: [RESEND RFC PATCH 2/2] ARM: dts: rockchip: Update the cpu freqs table
In-Reply-To: <1452252698-16765-1-git-send-email-zhengxing@rock-chips.com>
References: <1452252698-16765-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <1452252698-16765-3-git-send-email-zhengxing@rock-chips.com>

Actually, we need more cpu freqs which are 50MHz integer multiples.

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
---

 arch/arm/boot/dts/rk3036.dtsi |   13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
index 1cb5877..1a773eb 100644
--- a/arch/arm/boot/dts/rk3036.dtsi
+++ b/arch/arm/boot/dts/rk3036.dtsi
@@ -79,7 +79,18 @@
 			resets = <&cru SRST_CORE0>;
 			operating-points = <
 				/* KHz    uV */
-				 816000 1000000
+				1200000 1200000
+				1100000 1200000
+				1000000 1200000
+				 900000 1200000
+				 800000 1200000
+				 700000 1200000
+				 600000 1200000
+				 500000 1200000
+				 400000 1200000
+				 300000 1200000
+				 200000 1200000
+				 100000 1200000
 			>;
 			clock-latency = <40000>;
 			clocks = <&cru ARMCLK>;
-- 
1.7.9.5




From javier at osg.samsung.com  Fri Jan  8 04:13:39 2016
From: javier at osg.samsung.com (Javier Martinez Canillas)
Date: Fri, 8 Jan 2016 09:13:39 -0300
Subject: [PATCH v2 05/12] mmc: pwrseq: Document optional exteral vcc for
 the simple power sequence
In-Reply-To: <1452155155-16232-6-git-send-email-wxt@rock-chips.com>
References: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
 <1452155155-16232-6-git-send-email-wxt@rock-chips.com>
Message-ID: <568FA7F3.9090304@osg.samsung.com>

Hello Caesar,

On 01/07/2016 05:25 AM, Caesar Wang wrote:
> This patch adds the exteral vcc optional for the simple power sequence.
> 
> Some WLAN chips attached to a SDIO interface, need an external vcc
> to be operational. Since this is very common, extend the simple MMC
> power sequence DT binding to support an optional exteral vcc.
> 

s/exteral/external in the subject line and other places.

I think that makes sense to extend the simple power sequence provider
to add a regulator since many SDIO chips may needed it but it seems
from the commit message of patch 06/12 that you don't actually need
a regulator but to toggle a WL_REG_ON pin in your WiFi chip.

I'll answer to that patch though.

Best regards,
-- 
Javier Martinez Canillas
Open Source Group
Samsung Research America


From sjoerd.simons at collabora.co.uk  Fri Jan  8 04:16:46 2016
From: sjoerd.simons at collabora.co.uk (Sjoerd Simons)
Date: Fri,  8 Jan 2016 13:16:46 +0100
Subject: [PATCH] ARM: multi_v7_defconfig: Add rockchip audio support
Message-ID: <1452255406-2439-1-git-send-email-sjoerd.simons@collabora.co.uk>

As the rockchip SPDIF driver landed for 4.4 and the required clock
patches to make it work properly are scheduled for the 4.5 merge window
it seems time to support for audio device found on rockchip boards in
the defconfig as well.

Signed-off-by: Sjoerd Simons <sjoerd.simons at collabora.co.uk>

---

 arch/arm/configs/multi_v7_defconfig | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index f6e104d..089b4da 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -548,6 +548,10 @@ CONFIG_SND_SOC=m
 CONFIG_SND_ATMEL_SOC=m
 CONFIG_SND_ATMEL_SOC_WM8904=m
 CONFIG_SND_SOC_FSL_SAI=m
+CONFIG_SND_SOC_ROCKCHIP=m
+CONFIG_SND_SOC_ROCKCHIP_SPDIF=m
+CONFIG_SND_SOC_ROCKCHIP_MAX98090=m
+CONFIG_SND_SOC_ROCKCHIP_RT5645=m
 CONFIG_SND_SOC_SH4_FSI=m
 CONFIG_SND_SOC_RCAR=m
 CONFIG_SND_SOC_RSRC_CARD=m
@@ -564,6 +568,7 @@ CONFIG_SND_SOC_TEGRA_ALC5632=m
 CONFIG_SND_SOC_TEGRA_MAX98090=m
 CONFIG_SND_SOC_AK4642=m
 CONFIG_SND_SOC_SGTL5000=m
+CONFIG_SND_SOC_SPDIF=m
 CONFIG_SND_SOC_WM8978=m
 CONFIG_USB=y
 CONFIG_USB_XHCI_HCD=y
-- 
2.7.0.rc3



From javier at osg.samsung.com  Fri Jan  8 04:22:31 2016
From: javier at osg.samsung.com (Javier Martinez Canillas)
Date: Fri, 8 Jan 2016 09:22:31 -0300
Subject: [PATCH v2 06/12] mmc: pwrseq: add support for power-on sequencing
 through DT
In-Reply-To: <1452155155-16232-7-git-send-email-wxt@rock-chips.com>
References: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
 <1452155155-16232-7-git-send-email-wxt@rock-chips.com>
Message-ID: <568FAA07.1010509@osg.samsung.com>

Hello Caesar,

On 01/07/2016 05:25 AM, Caesar Wang wrote:
> This patch enables support for power-on sequencing of SDIO
> peripherals through DT.
>

I think the subject line and this first paragraph are misleading since
the simple power sequence provider already supports power-on sequencing.

This patch does not add or enable support but extends the current support
to also enable a regulator as a part of the SDIO chip power on sequencing.
 
> In general, it's quite common that wifi modules and other similar
> peripherals have several signals in addition to the SDIO interface that
> needs wiggling before the module will power on.
> 
> For example:
> we need enable wifi module power to via the WL_REG_ON
> pin, we need enable it as the regulator if this pin is connected to
> the gpio of cpu.
>

This part confuses me, so does your chip have an actual regulator that
needs to be enabled or is just a fake regulator whose gpio property is
used not to enable the regulator but to toggle the WL_REG_ON pin of
the WiFi chip?
 
> Maybe, someone will say that can pull up/down from dts.
> Unfortunately some SoCs can't support pinctrl pull up/down in
> internal.
>

Can you please elaborate on this? AFAIU this limitation is the reason
why you went with the regulator approach so I think it deserve a more
deep explanation.
 
> Anyway, we can add this patch to supprt the power-on sequencing for

s/supprt/support

> sdio.
> 
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
> 

Best regards,
-- 
Javier Martinez Canillas
Open Source Group
Samsung Research America


From arnd at arndb.de  Fri Jan  8 04:53:29 2016
From: arnd at arndb.de (Arnd Bergmann)
Date: Fri, 08 Jan 2016 13:53:29 +0100
Subject: [PATCH] ARM: multi_v7_defconfig: Add rockchip audio support
In-Reply-To: <1452255406-2439-1-git-send-email-sjoerd.simons@collabora.co.uk>
References: <1452255406-2439-1-git-send-email-sjoerd.simons@collabora.co.uk>
Message-ID: <4970673.6FENizv1M6@wuerfel>

On Friday 08 January 2016 13:16:46 Sjoerd Simons wrote:
> As the rockchip SPDIF driver landed for 4.4 and the required clock
> patches to make it work properly are scheduled for the 4.5 merge window
> it seems time to support for audio device found on rockchip boards in
> the defconfig as well.
> 
> Signed-off-by: Sjoerd Simons <sjoerd.simons at collabora.co.uk>
> 

Applied to next/defconfig with Heiko's Ack as discussed on IRC.

	Arnd


From wdc at rock-chips.com  Fri Jan  8 05:12:29 2016
From: wdc at rock-chips.com (David.Wu)
Date: Fri, 8 Jan 2016 21:12:29 +0800
Subject: [PATCH v1 1/3] i2c: rk3x: add calc_divs ops for new version
In-Reply-To: <20160104194042.GD1522@katana>
References: <1449844384-6236-1-git-send-email-wdc@rock-chips.com>
 <566F5FAC.3080404@rock-chips.com>
 <CAHp75VfkteDvC0KUXOXDik9rQeanTXLn9Qq3wux-0rxk5ZotNA@mail.gmail.com>
 <20160104194042.GD1522@katana>
Message-ID: <568FB5BD.9050809@rock-chips.com>

Hi Wolfram, Andy

Thanks for your suggestion.
I will  use i2c_parse_fw_timings() function to replace it.

? 2016/1/5 3:40, Wolfram Sang ??:
>>>> +static int rk3x_i2c_v0_calc_divs(unsigned long clk_rate, unsigned long
>>>> scl_rate,
>>>> +                                unsigned long scl_rise_ns,
>>>> +                                unsigned long scl_fall_ns,
>>>> +                                unsigned long sda_fall_ns,
>> Wolfram did some sturct to assign the parameters from device properties.
>> It might be re-used here.
> Yes, I think it makes sense to convert the driver first to use
> the new i2c_parse_fw_timings() function and see if it fits (or if we need
> to extend it perhaps).
>
> David, does this make sense to you?
>




From mark.rutland at arm.com  Fri Jan  8 06:28:33 2016
From: mark.rutland at arm.com (Mark Rutland)
Date: Fri, 8 Jan 2016 14:28:33 +0000
Subject: [PATCH v2 1/2] dt-bindings: add power-domain header for RK3368
 SoCs
In-Reply-To: <1452278261-16864-2-git-send-email-zhangqing@rock-chips.com>
References: <1452278261-16864-1-git-send-email-zhangqing@rock-chips.com>
 <1452278261-16864-2-git-send-email-zhangqing@rock-chips.com>
Message-ID: <20160108142833.GF3097@leverpostej>

On Fri, Jan 08, 2016 at 10:37:40AM -0800, zhangqing wrote:
> According to a description from TRM, add all the power domains.
> 
> Signed-off-by: zhangqing <zhangqing at rock-chips.com>
> ---
>  include/dt-bindings/power/rk3368-power.h | 28 ++++++++++++++++++++++++++++
>  1 file changed, 28 insertions(+)
>  create mode 100644 include/dt-bindings/power/rk3368-power.h

You also need to update the related binding file, for the new compatible
string.

Mark.

> 
> diff --git a/include/dt-bindings/power/rk3368-power.h b/include/dt-bindings/power/rk3368-power.h
> new file mode 100644
> index 0000000..93633d5
> --- /dev/null
> +++ b/include/dt-bindings/power/rk3368-power.h
> @@ -0,0 +1,28 @@
> +#ifndef __DT_BINDINGS_POWER_RK3368_POWER_H__
> +#define __DT_BINDINGS_POWER_RK3368_POWER_H__
> +
> +/* VD_CORE */
> +#define RK3368_PD_A53_L0	0
> +#define RK3368_PD_A53_L1	1
> +#define RK3368_PD_A53_L2	2
> +#define RK3368_PD_A53_L3	3
> +#define RK3368_PD_SCU_L		4
> +#define RK3368_PD_A53_B0	5
> +#define RK3368_PD_A53_B1	6
> +#define RK3368_PD_A53_B2	7
> +#define RK3368_PD_A53_B3	8
> +#define RK3368_PD_SCU_B		9
> +
> +/* VD_LOGIC */
> +#define RK3368_PD_BUS		10
> +#define RK3368_PD_PERI		11
> +#define RK3368_PD_VIO		12
> +#define RK3368_PD_ALIVE		13
> +#define RK3368_PD_VIDEO		14
> +#define RK3368_PD_GPU_0		15
> +#define RK3368_PD_GPU_1		16
> +
> +/* VD_PMU */
> +#define RK3368_PD_PMU		17
> +
> +#endif
> -- 
> 1.9.1
> 
> 


From heiko at sntech.de  Fri Jan  8 11:16:19 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Fri, 08 Jan 2016 20:16:19 +0100
Subject: [RESEND RFC PATCH 2/2] ARM: dts: rockchip: Update the cpu freqs
 table
In-Reply-To: <1452252698-16765-3-git-send-email-zhengxing@rock-chips.com>
References: <1452252698-16765-1-git-send-email-zhengxing@rock-chips.com>
 <1452252698-16765-3-git-send-email-zhengxing@rock-chips.com>
Message-ID: <4951150.PYM4FhWu6V@phil>

Hi Xing,

Am Freitag, 8. Januar 2016, 19:31:38 schrieb Xing Zheng:
> Actually, we need more cpu freqs which are 50MHz integer multiples.
> 
> Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
> ---
> 
>  arch/arm/boot/dts/rk3036.dtsi |   13 ++++++++++++-
>  1 file changed, 12 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
> index 1cb5877..1a773eb 100644
> --- a/arch/arm/boot/dts/rk3036.dtsi
> +++ b/arch/arm/boot/dts/rk3036.dtsi
> @@ -79,7 +79,18 @@
>  			resets = <&cru SRST_CORE0>;
>  			operating-points = <
>  				/* KHz    uV */
> -				 816000 1000000
> +				1200000 1200000
> +				1100000 1200000
> +				1000000 1200000
> +				 900000 1200000
> +				 800000 1200000
> +				 700000 1200000
> +				 600000 1200000
> +				 500000 1200000
> +				 400000 1200000
> +				 300000 1200000
> +				 200000 1200000
> +				 100000 1200000

what about reducing the voltage as well?

>From rk3288-specific discussions I remember that reducing the frequency 
without touching the voltage, didn't really save so much energy.
And it looks like it was running 816MHz at 1.0V just fine as well.

I guess you should find out which voltages are really necessary for each 
step.


Heiko


From heiko at sntech.de  Fri Jan  8 18:17:11 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sat, 09 Jan 2016 03:17:11 +0100
Subject: [PATCH v2 01/12] ARM: dts: rockchip: set the pinctrl default
 setting for rk3036 i2s
In-Reply-To: <1452155155-16232-2-git-send-email-wxt@rock-chips.com>
References: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
 <1452155155-16232-2-git-send-email-wxt@rock-chips.com>
Message-ID: <8406252.OUY4UcCHur@phil>

Am Donnerstag, 7. Januar 2016, 16:25:44 schrieb Caesar Wang:
> Sometime will hang if you set the i2s pinctrl as the none setting.
> Let's set the pinctrl as the default setting to enable the gpio up/down.
> 
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>

applied to my dts branch for 4.6


From heiko at sntech.de  Fri Jan  8 18:31:33 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sat, 09 Jan 2016 03:31:33 +0100
Subject: [PATCH v2 03/12] ARM: dts: rockchip: override the clocks in i2s
 for kylin board
In-Reply-To: <1452155155-16232-4-git-send-email-wxt@rock-chips.com>
References: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
 <1452155155-16232-4-git-send-email-wxt@rock-chips.com>
Message-ID: <1878139.nbddSBf70A@phil>

Hi Caesar,

Am Donnerstag, 7. Januar 2016, 16:25:46 schrieb Caesar Wang:
> The sysclk seems be incorrect since we use the simple card for kylin
> board.
> 
> The simple card call:
> 
> asoc_simple_card_probe ->
>  asoc_simple_card_dai_link_of ->
>   asoc_simple_card_sub_parse_of ->
> ...
> clk = of_clk_get(args.np, 0);
> if (!IS_ERR(clk))
> dai->sysclk = clk_get_rate(clk);
> 
> The sysclk come from the first clock, then first clock is hclk_i2s
> in i2s for rk3036 dtsi.
> So, we can override the clocks to fit the simple card in here.

It's not that effective, doing that change on a per-board level - as it
would possibly need to be repeated for future boards.
The i2s driver doesn't care, as it uses named clocks, so I instead did
the swap in the rk3036.dtsi for all boards. Can you check if that is ok
for you or if I did overlook something?


Thanks
Heiko

------- 8< -------
Subject: [PATCH] ARM: dts: rockchip: swap i2s clock ordering on rk3036

For sound setups using the simple-card mechanism, the main clock
(sysclk) is expected to be the first element. For the i2s-driver
itself it doesn't matter, as it uses named clocks, so we can just
swap them.

Reported-by: Caesar Wang <wxt at rock-chips.com>
Signed-off-by: Heiko Stuebner <heiko at sntech.de>
---
 arch/arm/boot/dts/rk3036.dtsi | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
index 8f1bb0f..ee457a2 100644
--- a/arch/arm/boot/dts/rk3036.dtsi
+++ b/arch/arm/boot/dts/rk3036.dtsi
@@ -241,8 +241,8 @@
 		interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;
 		#address-cells = <1>;
 		#size-cells = <0>;
-		clock-names = "i2s_hclk", "i2s_clk";
-		clocks = <&cru HCLK_I2S>, <&cru SCLK_I2S>;
+		clock-names = "i2s_clk", "i2s_hclk";
+		clocks = <&cru SCLK_I2S>, <&cru HCLK_I2S>;
 		dmas = <&pdma 0>, <&pdma 1>;
 		dma-names = "tx", "rx";
 		pinctrl-names = "default";
-- 
2.6.4




From heiko at sntech.de  Fri Jan  8 18:39:45 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sat, 09 Jan 2016 03:39:45 +0100
Subject: [PATCH v2 02/12] ARM: dts: rockchip: add the sound codec for
 kylin board
In-Reply-To: <1452155155-16232-3-git-send-email-wxt@rock-chips.com>
References: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
 <1452155155-16232-3-git-send-email-wxt@rock-chips.com>
Message-ID: <37250066.V9Rju7m7TL@phil>

Am Donnerstag, 7. Januar 2016, 16:25:45 schrieb Caesar Wang:
> Support the rt5616 codec for kylin board, but we need
> enable the i2s firstly.
> Also, this patch adds the CPU/CODEC information for simple card
> to make the happy work.
> 
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>

applied to my dts branch for 4.6.

I've adapted the commit message, to make it a bit more clear. [0]


Thanks
Heiko

[0] https://git.kernel.org/cgit/linux/kernel/git/mmind/linux-rockchip.git/commit/?h=v4.6-armsoc/dts32&id=1a21fe2a6f00226d126cd4ba4ac2484208c58b71



From heiko at sntech.de  Fri Jan  8 18:42:03 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sat, 09 Jan 2016 03:42:03 +0100
Subject: [PATCH v2 06/12] mmc: pwrseq: add support for power-on sequencing
 through DT
In-Reply-To: <568FAA07.1010509@osg.samsung.com>
References: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
 <1452155155-16232-7-git-send-email-wxt@rock-chips.com>
 <568FAA07.1010509@osg.samsung.com>
Message-ID: <6960259.dpUH8KWsNS@phil>

Am Freitag, 8. Januar 2016, 09:22:31 schrieb Javier Martinez Canillas:
> > For example:
> > we need enable wifi module power to via the WL_REG_ON
> > pin, we need enable it as the regulator if this pin is connected to
> > the gpio of cpu.
> 
> This part confuses me, so does your chip have an actual regulator that
> needs to be enabled or is just a fake regulator whose gpio property is
> used not to enable the regulator but to toggle the WL_REG_ON pin of
> the WiFi chip?

another option would be to use the reset-gpio-handles. rk3288-veyron and I 
think some Exynos as well use it that way.


> > Maybe, someone will say that can pull up/down from dts.
> > Unfortunately some SoCs can't support pinctrl pull up/down in
> > internal.
> 
> Can you please elaborate on this? AFAIU this limitation is the reason
> why you went with the regulator approach so I think it deserve a more
> deep explanation.

On the rk3036 each pin has an individual unchangable pull direction. So it's 
either no bias or pulling in the predefined direction (the pin_default bias 
option).


Heiko


From zhengxing at rock-chips.com  Fri Jan  8 19:04:36 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Sat, 9 Jan 2016 11:04:36 +0800
Subject: [RESEND RFC PATCH 2/2] ARM: dts: rockchip: Update the cpu freqs
 table
In-Reply-To: <4951150.PYM4FhWu6V@phil>
References: <1452252698-16765-1-git-send-email-zhengxing@rock-chips.com>
 <1452252698-16765-3-git-send-email-zhengxing@rock-chips.com>
 <4951150.PYM4FhWu6V@phil>
Message-ID: <569078C4.8080000@rock-chips.com>

Hi Heiko,

On 2016?01?09? 03:16, Heiko Stuebner wrote:
> Hi Xing,
>
> Am Freitag, 8. Januar 2016, 19:31:38 schrieb Xing Zheng:
>> Actually, we need more cpu freqs which are 50MHz integer multiples.
>>
>> Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
>> ---
>>
>>   arch/arm/boot/dts/rk3036.dtsi |   13 ++++++++++++-
>>   1 file changed, 12 insertions(+), 1 deletion(-)
>>
>> diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
>> index 1cb5877..1a773eb 100644
>> --- a/arch/arm/boot/dts/rk3036.dtsi
>> +++ b/arch/arm/boot/dts/rk3036.dtsi
>> @@ -79,7 +79,18 @@
>>   			resets = <&cru SRST_CORE0>;
>>   			operating-points = <
>>   				/* KHz    uV */
>> -				 816000 1000000
>> +				1200000 1200000
>> +				1100000 1200000
>> +				1000000 1200000
>> +				 900000 1200000
>> +				 800000 1200000
>> +				 700000 1200000
>> +				 600000 1200000
>> +				 500000 1200000
>> +				 400000 1200000
>> +				 300000 1200000
>> +				 200000 1200000
>> +				 100000 1200000
> what about reducing the voltage as well?
>
> >From rk3288-specific discussions I remember that reducing the frequency
> without touching the voltage, didn't really save so much energy.
> And it looks like it was running 816MHz at 1.0V just fine as well.
>
> I guess you should find out which voltages are really necessary for each
> step.
>
>
> Heiko
>
Thank you for your comments. :-)

Yes, these patches currently still in draft stage. Modify the frequency 
of 816MHz is for mac apll patch can be FROMLIST for Kylin Board,
and will work cpu frequency table as a start.

We know you want to set the appropriate frequency of different batches 
of The RK3036 SoCs take some time,so I just tested a fixed
high voltage 1.2v on my evb board, the frequency of the voltage to 
ensure that we can work needed.
We'll start work dynamically adjust the voltage.

Thanks.

- Xing Zheng




From wxt at rock-chips.com  Fri Jan  8 23:27:57 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Sat,  9 Jan 2016 15:27:57 +0800
Subject: [RFC PATCH] ARM: dts: rk3036: override operating-points to 600M on
 kylin board
Message-ID: <1452324477-22828-1-git-send-email-wxt@rock-chips.com>

That's a hack way to make the emac working.
Let the CPU can divide out the clock since the emac depends on
the 50M clock to work.

Moment, we add the hacy way to make the emac working firstly.
We can revert it if we find the apll or other issues.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

 arch/arm/boot/dts/rk3036-kylin.dts | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/arch/arm/boot/dts/rk3036-kylin.dts b/arch/arm/boot/dts/rk3036-kylin.dts
index 992f9ca..1d0441c 100644
--- a/arch/arm/boot/dts/rk3036-kylin.dts
+++ b/arch/arm/boot/dts/rk3036-kylin.dts
@@ -60,6 +60,13 @@
 	status = "okay";
 };
 
+&cpu0 {
+	operating-points = <
+		/* KHz    uV */
+		 600000 1000000
+	>;
+};
+
 &emmc {
 	status = "okay";
 };
-- 
1.9.1



From sjoerd.simons at collabora.co.uk  Sat Jan  9 04:40:34 2016
From: sjoerd.simons at collabora.co.uk (Sjoerd Simons)
Date: Sat, 09 Jan 2016 13:40:34 +0100
Subject: [RFC PATCH v3] clk: defer clk_gets on orphan clocks
In-Reply-To: <1483326.lTmfGmv5pO@diego>
References: <1483326.lTmfGmv5pO@diego>
Message-ID: <1452343234.4120.3.camel@collabora.co.uk>

On Tue, 2015-11-24 at 13:42 +0100, Heiko St?bner wrote:
> Orphan clocks or children of orphan clocks don't have rate
> information at
> all and can produce strange results if they're allowed to be used and
> the
> parent becomes available later on.
> 
> This change, based on one from Stephen Boyd, defers
> __clk_create_clk()
> calls on orphan clocks in all regular cases.
> 
> One special case that gets handled, is accessing such orphan clocks
> when
> handling assigned-clocks configurations. In the boot-defaults it may
> be
> the case that a clock is connected to an orphan parent which then
> might
> be needed to get reparented to an actually usable clock using
> assigned-clock-parents. In this case even orphaned clocks should be
> usable, but only for the set-parent case.
> 
> The added of_clk_get_from_provider_with_orphans() is only available
> to ccf internal parts to prevent abuse.
> 
> Signed-off-by: Heiko Stuebner <heiko.stuebner at collabora.com>

Tested-by: Sjoerd Simons <sjoerd.simons at collabora.co.uk>

Just tested this on a rock 2 with the RTC as a module (which is the
provider of the 32k clock on this board). This solves both unreliably
probing of the wireless card and much more importantly the thermal
sensors.

Would be great to see this move out of the RFC stage at some point :)


> ---
> This of course will still break sunxi, hence the RFC state for now.
> As there is work pending for critical/handed-off clocks, I'm hoping
> this can move forward at some point :-) .
> 
> I'd mainly like to get feedback if the assigned-clocks case looks ok
> or should move somewhere else.
> 
> changes in v3:
> - add special handling for assigned-clock-parents to allow
> reparenting
> ? of orphaned child-clocks.
> 
> changes in v2 (Stephens version):
> - find a better place for the deferral
> 
> ?drivers/clk/clk-conf.c |??5 +++--
> ?drivers/clk/clk.c??????| 48 ++++++++++++++++++++++++++++++++++++++
> ----------
> ?drivers/clk/clk.h??????| 10 +++++++---
> ?drivers/clk/clkdev.c???|??4 ++--
> ?4 files changed, 50 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/clk/clk-conf.c b/drivers/clk/clk-conf.c
> index 43a218f..60ebfd9 100644
> --- a/drivers/clk/clk-conf.c
> +++ b/drivers/clk/clk-conf.c
> @@ -13,6 +13,7 @@
> ?#include <linux/device.h>
> ?#include <linux/of.h>
> ?#include <linux/printk.h>
> +#include "clk.h"
> ?
> ?static int __set_clk_parents(struct device_node *node, bool
> clk_supplier)
> ?{
> @@ -38,7 +39,7 @@ static int __set_clk_parents(struct device_node
> *node, bool clk_supplier)
> ?		}
> ?		if (clkspec.np == node && !clk_supplier)
> ?			return 0;
> -		pclk = of_clk_get_from_provider(&clkspec);
> +		pclk =
> of_clk_get_from_provider_with_orphans(&clkspec);
> ?		if (IS_ERR(pclk)) {
> ?			pr_warn("clk: couldn't get parent clock %d
> for %s\n",
> ?				index, node->full_name);
> @@ -53,7 +54,7 @@ static int __set_clk_parents(struct device_node
> *node, bool clk_supplier)
> ?			rc = 0;
> ?			goto err;
> ?		}
> -		clk = of_clk_get_from_provider(&clkspec);
> +		clk =
> of_clk_get_from_provider_with_orphans(&clkspec);
> ?		if (IS_ERR(clk)) {
> ?			pr_warn("clk: couldn't get parent clock %d
> for %s\n",
> ?				index, node->full_name);
> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> index f13c3f4..46760cb 100644
> --- a/drivers/clk/clk.c
> +++ b/drivers/clk/clk.c
> @@ -2476,15 +2476,11 @@ out:
> ?	return ret;
> ?}
> ?
> -struct clk *__clk_create_clk(struct clk_hw *hw, const char *dev_id,
> -			?????const char *con_id)
> +static struct clk *clk_hw_create_clk(struct clk_hw *hw, const char
> *dev_id,
> +				?????const char *con_id)
> ?{
> ?	struct clk *clk;
> ?
> -	/* This is to allow this function to be chained to others */
> -	if (!hw || IS_ERR(hw))
> -		return (struct clk *) hw;
> -
> ?	clk = kzalloc(sizeof(*clk), GFP_KERNEL);
> ?	if (!clk)
> ?		return ERR_PTR(-ENOMEM);
> @@ -2501,6 +2497,19 @@ struct clk *__clk_create_clk(struct clk_hw
> *hw, const char *dev_id,
> ?	return clk;
> ?}
> ?
> +struct clk *__clk_create_clk(struct clk_hw *hw, const char *dev_id,
> +			?????const char *con_id, bool with_orphans)
> +{
> +	/* This is to allow this function to be chained to others */
> +	if (!hw || IS_ERR(hw))
> +		return (struct clk *) hw;
> +
> +	if (hw->core->orphan && !with_orphans)
> +		return ERR_PTR(-EPROBE_DEFER);
> +
> +	return clk_hw_create_clk(hw, dev_id, con_id);
> +}
> +
> ?void __clk_free_clk(struct clk *clk)
> ?{
> ?	clk_prepare_lock();
> @@ -2569,7 +2578,7 @@ struct clk *clk_register(struct device *dev,
> struct clk_hw *hw)
> ?
> ?	INIT_HLIST_HEAD(&core->clks);
> ?
> -	hw->clk = __clk_create_clk(hw, NULL, NULL);
> +	hw->clk = clk_hw_create_clk(hw, NULL, NULL);
> ?	if (IS_ERR(hw->clk)) {
> ?		ret = PTR_ERR(hw->clk);
> ?		goto fail_parent_names_copy;
> @@ -3002,7 +3011,8 @@ void of_clk_del_provider(struct device_node
> *np)
> ?EXPORT_SYMBOL_GPL(of_clk_del_provider);
> ?
> ?struct clk *__of_clk_get_from_provider(struct of_phandle_args
> *clkspec,
> -				???????const char *dev_id, const
> char *con_id)
> +				???????const char *dev_id, const
> char *con_id,
> +				???????bool with_orphans)
> ?{
> ?	struct of_clk_provider *provider;
> ?	struct clk *clk = ERR_PTR(-EPROBE_DEFER);
> @@ -3017,7 +3027,7 @@ struct clk *__of_clk_get_from_provider(struct
> of_phandle_args *clkspec,
> ?			clk = provider->get(clkspec, provider-
> >data);
> ?		if (!IS_ERR(clk)) {
> ?			clk = __clk_create_clk(__clk_get_hw(clk),
> dev_id,
> -					???????con_id);
> +					???????con_id,
> with_orphans);
> ?
> ?			if (!IS_ERR(clk) && !__clk_get(clk)) {
> ?				__clk_free_clk(clk);
> @@ -3042,7 +3052,25 @@ struct clk *__of_clk_get_from_provider(struct
> of_phandle_args *clkspec,
> ? */
> ?struct clk *of_clk_get_from_provider(struct of_phandle_args
> *clkspec)
> ?{
> -	return __of_clk_get_from_provider(clkspec, NULL, __func__);
> +	return __of_clk_get_from_provider(clkspec, NULL, __func__,
> false);
> +}
> +
> +/**
> + * of_clk_get_from_provider_with_orphans() - Lookup clock from a
> clock provider
> + * @clkspec: pointer to a clock specifier data structure
> + *
> + * This function looks up a struct clk from the registered list of
> clock
> + * providers, an input is a clock specifier data structure as
> returned
> + * from the of_parse_phandle_with_args() function call.
> + *
> + * The difference to of_clk_get_from_provider() is that this
> function will
> + * also successfully lookup orphan-clocks, as it in some cases may
> be
> + * necessary to access such orphan-clocks as well.
> + */
> +struct clk *
> +of_clk_get_from_provider_with_orphans(struct of_phandle_args
> *clkspec)
> +{
> +	return __of_clk_get_from_provider(clkspec, NULL, __func__,
> true);
> ?}
> ?
> ?int of_clk_get_parent_count(struct device_node *np)
> diff --git a/drivers/clk/clk.h b/drivers/clk/clk.h
> index 00b35a1..5a70f09 100644
> --- a/drivers/clk/clk.h
> +++ b/drivers/clk/clk.h
> @@ -13,17 +13,21 @@ struct clk_hw;
> ?
> ?#if defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK)
> ?struct clk *__of_clk_get_from_provider(struct of_phandle_args
> *clkspec,
> -				???????const char *dev_id, const
> char *con_id);
> +				???????const char *dev_id, const
> char *con_id,
> +				???????bool with_orphans);
> +struct clk *
> +of_clk_get_from_provider_with_orphans(struct of_phandle_args
> *clkspec);
> ?#endif
> ?
> ?#ifdef CONFIG_COMMON_CLK
> ?struct clk *__clk_create_clk(struct clk_hw *hw, const char *dev_id,
> -			?????const char *con_id);
> +			?????const char *con_id, bool with_orphans);
> ?void __clk_free_clk(struct clk *clk);
> ?#else
> ?/* All these casts to avoid ifdefs in clkdev... */
> ?static inline struct clk *
> -__clk_create_clk(struct clk_hw *hw, const char *dev_id, const char
> *con_id)
> +__clk_create_clk(struct clk_hw *hw, const char *dev_id, const char
> *con_id,
> +		?bool with_orphans)
> ?{
> ?	return (struct clk *)hw;
> ?}
> diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c
> index 779b6ff..eda20c2 100644
> --- a/drivers/clk/clkdev.c
> +++ b/drivers/clk/clkdev.c
> @@ -43,7 +43,7 @@ static struct clk *__of_clk_get(struct device_node
> *np, int index,
> ?	if (rc)
> ?		return ERR_PTR(rc);
> ?
> -	clk = __of_clk_get_from_provider(&clkspec, dev_id, con_id);
> +	clk = __of_clk_get_from_provider(&clkspec, dev_id, con_id,
> false);
> ?	of_node_put(clkspec.np);
> ?
> ?	return clk;
> @@ -177,7 +177,7 @@ struct clk *clk_get_sys(const char *dev_id, const
> char *con_id)
> ?	if (!cl)
> ?		goto out;
> ?
> -	clk = __clk_create_clk(cl->clk_hw, dev_id, con_id);
> +	clk = __clk_create_clk(cl->clk_hw, dev_id, con_id, false);
> ?	if (IS_ERR(clk))
> ?		goto out;
> ?

-- 
Sjoerd Simons
Collabora Ltd.


From sjoerd.simons at collabora.co.uk  Sat Jan  9 04:54:16 2016
From: sjoerd.simons at collabora.co.uk (Sjoerd Simons)
Date: Sat,  9 Jan 2016 13:54:16 +0100
Subject: [PATCH 0/2] Add support for wireless on Radxa Rock2
Message-ID: <1452344058-27434-1-git-send-email-sjoerd.simons@collabora.co.uk>

First patch improved the modelling of the power supplies on the Rock2
some a bit and adds the iodomain setup needed for wireless to work.
Second patch adds the actual updates for the wireless setup on the rock2
square board.

With current kernels the probing of the wifi card is unfortunately not
reliable as the 32k clock is provided by the external RTC module, this
is fixed by a patch from Heiko still in RFC state[0] to properly defer
things as needed. Also things only work if the io domains get
initialized before the sdio bus is probed as their is no explicit
dependency, fixing that is for a later patchset.

0: http://lists.infradead.org/pipermail/linux-arm-kernel/2015-November/387833.html


Sjoerd Simons (2):
  ARM: dts: rockchip: Add the iodomains for the Rock2 SOM
  ARM: dts: rockchip: Add the SDIO wifi on Radxa Rock2 square

 arch/arm/boot/dts/rk3288-rock2-som.dtsi   | 26 ++++++++++++++++++++++++
 arch/arm/boot/dts/rk3288-rock2-square.dts | 33 ++++++++++++++++++++++++++++++-
 2 files changed, 58 insertions(+), 1 deletion(-)

-- 
2.7.0.rc3



From sjoerd.simons at collabora.co.uk  Sat Jan  9 04:54:17 2016
From: sjoerd.simons at collabora.co.uk (Sjoerd Simons)
Date: Sat,  9 Jan 2016 13:54:17 +0100
Subject: [PATCH 1/2] ARM: dts: rockchip: Add the iodomains for the Rock2 SOM
In-Reply-To: <1452344058-27434-1-git-send-email-sjoerd.simons@collabora.co.uk>
References: <1452344058-27434-1-git-send-email-sjoerd.simons@collabora.co.uk>
Message-ID: <1452344058-27434-2-git-send-email-sjoerd.simons@collabora.co.uk>

Add the IO domain configuration for the Rock2 SOM and model the fixed
regulator used as the vqmmc for the EMMC device.

Signed-off-by: Sjoerd Simons <sjoerd.simons at collabora.co.uk>
---

 arch/arm/boot/dts/rk3288-rock2-som.dtsi | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/arch/arm/boot/dts/rk3288-rock2-som.dtsi b/arch/arm/boot/dts/rk3288-rock2-som.dtsi
index 1813b7c3..4e93316 100644
--- a/arch/arm/boot/dts/rk3288-rock2-som.dtsi
+++ b/arch/arm/boot/dts/rk3288-rock2-som.dtsi
@@ -61,6 +61,31 @@
 		clock-output-names = "ext_gmac";
 	};
 
+	io_domains: io-domains {
+		compatible = "rockchip,rk3288-io-voltage-domain";
+		rockchip,grf = <&grf>;
+
+		audio-supply = <&vcc_io>;
+		bb-supply = <&vcc_io>;
+		dvp-supply = <&vcc_18>;
+		flash0-supply = <&vcc_flash>;
+		flash1-supply = <&vccio_pmu>;
+		gpio30-supply = <&vccio_pmu>;
+		gpio1830 = <&vcc_io>;
+		lcdc-supply = <&vcc_io>;
+		sdcard-supply = <&vccio_sd>;
+		wifi-supply = <&vcc_18>;
+	};
+
+	vcc_flash: flash-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_sys";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+		startup-delay-us = <150>;
+		vin-supply = <&vcc_io>;
+	};
+
 	vcc_sys: vsys-regulator {
 		compatible = "regulator-fixed";
 		regulator-name = "vcc_sys";
@@ -85,6 +110,7 @@
 	pinctrl-names = "default";
 	pinctrl-0 = <&emmc_clk &emmc_cmd &emmc_bus8>;
 	vmmc-supply = <&vcc_io>;
+	vqmmc-supply = <&vcc_flash>;
 	status = "okay";
 };
 
-- 
2.7.0.rc3



From sjoerd.simons at collabora.co.uk  Sat Jan  9 04:54:18 2016
From: sjoerd.simons at collabora.co.uk (Sjoerd Simons)
Date: Sat,  9 Jan 2016 13:54:18 +0100
Subject: [PATCH 2/2] ARM: dts: rockchip: Add the SDIO wifi on Radxa Rock2
 square
In-Reply-To: <1452344058-27434-1-git-send-email-sjoerd.simons@collabora.co.uk>
References: <1452344058-27434-1-git-send-email-sjoerd.simons@collabora.co.uk>
Message-ID: <1452344058-27434-3-git-send-email-sjoerd.simons@collabora.co.uk>

Enable the sdio0 slot on the Rock2 square which has a broadcom wifi chip
attached and add a power sequence to enable the wifi chip and turn on
the required 32k clock.

Signed-off-by: Sjoerd Simons <sjoerd.simons at collabora.co.uk>

---

 arch/arm/boot/dts/rk3288-rock2-square.dts | 33 ++++++++++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/rk3288-rock2-square.dts b/arch/arm/boot/dts/rk3288-rock2-square.dts
index 8af35c8..dd53af3 100644
--- a/arch/arm/boot/dts/rk3288-rock2-square.dts
+++ b/arch/arm/boot/dts/rk3288-rock2-square.dts
@@ -63,6 +63,16 @@
 		#sound-dai-cells = <0>;
 	};
 
+	sdio_pwrseq: sdio-pwrseq {
+		compatible = "mmc-pwrseq-simple";
+		clocks = <&hym8563>;
+		clock-names = "xin32k";
+		reset-gpios = <&gpio4 28 GPIO_ACTIVE_LOW>;
+
+		pinctrl-names = "default";
+		pinctrl-0 = <&wifi_enable>;
+	};
+
 	vcc_usb_host: vcc-host-regulator {
 		compatible = "regulator-fixed";
 		enable-active-high;
@@ -88,6 +98,21 @@
 	};
 };
 
+&sdio0 {
+	bus-width = <4>;
+	cap-sd-highspeed;
+	cap-sdio-irq;
+	disable-wp;
+	mmc-pwrseq = <&sdio_pwrseq>;
+	non-removable;
+	num-slots = <1>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&sdio0_bus4 &sdio0_cmd &sdio0_clk &sdio0_int>;
+	vmmc-supply = <&vcc_io>;
+	vqmmc-supply = <&vcc_18>;
+	status = "okay";
+};
+
 &sdmmc {
 	bus-width = <4>;
 	cap-mmc-highspeed;
@@ -112,7 +137,7 @@
 };
 
 &i2c0 {
-	hym8563 at 51 {
+	hym8563: hym8563 at 51 {
 		compatible = "haoyu,hym8563";
 		reg = <0x51>;
 		#clock-cells = <0>;
@@ -148,6 +173,12 @@
 			rockchip,pins = <7 11 RK_FUNC_GPIO &pcfg_pull_none>;
 		};
 	};
+
+	sdio {
+		wifi_enable: wifi-enable {
+			rockchip,pins = <4 28 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
 };
 
 &spdif {
-- 
2.7.0.rc3



From sjoerd.simons at collabora.co.uk  Sat Jan  9 11:28:02 2016
From: sjoerd.simons at collabora.co.uk (Sjoerd Simons)
Date: Sat,  9 Jan 2016 20:28:02 +0100
Subject: [PATCH] ARM: multi_v7_defconfig: Change RTC_DRV_HYM8563 to builtin
Message-ID: <1452367683-11160-1-git-send-email-sjoerd.simons@collabora.co.uk>

Apart from providing an RTC the hym8563 chip on some rockchip boards
(e.g. rock2 square and firefly) also provide the 32khz clock used by the
SoCs thermal sensors.

Switch the driver from a module to builtin such that the thermal
protection can work even if modules aren't used/loaded.

Signed-off-by: Sjoerd Simons <sjoerd.simons at collabora.co.uk>

---

 arch/arm/configs/multi_v7_defconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index f6e104d..0fcb1dd 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -650,7 +650,7 @@ CONFIG_EDAC_HIGHBANK_L2=y
 CONFIG_RTC_CLASS=y
 CONFIG_RTC_DRV_AS3722=y
 CONFIG_RTC_DRV_DS1307=y
-CONFIG_RTC_DRV_HYM8563=m
+CONFIG_RTC_DRV_HYM8563=y
 CONFIG_RTC_DRV_MAX8907=y
 CONFIG_RTC_DRV_MAX8997=m
 CONFIG_RTC_DRV_MAX77686=y
-- 
2.7.0.rc3



From heiko at sntech.de  Sat Jan  9 11:33:51 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sat, 09 Jan 2016 20:33:51 +0100
Subject: [PATCH] ARM: multi_v7_defconfig: Change RTC_DRV_HYM8563 to builtin
In-Reply-To: <1452367683-11160-1-git-send-email-sjoerd.simons@collabora.co.uk>
References: <1452367683-11160-1-git-send-email-sjoerd.simons@collabora.co.uk>
Message-ID: <4578616.QcQm44RoCq@phil>

Am Samstag, 9. Januar 2016, 20:28:02 schrieb Sjoerd Simons:
> Apart from providing an RTC the hym8563 chip on some rockchip boards
> (e.g. rock2 square and firefly) also provide the 32khz clock used by the
> SoCs thermal sensors.
> 
> Switch the driver from a module to builtin such that the thermal
> protection can work even if modules aren't used/loaded.
> 
> Signed-off-by: Sjoerd Simons <sjoerd.simons at collabora.co.uk>

Personally I'm ok with the reasoning for requiring rtc to be build in,
but I guess others might disagree. Nevertheless

Acked-by: Heiko Stuebner <heiko at sntech.de>

and we'll let armsoc-people decide ;-) .


Ideally we would do the same for the rk808 rtc as well - as it is exactly 
the same argument / case.


Heiko



From heiko at sntech.de  Sat Jan  9 12:10:21 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sat, 09 Jan 2016 21:10:21 +0100
Subject: [PATCH v2 0/5] fix some clock configuration for the RK3036
 platform
In-Reply-To: <1452169057-24467-1-git-send-email-zhengxing@rock-chips.com>
References: <1452169057-24467-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <2423996.DXmZSXDRX7@phil>

Hi Xing

Am Donnerstag, 7. Januar 2016, 20:17:32 schrieb Xing Zheng:
>   In the development work, we found that some of the previous
> incorrect clock configuration on the RK3036 platform, we should
> fix them.

I've applied patches 1-4 as fixes for 4.5 [0] and will apply patch 5
regularly once 4.5-rc1 is released and I have stable base again.


> Xing Zheng (5):
>   clk: rockchip: rk3036: fix the FLAGs for clock mux
>   clk: rockchip: rk3036: fix uarts clock error
>   clk: rockchip: rk3036: fix the div offset for emac clock
>   clk: rockchip: rk3036: rename emac ext source clock

please make sure to also update the binding document in such cases.
I've done it myself for this one.

[for people reading along: the rk3036 clock-driver + binding is new in
4.5 so we should be ok to fix the naming before it is actually released]


Heiko


[0] https://git.kernel.org/cgit/linux/kernel/git/mmind/linux-rockchip.git/log/?h=v4.5-clk/fixes




From heiko at sntech.de  Sat Jan  9 14:26:43 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sat, 09 Jan 2016 23:26:43 +0100
Subject: [PATCH] clk: rockchip: fix some clock gates for rk3368
In-Reply-To: <1452158287-18742-1-git-send-email-wxt@rock-chips.com>
References: <1452158287-18742-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1873444.tpQzgfaHRN@phil>

Am Donnerstag, 7. Januar 2016, 17:18:07 schrieb Caesar Wang:
> From: Jianqun xu <jay.xu at rock-chips.com>
> 
> Reference to the Rockchip RK3368 TRM v1.1, some clock
> gates need to be updated.
> 
> Signed-off-by: Jianqun xu <jay.xu at rock-chips.com>
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>

applied to my clk-fixes branch for 4.5


Thanks
Heiko


From heiko at sntech.de  Sat Jan  9 14:38:58 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sat, 09 Jan 2016 23:38:58 +0100
Subject: [PATCH v2] ARM: dts: rockchip: Adding LEDs handling via leds-gpio
 for the Radxa Rock2 Square
In-Reply-To: <1451553232-2533-1-git-send-email-romain.perier@gmail.com>
References: <1451553232-2533-1-git-send-email-romain.perier@gmail.com>
Message-ID: <3350223.M0j5cTJeGc@phil>

Am Donnerstag, 31. Dezember 2015, 10:13:52 schrieb Romain Perier:
> Describe the two user-controllable LEDs on Rock2 Square boards.
> All information have been retrieved from the schematics and the vendor
> devicetree. The default-triggers mimic the behaviour of the vendor-kernel
> to keep functionalities in sync.
> 
> Signed-off-by: Romain Perier <romain.perier at gmail.com>

applied to my dts32-branch for 4.6 with Sjoerd's Review


Thanks
Heiko


From heiko at sntech.de  Sat Jan  9 14:45:52 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sat, 09 Jan 2016 23:45:52 +0100
Subject: [PATCH] ARM: multi_v7_defconfig: Change RTC_DRV_HYM8563 to builtin
In-Reply-To: <4578616.QcQm44RoCq@phil>
References: <1452367683-11160-1-git-send-email-sjoerd.simons@collabora.co.uk>
 <4578616.QcQm44RoCq@phil>
Message-ID: <2746599.O3XccuMTWz@phil>

Am Samstag, 9. Januar 2016, 20:33:51 schrieb Heiko Stuebner:
> Am Samstag, 9. Januar 2016, 20:28:02 schrieb Sjoerd Simons:
> > Apart from providing an RTC the hym8563 chip on some rockchip boards
> > (e.g. rock2 square and firefly) also provide the 32khz clock used by the
> > SoCs thermal sensors.
> > 
> > Switch the driver from a module to builtin such that the thermal
> > protection can work even if modules aren't used/loaded.
> > 
> > Signed-off-by: Sjoerd Simons <sjoerd.simons at collabora.co.uk>
> 
> Personally I'm ok with the reasoning for requiring rtc to be build in,
> but I guess others might disagree. Nevertheless
> 
> Acked-by: Heiko Stuebner <heiko at sntech.de>
> 
> and we'll let armsoc-people decide ;-) .
> 
> 
> Ideally we would do the same for the rk808 rtc as well - as it is exactly
> the same argument / case.

just so nobody gets the wrong idea, on the rk808, the needed clkout is 
separate in CONFIG_COMMON_CLK_RK808, not in the rtc. Still currently build 
as module, so depending on if we can build-in the hym8563, we should change 
the rk808-clkout accordingly.


From michael at amarulasolutions.com  Sat Jan  9 14:47:58 2016
From: michael at amarulasolutions.com (Michael Trimarchi)
Date: Sat,  9 Jan 2016 23:47:58 +0100
Subject: [PATCH] ASoC: rockchip: i2s: Add SNDRV_PCM_FMTBIT_S32_LE support
Message-ID: <1452379678-14160-1-git-send-email-michael@amarulasolutions.com>

Signed-off-by: Michael Trimarchi <michael at amarulasolutions.com>
---
 sound/soc/rockchip/rockchip_i2s.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c
index 58ee645..c35700f 100644
--- a/sound/soc/rockchip/rockchip_i2s.c
+++ b/sound/soc/rockchip/rockchip_i2s.c
@@ -242,6 +242,9 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
 	case SNDRV_PCM_FORMAT_S24_LE:
 		val |= I2S_TXCR_VDW(24);
 		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		val |= I2S_TXCR_VDW(32);
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -360,7 +363,8 @@ static struct snd_soc_dai_driver rockchip_i2s_dai = {
 		.formats = (SNDRV_PCM_FMTBIT_S8 |
 			    SNDRV_PCM_FMTBIT_S16_LE |
 			    SNDRV_PCM_FMTBIT_S20_3LE |
-			    SNDRV_PCM_FMTBIT_S24_LE),
+			    SNDRV_PCM_FMTBIT_S24_LE |
+			    SNDRV_PCM_FMTBIT_S32_LE),
 	},
 	.capture = {
 		.stream_name = "Capture",
@@ -370,7 +374,8 @@ static struct snd_soc_dai_driver rockchip_i2s_dai = {
 		.formats = (SNDRV_PCM_FMTBIT_S8 |
 			    SNDRV_PCM_FMTBIT_S16_LE |
 			    SNDRV_PCM_FMTBIT_S20_3LE |
-			    SNDRV_PCM_FMTBIT_S24_LE),
+			    SNDRV_PCM_FMTBIT_S24_LE |
+			    SNDRV_PCM_FMTBIT_S32_LE),
 	},
 	.ops = &rockchip_i2s_dai_ops,
 	.symmetric_rates = 1,
-- 
2.7.0



From zhengxing at rock-chips.com  Sat Jan  9 23:22:37 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Sun, 10 Jan 2016 15:22:37 +0800
Subject: [PATCH] dt-bindings: Fix the external clock name for the RK3036 EMAC
Message-ID: <1452410557-29208-1-git-send-email-zhengxing@rock-chips.com>

Because the RK3036 doesn't support EMAC, and only support the RMII mode
in the EMAC IP, we refer to the TRM CRU and update the new external
clock name for EMAC.

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
---

 .../bindings/clock/rockchip,rk3036-cru.txt         |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/clock/rockchip,rk3036-cru.txt b/Documentation/devicetree/bindings/clock/rockchip,rk3036-cru.txt
index ace0599..20df350 100644
--- a/Documentation/devicetree/bindings/clock/rockchip,rk3036-cru.txt
+++ b/Documentation/devicetree/bindings/clock/rockchip,rk3036-cru.txt
@@ -30,7 +30,7 @@ that they are defined using standard clock bindings with following
 clock-output-names:
  - "xin24m" - crystal input - required,
  - "ext_i2s" - external I2S clock - optional,
- - "ext_gmac" - external GMAC clock - optional
+ - "rmii_clkin" - external EMAC clock - optional
 
 Example: Clock controller node:
 
-- 
1.7.9.5




From zhengxing at rock-chips.com  Sat Jan  9 23:26:18 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Sun, 10 Jan 2016 15:26:18 +0800
Subject: [PATCH v2 0/5] fix some clock configuration for the RK3036
 platform
In-Reply-To: <2423996.DXmZSXDRX7@phil>
References: <1452169057-24467-1-git-send-email-zhengxing@rock-chips.com>
 <2423996.DXmZSXDRX7@phil>
Message-ID: <5692079A.50007@rock-chips.com>

Hi Heiko,

On 2016?01?10? 04:10, Heiko Stuebner wrote:
> Hi Xing
>
> Am Donnerstag, 7. Januar 2016, 20:17:32 schrieb Xing Zheng:
>>    In the development work, we found that some of the previous
>> incorrect clock configuration on the RK3036 platform, we should
>> fix them.
> I've applied patches 1-4 as fixes for 4.5 [0] and will apply patch 5
> regularly once 4.5-rc1 is released and I have stable base again.
>
>
>> Xing Zheng (5):
>>    clk: rockchip: rk3036: fix the FLAGs for clock mux
>>    clk: rockchip: rk3036: fix uarts clock error
>>    clk: rockchip: rk3036: fix the div offset for emac clock
>>    clk: rockchip: rk3036: rename emac ext source clock
> please make sure to also update the binding document in such cases.
> I've done it myself for this one.
>
> [for people reading along: the rk3036 clock-driver + binding is new in
> 4.5 so we should be ok to fix the naming before it is actually released]
>
>
> Heiko
>
>
> [0] https://git.kernel.org/cgit/linux/kernel/git/mmind/linux-rockchip.git/log/?h=v4.5-clk/fixes
>
OK, I have updated the rockchip,rk3036-cru.txt and sent a patch to the 
upstream.

Thanks.



From heiko at sntech.de  Sun Jan 10 01:59:13 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sun, 10 Jan 2016 10:59:13 +0100
Subject: [PATCH] dt-bindings: Fix the external clock name for the RK3036
 EMAC
In-Reply-To: <1452410557-29208-1-git-send-email-zhengxing@rock-chips.com>
References: <1452410557-29208-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <4001585.V8iyL2PDAE@phil>

Hi Xing,

Am Sonntag, 10. Januar 2016, 15:22:37 schrieb Xing Zheng:
> Because the RK3036 doesn't support EMAC, and only support the RMII mode
> in the EMAC IP, we refer to the TRM CRU and update the new external
> clock name for EMAC.
> 
> Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>

as I said in the other mail, I already fixed the original patch myself :-)
So I'm just going to drop this one.


Heiko

https://git.kernel.org/cgit/linux/kernel/git/mmind/linux-rockchip.git/commit/?h=v4.5-clk/fixes&id=88807906af108a8384d77ad9cfb9421f164ea931



From zhengxing at rock-chips.com  Sun Jan 10 02:29:49 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Sun, 10 Jan 2016 18:29:49 +0800
Subject: [PATCH] dt-bindings: Fix the external clock name for the RK3036
 EMAC
In-Reply-To: <4001585.V8iyL2PDAE@phil>
References: <1452410557-29208-1-git-send-email-zhengxing@rock-chips.com>
 <4001585.V8iyL2PDAE@phil>
Message-ID: <E6BB80D3-5A62-4BE1-BFC2-74DAC9213C95@rock-chips.com>


Hi Heiko,

> ? 2016?1?10??17:59?Heiko Stuebner <heiko at sntech.de> ???
> 
> Hi Xing,
> 
> Am Sonntag, 10. Januar 2016, 15:22:37 schrieb Xing Zheng:
>> Because the RK3036 doesn't support EMAC, and only support the RMII mode
>> in the EMAC IP, we refer to the TRM CRU and update the new external
>> clock name for EMAC.
>> 
>> Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
> 
> as I said in the other mail, I already fixed the original patch myself :-)
> So I'm just going to drop this one.
> 
> 
> Heiko
> 
> https://git.kernel.org/cgit/linux/kernel/git/mmind/linux-rockchip.git/commit/?h=v4.5-clk/fixes&id=88807906af108a8384d77ad9cfb9421f164ea931
> 
I am sorry to misunderstand.

OK, thank you for your help.  :)


From zhengxing at rock-chips.com  Sun Jan 10 02:36:12 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Sun, 10 Jan 2016 18:36:12 +0800
Subject: [PATCH] dt-bindings: Fix the external clock name for the RK3036
 EMAC
In-Reply-To: <E6BB80D3-5A62-4BE1-BFC2-74DAC9213C95@rock-chips.com>
References: <1452410557-29208-1-git-send-email-zhengxing@rock-chips.com>
 <4001585.V8iyL2PDAE@phil>
 <E6BB80D3-5A62-4BE1-BFC2-74DAC9213C95@rock-chips.com>
Message-ID: <B588B7A8-EC8A-44A5-AEDF-F75AB5361F01@rock-chips.com>

Hi Heiko,

> ? 2016?1?10??18:29?Xing Zheng <zhengxing at rock-chips.com> ???
> 
> 
> Hi Heiko,
> 
>> ? 2016?1?10??17:59?Heiko Stuebner <heiko at sntech.de> ???
>> 
>> Hi Xing,
>> 
>> Am Sonntag, 10. Januar 2016, 15:22:37 schrieb Xing Zheng:
>>> Because the RK3036 doesn't support EMAC, and only support the RMII mode
>>> in the EMAC IP, we refer to the TRM CRU and update the new external
>>> clock name for EMAC.
>>> 
>>> Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
>> 
>> as I said in the other mail, I already fixed the original patch myself :-)
>> So I'm just going to drop this one.
>> 
>> 
>> Heiko
>> 
>> https://git.kernel.org/cgit/linux/kernel/git/mmind/linux-rockchip.git/commit/?h=v4.5-clk/fixes&id=88807906af108a8384d77ad9cfb9421f164ea931
> I am sorry to misunderstand.
> 
> OK, thank you for your help.  :)

Your patch is:
- - "ext_gmac" - external GMAC clock - optional
+ - "rmii_clkin" - external GMAC clock - optional

I think that these should be like this:
- - "ext_gmac" - external GMAC clock - optional
+ - "rmii_clkin" - external EMAC clock - optional

Thanks. :)


From heiko at sntech.de  Sun Jan 10 10:23:40 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sun, 10 Jan 2016 19:23:40 +0100
Subject: [PATCH] dt-bindings: Fix the external clock name for the RK3036
 EMAC
In-Reply-To: <B588B7A8-EC8A-44A5-AEDF-F75AB5361F01@rock-chips.com>
References: <1452410557-29208-1-git-send-email-zhengxing@rock-chips.com>
 <E6BB80D3-5A62-4BE1-BFC2-74DAC9213C95@rock-chips.com>
 <B588B7A8-EC8A-44A5-AEDF-F75AB5361F01@rock-chips.com>
Message-ID: <1714176.Nme8cQ9zc4@phil>

Am Sonntag, 10. Januar 2016, 18:36:12 schrieb Xing Zheng:
> Hi Heiko,
> 
> > ? 2016?1?10??18:29?Xing Zheng <zhengxing at rock-chips.com> ???
> > 
> > 
> > Hi Heiko,
> > 
> >> ? 2016?1?10??17:59?Heiko Stuebner <heiko at sntech.de> ???
> >> 
> >> Hi Xing,
> >> 
> >> Am Sonntag, 10. Januar 2016, 15:22:37 schrieb Xing Zheng:
> >>> Because the RK3036 doesn't support EMAC, and only support the RMII
> >>> mode
> >>> in the EMAC IP, we refer to the TRM CRU and update the new external
> >>> clock name for EMAC.
> >>> 
> >>> Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
> >> 
> >> as I said in the other mail, I already fixed the original patch myself
> >> :-) So I'm just going to drop this one.
> >> 
> >> 
> >> Heiko
> >> 
> >> https://git.kernel.org/cgit/linux/kernel/git/mmind/linux-rockchip.git/c
> >> ommit/?h=v4.5-clk/fixes&id=88807906af108a8384d77ad9cfb9421f164ea931> 
> > I am sorry to misunderstand.
> > 
> > OK, thank you for your help.  :)
> 
> Your patch is:
> - - "ext_gmac" - external GMAC clock - optional
> + - "rmii_clkin" - external GMAC clock - optional
> 
> I think that these should be like this:
> - - "ext_gmac" - external GMAC clock - optional
> + - "rmii_clkin" - external EMAC clock - optional

I fixed that up now, thanks for catching this.

https://git.kernel.org/cgit/linux/kernel/git/mmind/linux-rockchip.git/commit/?h=v4.5-clk/fixes&id=1c888eb1054a08620a8d2bc5dd2d7cc290897371


Heiko


From davem at davemloft.net  Sun Jan 10 15:03:17 2016
From: davem at davemloft.net (David Miller)
Date: Sun, 10 Jan 2016 18:03:17 -0500 (EST)
Subject: [PATCH v2] stmmac: Don't exit mdio registration when mdio
 subnode is not found in the DTS
In-Reply-To: <1452197608-29060-1-git-send-email-romain.perier@gmail.com>
References: <1452197608-29060-1-git-send-email-romain.perier@gmail.com>
Message-ID: <20160110.180317.1091002009651518965.davem@davemloft.net>

From: Romain Perier <romain.perier at gmail.com>
Date: Thu,  7 Jan 2016 21:13:28 +0100

> Originally, most of the platforms using this driver did not define an mdio subnode
> in the devicetree. Commit e34d65 ("stmmac: create of compatible mdio bus for stmmac driver")
> introduced a backward compatibily issue by using of_mdiobus_register explicitly
> with an mdio subnode. This patch fixes the issue by calling the function
> mdiobus_register, when mdio subnode is not found. The driver is now compatible
> with both modes.
> 
> Fixes: e34d65696d2e ("stmmac: create of compatible mdio bus for stmmac driver")
> Signed-off-by: Romain Perier <romain.perier at gmail.com>

Applied, thanks.


From jay.xu at rock-chips.com  Sun Jan 10 16:32:32 2016
From: jay.xu at rock-chips.com (Jianqun Xu)
Date: Mon, 11 Jan 2016 08:32:32 +0800
Subject: [PATCH] clk: rockchip: fix some clock gates for rk3368
In-Reply-To: <1873444.tpQzgfaHRN@phil>
References: <1452158287-18742-1-git-send-email-wxt@rock-chips.com>
 <1873444.tpQzgfaHRN@phil>
Message-ID: <5692F820.6030500@rock-chips.com>

Thanks

? 10/01/2016 06:26, Heiko Stuebner ??:
> Am Donnerstag, 7. Januar 2016, 17:18:07 schrieb Caesar Wang:
>> From: Jianqun xu <jay.xu at rock-chips.com>
>>
>> Reference to the Rockchip RK3368 TRM v1.1, some clock
>> gates need to be updated.
>>
>> Signed-off-by: Jianqun xu <jay.xu at rock-chips.com>
>> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
> applied to my clk-fixes branch for 4.5
>
>
> Thanks
> Heiko
>
>
>




From robh at kernel.org  Sun Jan 10 18:06:18 2016
From: robh at kernel.org (Rob Herring)
Date: Sun, 10 Jan 2016 20:06:18 -0600
Subject: [RFC PATCH v2 4/4] dt-bindings: add document for rk3229-hdmi
In-Reply-To: <1452157511-18891-1-git-send-email-ykk@rock-chips.com>
References: <1452156811-18150-1-git-send-email-ykk@rock-chips.com>
 <1452157511-18891-1-git-send-email-ykk@rock-chips.com>
Message-ID: <20160111020618.GA20680@rob-hp-laptop>

On Thu, Jan 07, 2016 at 05:05:11PM +0800, Yakir Yang wrote:
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
> ---
> Changes in v2: None
> 
>  .../devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt         | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
> index 668091f..1cdc627 100644
> --- a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
> +++ b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
> @@ -2,7 +2,7 @@ Rockchip specific extensions to the Synopsys Designware HDMI
>  ================================
>  
>  Required properties:
> -- compatible: "rockchip,rk3288-dw-hdmi";
> +- compatible: "rockchip,rk3288-dw-hdmi", "rockchip,rk3229-dw-hdmi";
>  - reg: Physical base address and length of the controller's registers.
>  - clocks: phandle to hdmi iahb and isfr clocks.
>  - clock-names: should be "iahb" "isfr"
> @@ -15,8 +15,10 @@ Required properties:
>    rk3288 platform
>  
>  Optional properties
> +- reg: Physical base address and length of the external HDMI PHY's registers.

This is already required...

>  - ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
>  - clocks, clock-names: phandle to the HDMI CEC clock, name should be "cec"
> +		       phandle to the external HDMI PHY clock, name should be "extphy"

Does this change apply to rk3288 or just rk3229? If the latter, you 
should make that clear. The driver needs to allow for old dtb's that 
only have 1 clock.

Based on Philipp's feedback, you also need to add a phys property.

Rob


From zhangqing at rock-chips.com  Mon Jan 11 02:36:37 2016
From: zhangqing at rock-chips.com (zhangqing)
Date: Mon, 11 Jan 2016 02:36:37 -0800
Subject: [PATCH v3 0/3] SoC: rockchip: support power domain for RK3368
Message-ID: <1452508600-3512-1-git-send-email-zhangqing@rock-chips.com>

add pd id in rk3368-power.h
modify pm_domains.c to support RK3368 SoC
add RK3368 document for Rockchip power domains

zhangqing (3):
  dt-bindings: add power-domain header for RK3368 SoCs
  soc: rockchip: power-domain: Modify power domain driver for rk3368
  dt-bindings: modify document of Rockchip power domains

 .../bindings/soc/rockchip/power_domain.txt         | 21 ++++++++++++++
 drivers/soc/rockchip/pm_domains.c                  | 33 ++++++++++++++++++++++
 include/dt-bindings/power/rk3368-power.h           | 28 ++++++++++++++++++
 3 files changed, 82 insertions(+)
 create mode 100644 include/dt-bindings/power/rk3368-power.h

-- 
1.9.1




From zhangqing at rock-chips.com  Mon Jan 11 02:36:38 2016
From: zhangqing at rock-chips.com (zhangqing)
Date: Mon, 11 Jan 2016 02:36:38 -0800
Subject: [PATCH v3 1/3] dt-bindings: add power-domain header for RK3368 SoCs
In-Reply-To: <1452508600-3512-1-git-send-email-zhangqing@rock-chips.com>
References: <1452508600-3512-1-git-send-email-zhangqing@rock-chips.com>
Message-ID: <1452508600-3512-2-git-send-email-zhangqing@rock-chips.com>

According to a description from TRM, add all the power domains.

Signed-off-by: zhangqing <zhangqing at rock-chips.com>
---
 include/dt-bindings/power/rk3368-power.h | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)
 create mode 100644 include/dt-bindings/power/rk3368-power.h

diff --git a/include/dt-bindings/power/rk3368-power.h b/include/dt-bindings/power/rk3368-power.h
new file mode 100644
index 0000000..93633d5
--- /dev/null
+++ b/include/dt-bindings/power/rk3368-power.h
@@ -0,0 +1,28 @@
+#ifndef __DT_BINDINGS_POWER_RK3368_POWER_H__
+#define __DT_BINDINGS_POWER_RK3368_POWER_H__
+
+/* VD_CORE */
+#define RK3368_PD_A53_L0	0
+#define RK3368_PD_A53_L1	1
+#define RK3368_PD_A53_L2	2
+#define RK3368_PD_A53_L3	3
+#define RK3368_PD_SCU_L		4
+#define RK3368_PD_A53_B0	5
+#define RK3368_PD_A53_B1	6
+#define RK3368_PD_A53_B2	7
+#define RK3368_PD_A53_B3	8
+#define RK3368_PD_SCU_B		9
+
+/* VD_LOGIC */
+#define RK3368_PD_BUS		10
+#define RK3368_PD_PERI		11
+#define RK3368_PD_VIO		12
+#define RK3368_PD_ALIVE		13
+#define RK3368_PD_VIDEO		14
+#define RK3368_PD_GPU_0		15
+#define RK3368_PD_GPU_1		16
+
+/* VD_PMU */
+#define RK3368_PD_PMU		17
+
+#endif
-- 
1.9.1




From zhangqing at rock-chips.com  Mon Jan 11 02:36:39 2016
From: zhangqing at rock-chips.com (zhangqing)
Date: Mon, 11 Jan 2016 02:36:39 -0800
Subject: [PATCH v3 2/3] soc: rockchip: power-domain: Modify power domain
 driver for rk3368
In-Reply-To: <1452508600-3512-1-git-send-email-zhangqing@rock-chips.com>
References: <1452508600-3512-1-git-send-email-zhangqing@rock-chips.com>
Message-ID: <1452508600-3512-3-git-send-email-zhangqing@rock-chips.com>

This driver is modified to support RK3368 SoC.

Signed-off-by: zhangqing <zhangqing at rock-chips.com>
---
 drivers/soc/rockchip/pm_domains.c | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/drivers/soc/rockchip/pm_domains.c b/drivers/soc/rockchip/pm_domains.c
index 534c589..6cdffb1 100644
--- a/drivers/soc/rockchip/pm_domains.c
+++ b/drivers/soc/rockchip/pm_domains.c
@@ -18,6 +18,7 @@
 #include <linux/regmap.h>
 #include <linux/mfd/syscon.h>
 #include <dt-bindings/power/rk3288-power.h>
+#include <dt-bindings/power/rk3368-power.h>
 
 struct rockchip_domain_info {
 	int pwr_mask;
@@ -75,6 +76,9 @@ struct rockchip_pmu {
 #define DOMAIN_RK3288(pwr, status, req)		\
 	DOMAIN(pwr, status, req, req, (req) + 16)
 
+#define DOMAIN_RK3368(pwr, status, req)		\
+	DOMAIN(pwr, status, req, (req) + 16, req)
+
 static bool rockchip_pmu_domain_is_idle(struct rockchip_pm_domain *pd)
 {
 	struct rockchip_pmu *pmu = pd->pmu;
@@ -444,6 +448,14 @@ static const struct rockchip_domain_info rk3288_pm_domains[] = {
 	[RK3288_PD_GPU]		= DOMAIN_RK3288(9, 9, 2),
 };
 
+static const struct rockchip_domain_info rk3368_pm_domains[] = {
+	[RK3368_PD_PERI]	= DOMAIN_RK3368(13, 12, 6),
+	[RK3368_PD_VIO]		= DOMAIN_RK3368(15, 14, 8),
+	[RK3368_PD_VIDEO]	= DOMAIN_RK3368(14, 13, 7),
+	[RK3368_PD_GPU_0]	= DOMAIN_RK3368(16, 15, 2),
+	[RK3368_PD_GPU_1]	= DOMAIN_RK3368(17, 16, 2),
+};
+
 static const struct rockchip_pmu_info rk3288_pmu = {
 	.pwr_offset = 0x08,
 	.status_offset = 0x0c,
@@ -461,11 +473,32 @@ static const struct rockchip_pmu_info rk3288_pmu = {
 	.domain_info = rk3288_pm_domains,
 };
 
+static const struct rockchip_pmu_info rk3368_pmu = {
+	.pwr_offset = 0x0c,
+	.status_offset = 0x10,
+	.req_offset = 0x3c,
+	.idle_offset = 0x40,
+	.ack_offset = 0x40,
+
+	.core_pwrcnt_offset = 0x48,
+	.gpu_pwrcnt_offset = 0x50,
+
+	.core_power_transition_time = 24,
+	.gpu_power_transition_time = 24,
+
+	.num_domains = ARRAY_SIZE(rk3368_pm_domains),
+	.domain_info = rk3368_pm_domains,
+};
+
 static const struct of_device_id rockchip_pm_domain_dt_match[] = {
 	{
 		.compatible = "rockchip,rk3288-power-controller",
 		.data = (void *)&rk3288_pmu,
 	},
+	{
+		.compatible = "rockchip,rk3368-power-controller",
+		.data = (void *)&rk3368_pmu,
+	},
 	{ /* sentinel */ },
 };
 
-- 
1.9.1




From zhangqing at rock-chips.com  Mon Jan 11 02:36:40 2016
From: zhangqing at rock-chips.com (zhangqing)
Date: Mon, 11 Jan 2016 02:36:40 -0800
Subject: [PATCH v3 3/3] dt-bindings: modify document of Rockchip power domains
In-Reply-To: <1452508600-3512-1-git-send-email-zhangqing@rock-chips.com>
References: <1452508600-3512-1-git-send-email-zhangqing@rock-chips.com>
Message-ID: <1452508600-3512-4-git-send-email-zhangqing@rock-chips.com>

Modify binding documentation for the power domains
found on Rockchip RK3368 SoCs.

Signed-off-by: zhangqing <zhangqing at rock-chips.com>
---
 .../bindings/soc/rockchip/power_domain.txt          | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/Documentation/devicetree/bindings/soc/rockchip/power_domain.txt b/Documentation/devicetree/bindings/soc/rockchip/power_domain.txt
index 112756e..13dc6a3 100644
--- a/Documentation/devicetree/bindings/soc/rockchip/power_domain.txt
+++ b/Documentation/devicetree/bindings/soc/rockchip/power_domain.txt
@@ -6,6 +6,7 @@ powered up/down by software based on different application scenes to save power.
 Required properties for power domain controller:
 - compatible: Should be one of the following.
 	"rockchip,rk3288-power-controller" - for RK3288 SoCs.
+	"rockchip,rk3368-power-controller" - for RK3368 SoCs.
 - #power-domain-cells: Number of cells in a power-domain specifier.
 	Should be 1 for multiple PM domains.
 - #address-cells: Should be 1.
@@ -14,6 +15,7 @@ Required properties for power domain controller:
 Required properties for power domain sub nodes:
 - reg: index of the power domain, should use macros in:
 	"include/dt-bindings/power/rk3288-power.h" - for RK3288 type power domain.
+	"include/dt-bindings/power/rk3368-power.h" - for RK3368 type power domain.
 - clocks (optional): phandles to clocks which need to be enabled while power domain
 	switches state.
 
@@ -31,11 +33,24 @@ Example:
 		};
 	};
 
+	 power: power-controller {
+                compatible = "rockchip,rk3368-power-controller";
+                #power-domain-cells = <1>;
+                #address-cells = <1>;
+                #size-cells = <0>;
+
+                pd_gpu_1 {
+                        reg = <RK3368_PD_GPU_1>;
+                        clocks = <&cru ACLK_GPU_CFG>;
+                };
+        };
+
 Node of a device using power domains must have a power-domains property,
 containing a phandle to the power device node and an index specifying which
 power domain to use.
 The index should use macros in:
 	"include/dt-bindings/power/rk3288-power.h" - for rk3288 type power domain.
+	"include/dt-bindings/power/rk3368-power.h" - for rk3368 type power domain.
 
 Example of the node using power domain:
 
@@ -44,3 +59,9 @@ Example of the node using power domain:
 		power-domains = <&power RK3288_PD_GPU>;
 		/* ... */
 	};
+
+	node {
+                /* ... */
+                power-domains = <&power RK3368_PD_GPU_1>;
+                /* ... */
+        };
-- 
1.9.1




From robh at kernel.org  Sun Jan 10 18:55:10 2016
From: robh at kernel.org (Rob Herring)
Date: Sun, 10 Jan 2016 20:55:10 -0600
Subject: [PATCH v3 3/3] dt-bindings: modify document of Rockchip power
 domains
In-Reply-To: <1452508600-3512-4-git-send-email-zhangqing@rock-chips.com>
References: <1452508600-3512-1-git-send-email-zhangqing@rock-chips.com>
 <1452508600-3512-4-git-send-email-zhangqing@rock-chips.com>
Message-ID: <20160111025510.GA17050@rob-hp-laptop>

On Mon, Jan 11, 2016 at 02:36:40AM -0800, zhangqing wrote:
> Modify binding documentation for the power domains
> found on Rockchip RK3368 SoCs.
> 
> Signed-off-by: zhangqing <zhangqing at rock-chips.com>
> ---
>  .../bindings/soc/rockchip/power_domain.txt          | 21 +++++++++++++++++++++
>  1 file changed, 21 insertions(+)

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


From davem at davemloft.net  Sun Jan 10 19:21:51 2016
From: davem at davemloft.net (David Miller)
Date: Sun, 10 Jan 2016 22:21:51 -0500 (EST)
Subject: [PATCH v2 0/3] Add support emac for the RK3036 SoC platform
In-Reply-To: <1452216902-21063-1-git-send-email-zhengxing@rock-chips.com>
References: <1452216902-21063-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <20160110.222151.599769321034656161.davem@davemloft.net>

From: Xing Zheng <zhengxing at rock-chips.com>
Date: Fri,  8 Jan 2016 09:34:59 +0800

>   We have supported the emac for RK3066/RK3188, but the RK3036 have
> some configuration different with them. We should let the driver of
> emac_rockchip compatible with other Rockchip SoCs.

Series applied to net-next.


From ykk at rock-chips.com  Sun Jan 10 19:48:15 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Mon, 11 Jan 2016 11:48:15 +0800
Subject: [RFC PATCH v2 4/4] dt-bindings: add document for rk3229-hdmi
In-Reply-To: <20160111020618.GA20680@rob-hp-laptop>
References: <1452156811-18150-1-git-send-email-ykk@rock-chips.com>
 <1452157511-18891-1-git-send-email-ykk@rock-chips.com>
 <20160111020618.GA20680@rob-hp-laptop>
Message-ID: <569325FF.10902@rock-chips.com>

Hi Rob,

On 01/11/2016 10:06 AM, Rob Herring wrote:
> On Thu, Jan 07, 2016 at 05:05:11PM +0800, Yakir Yang wrote:
>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
>> ---
>> Changes in v2: None
>>
>>   .../devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt         | 4 +++-
>>   1 file changed, 3 insertions(+), 1 deletion(-)
>>
>> diff --git a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
>> index 668091f..1cdc627 100644
>> --- a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
>> +++ b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
>> @@ -2,7 +2,7 @@ Rockchip specific extensions to the Synopsys Designware HDMI
>>   ================================
>>   
>>   Required properties:
>> -- compatible: "rockchip,rk3288-dw-hdmi";
>> +- compatible: "rockchip,rk3288-dw-hdmi", "rockchip,rk3229-dw-hdmi";
>>   - reg: Physical base address and length of the controller's registers.
>>   - clocks: phandle to hdmi iahb and isfr clocks.
>>   - clock-names: should be "iahb" "isfr"
>> @@ -15,8 +15,10 @@ Required properties:
>>     rk3288 platform
>>   
>>   Optional properties
>> +- reg: Physical base address and length of the external HDMI PHY's registers.
> This is already required...
>

The original reg only include the DW HDMI controller registers:
         reg = <0x200a0000 0x20000>;

But for now, I need to pass the external HDMI PHY registers:
         reg = <0x200a0000 0x20000>, <0x12030000 0x10000>;

>>   - ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
>>   - clocks, clock-names: phandle to the HDMI CEC clock, name should be "cec"
>> +		       phandle to the external HDMI PHY clock, name should be "extphy"
> Does this change apply to rk3288 or just rk3229? If the latter, you
> should make that clear. The driver needs to allow for old dtb's that
> only have 1 clock.

It's the latter one, driver have allowed that by judging the device type,
but I should make the document more clear.

> Based on Philipp's feedback, you also need to add a phys property.

Yes, I'm preparing the new patches that create a new separate phy
driver for external HDMI phy, there would be much difference from
this version.

Thanks for your respond,
- Yakir

>
> Rob
>
>
>




From wzz at rock-chips.com  Sun Jan 10 19:57:27 2016
From: wzz at rock-chips.com (Zain Wang)
Date: Mon, 11 Jan 2016 11:57:27 +0800
Subject: [RFC PATCH v5] Crypto: rockchip/crypto - add hash support for crypto
 engine in rk3288
Message-ID: <1452484647-30260-1-git-send-email-wzz@rock-chips.com>

From: Zain Wang <zain.wang at rock-chips.com>

Add md5 sha1 sha256 support for crypto engine in rk3288.
This patch can't support multiple updatings because of limited of IC,
as result, it can't support import and export too.

Signed-off-by: Zain Wang <zain.wang at rock-chips.com>
---
Changes in V5:
- fix some mistakes with applying

Changes in V4:
- remove CRYPTO_ALG_NEED_FALLBACK.

Changes in V3:
- add switch instead of multiple if.

Changes in V2:
- add some comments to code.
- fix some issues about zero message process.

 drivers/crypto/rockchip/Makefile                   |   1 +
 drivers/crypto/rockchip/rk3288_crypto.c            |  28 +-
 drivers/crypto/rockchip/rk3288_crypto.h            |  51 ++-
 drivers/crypto/rockchip/rk3288_crypto_ablkcipher.c |  20 +-
 drivers/crypto/rockchip/rk3288_crypto_ahash.c      | 386 +++++++++++++++++++++
 5 files changed, 472 insertions(+), 14 deletions(-)
 create mode 100644 drivers/crypto/rockchip/rk3288_crypto_ahash.c

diff --git a/drivers/crypto/rockchip/Makefile b/drivers/crypto/rockchip/Makefile
index 7051c6c..30f9129 100644
--- a/drivers/crypto/rockchip/Makefile
+++ b/drivers/crypto/rockchip/Makefile
@@ -1,3 +1,4 @@
 obj-$(CONFIG_CRYPTO_DEV_ROCKCHIP) += rk_crypto.o
 rk_crypto-objs := rk3288_crypto.o \
 		  rk3288_crypto_ablkcipher.o \
+		  rk3288_crypto_ahash.o
diff --git a/drivers/crypto/rockchip/rk3288_crypto.c b/drivers/crypto/rockchip/rk3288_crypto.c
index da9c73d..af50825 100644
--- a/drivers/crypto/rockchip/rk3288_crypto.c
+++ b/drivers/crypto/rockchip/rk3288_crypto.c
@@ -208,6 +208,8 @@ static void rk_crypto_tasklet_cb(unsigned long data)
 
 	if (crypto_tfm_alg_type(async_req->tfm) == CRYPTO_ALG_TYPE_ABLKCIPHER)
 		dev->ablk_req = ablkcipher_request_cast(async_req);
+	else
+		dev->ahash_req = ahash_request_cast(async_req);
 	err = dev->start(dev);
 	if (err)
 		dev->complete(dev, err);
@@ -220,6 +222,9 @@ static struct rk_crypto_tmp *rk_cipher_algs[] = {
 	&rk_cbc_des_alg,
 	&rk_ecb_des3_ede_alg,
 	&rk_cbc_des3_ede_alg,
+	&rk_ahash_sha1,
+	&rk_ahash_sha256,
+	&rk_ahash_md5,
 };
 
 static int rk_crypto_register(struct rk_crypto_info *crypto_info)
@@ -229,15 +234,24 @@ static int rk_crypto_register(struct rk_crypto_info *crypto_info)
 
 	for (i = 0; i < ARRAY_SIZE(rk_cipher_algs); i++) {
 		rk_cipher_algs[i]->dev = crypto_info;
-		err = crypto_register_alg(&rk_cipher_algs[i]->alg);
+		if (rk_cipher_algs[i]->type == ALG_TYPE_CIPHER)
+			err = crypto_register_alg(
+					&rk_cipher_algs[i]->alg.crypto);
+		else
+			err = crypto_register_ahash(
+					&rk_cipher_algs[i]->alg.hash);
 		if (err)
 			goto err_cipher_algs;
 	}
 	return 0;
 
 err_cipher_algs:
-	for (k = 0; k < i; k++)
-		crypto_unregister_alg(&rk_cipher_algs[k]->alg);
+	for (k = 0; k < i; k++) {
+		if (rk_cipher_algs[i]->type == ALG_TYPE_CIPHER)
+			crypto_unregister_alg(&rk_cipher_algs[k]->alg.crypto);
+		else
+			crypto_unregister_ahash(&rk_cipher_algs[i]->alg.hash);
+	}
 	return err;
 }
 
@@ -245,8 +259,12 @@ static void rk_crypto_unregister(void)
 {
 	unsigned int i;
 
-	for (i = 0; i < ARRAY_SIZE(rk_cipher_algs); i++)
-		crypto_unregister_alg(&rk_cipher_algs[i]->alg);
+	for (i = 0; i < ARRAY_SIZE(rk_cipher_algs); i++) {
+		if (rk_cipher_algs[i]->type == ALG_TYPE_CIPHER)
+			crypto_unregister_alg(&rk_cipher_algs[i]->alg.crypto);
+		else
+			crypto_unregister_ahash(&rk_cipher_algs[i]->alg.hash);
+	}
 }
 
 static void rk_crypto_action(void *data)
diff --git a/drivers/crypto/rockchip/rk3288_crypto.h b/drivers/crypto/rockchip/rk3288_crypto.h
index e499c2c..4eda50a 100644
--- a/drivers/crypto/rockchip/rk3288_crypto.h
+++ b/drivers/crypto/rockchip/rk3288_crypto.h
@@ -6,6 +6,10 @@
 #include <crypto/algapi.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
+#include <crypto/internal/hash.h>
+
+#include <crypto/md5.h>
+#include <crypto/sha.h>
 
 #define _SBF(v, f)			((v) << (f))
 
@@ -149,6 +153,28 @@
 #define RK_CRYPTO_TDES_KEY3_0		0x0130
 #define RK_CRYPTO_TDES_KEY3_1		0x0134
 
+/* HASH */
+#define RK_CRYPTO_HASH_CTRL		0x0180
+#define RK_CRYPTO_HASH_SWAP_DO		BIT(3)
+#define RK_CRYPTO_HASH_SWAP_DI		BIT(2)
+#define RK_CRYPTO_HASH_SHA1		_SBF(0x00, 0)
+#define RK_CRYPTO_HASH_MD5		_SBF(0x01, 0)
+#define RK_CRYPTO_HASH_SHA256		_SBF(0x02, 0)
+#define RK_CRYPTO_HASH_PRNG		_SBF(0x03, 0)
+
+#define RK_CRYPTO_HASH_STS		0x0184
+#define RK_CRYPTO_HASH_DONE		BIT(0)
+
+#define RK_CRYPTO_HASH_MSG_LEN		0x0188
+#define RK_CRYPTO_HASH_DOUT_0		0x018c
+#define RK_CRYPTO_HASH_DOUT_1		0x0190
+#define RK_CRYPTO_HASH_DOUT_2		0x0194
+#define RK_CRYPTO_HASH_DOUT_3		0x0198
+#define RK_CRYPTO_HASH_DOUT_4		0x019c
+#define RK_CRYPTO_HASH_DOUT_5		0x01a0
+#define RK_CRYPTO_HASH_DOUT_6		0x01a4
+#define RK_CRYPTO_HASH_DOUT_7		0x01a8
+
 #define CRYPTO_READ(dev, offset)		  \
 		readl_relaxed(((dev)->reg + (offset)))
 #define CRYPTO_WRITE(dev, offset, val)	  \
@@ -166,6 +192,7 @@ struct rk_crypto_info {
 	struct crypto_queue		queue;
 	struct tasklet_struct		crypto_tasklet;
 	struct ablkcipher_request	*ablk_req;
+	struct ahash_request		*ahash_req;
 	/* device lock */
 	spinlock_t			lock;
 
@@ -195,15 +222,31 @@ struct rk_crypto_info {
 	void (*unload_data)(struct rk_crypto_info *dev);
 };
 
+/* the private variable of hash */
+struct rk_ahash_ctx {
+	struct rk_crypto_info		*dev;
+	int				flag_finup;
+	int				first_op;
+};
+
 /* the private variable of cipher */
 struct rk_cipher_ctx {
 	struct rk_crypto_info		*dev;
 	unsigned int			keylen;
 };
 
+enum alg_type {
+	ALG_TYPE_HASH,
+	ALG_TYPE_CIPHER,
+};
+
 struct rk_crypto_tmp {
-	struct rk_crypto_info *dev;
-	struct crypto_alg alg;
+	struct rk_crypto_info		*dev;
+	union {
+		struct crypto_alg	crypto;
+		struct ahash_alg	hash;
+	} alg;
+	enum alg_type			type;
 };
 
 extern struct rk_crypto_tmp rk_ecb_aes_alg;
@@ -213,4 +256,8 @@ extern struct rk_crypto_tmp rk_cbc_des_alg;
 extern struct rk_crypto_tmp rk_ecb_des3_ede_alg;
 extern struct rk_crypto_tmp rk_cbc_des3_ede_alg;
 
+extern struct rk_crypto_tmp rk_ahash_sha1;
+extern struct rk_crypto_tmp rk_ahash_sha256;
+extern struct rk_crypto_tmp rk_ahash_md5;
+
 #endif
diff --git a/drivers/crypto/rockchip/rk3288_crypto_ablkcipher.c b/drivers/crypto/rockchip/rk3288_crypto_ablkcipher.c
index d98b681..b5a3afe 100644
--- a/drivers/crypto/rockchip/rk3288_crypto_ablkcipher.c
+++ b/drivers/crypto/rockchip/rk3288_crypto_ablkcipher.c
@@ -336,7 +336,7 @@ static int rk_ablk_cra_init(struct crypto_tfm *tfm)
 	struct crypto_alg *alg = tfm->__crt_alg;
 	struct rk_crypto_tmp *algt;
 
-	algt = container_of(alg, struct rk_crypto_tmp, alg);
+	algt = container_of(alg, struct rk_crypto_tmp, alg.crypto);
 
 	ctx->dev = algt->dev;
 	ctx->dev->align_size = crypto_tfm_alg_alignmask(tfm) + 1;
@@ -357,7 +357,8 @@ static void rk_ablk_cra_exit(struct crypto_tfm *tfm)
 }
 
 struct rk_crypto_tmp rk_ecb_aes_alg = {
-	.alg = {
+	.type = ALG_TYPE_CIPHER,
+	.alg.crypto = {
 		.cra_name		= "ecb(aes)",
 		.cra_driver_name	= "ecb-aes-rk",
 		.cra_priority		= 300,
@@ -381,7 +382,8 @@ struct rk_crypto_tmp rk_ecb_aes_alg = {
 };
 
 struct rk_crypto_tmp rk_cbc_aes_alg = {
-	.alg = {
+	.type = ALG_TYPE_CIPHER,
+	.alg.crypto = {
 		.cra_name		= "cbc(aes)",
 		.cra_driver_name	= "cbc-aes-rk",
 		.cra_priority		= 300,
@@ -406,7 +408,8 @@ struct rk_crypto_tmp rk_cbc_aes_alg = {
 };
 
 struct rk_crypto_tmp rk_ecb_des_alg = {
-	.alg = {
+	.type = ALG_TYPE_CIPHER,
+	.alg.crypto = {
 		.cra_name		= "ecb(des)",
 		.cra_driver_name	= "ecb-des-rk",
 		.cra_priority		= 300,
@@ -430,7 +433,8 @@ struct rk_crypto_tmp rk_ecb_des_alg = {
 };
 
 struct rk_crypto_tmp rk_cbc_des_alg = {
-	.alg = {
+	.type = ALG_TYPE_CIPHER,
+	.alg.crypto = {
 		.cra_name		= "cbc(des)",
 		.cra_driver_name	= "cbc-des-rk",
 		.cra_priority		= 300,
@@ -455,7 +459,8 @@ struct rk_crypto_tmp rk_cbc_des_alg = {
 };
 
 struct rk_crypto_tmp rk_ecb_des3_ede_alg = {
-	.alg = {
+	.type = ALG_TYPE_CIPHER,
+	.alg.crypto = {
 		.cra_name		= "ecb(des3_ede)",
 		.cra_driver_name	= "ecb-des3-ede-rk",
 		.cra_priority		= 300,
@@ -480,7 +485,8 @@ struct rk_crypto_tmp rk_ecb_des3_ede_alg = {
 };
 
 struct rk_crypto_tmp rk_cbc_des3_ede_alg = {
-	.alg = {
+	.type = ALG_TYPE_CIPHER,
+	.alg.crypto = {
 		.cra_name		= "cbc(des3_ede)",
 		.cra_driver_name	= "cbc-des3-ede-rk",
 		.cra_priority		= 300,
diff --git a/drivers/crypto/rockchip/rk3288_crypto_ahash.c b/drivers/crypto/rockchip/rk3288_crypto_ahash.c
new file mode 100644
index 0000000..d77b3f1
--- /dev/null
+++ b/drivers/crypto/rockchip/rk3288_crypto_ahash.c
@@ -0,0 +1,386 @@
+/*
+ * Crypto acceleration support for Rockchip RK3288
+ *
+ * Copyright (c) 2015, Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * Author: Zain Wang <zain.wang at rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * Some ideas are from marvell/cesa.c and s5p-sss.c driver.
+ */
+#include "rk3288_crypto.h"
+
+/*
+ * IC can not process zero message hash,
+ * so we put the fixed hash out when met zero message.
+ */
+static const u8 *sha1_zero_message_hash = {
+	"\xda\x39\xa3\xee\x5e\x6b\x4b\x0d\x32\x55"
+	"\xbf\xef\x95\x60\x18\x90\xaf\xd8\x07\x09"
+};
+
+static const u8 *sha256_zero_message_hash = {
+	"\xe3\xb0\xc4\x42\x98\xfc\x1c\x14"
+	"\x9a\xfb\xf4\xc8\x99\x6f\xb9\x24"
+	"\x27\xae\x41\xe4\x64\x9b\x93\x4c"
+	"\xa4\x95\x99\x1b\x78\x52\xb8\x55"
+};
+
+static const u8 *md5_zero_message_hash = {
+	"\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04"
+	"\xe9\x80\x09\x98\xec\xf8\x42\x7e"
+};
+
+static int zero_message_process(struct ahash_request *req)
+{
+	struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+	int rk_digest_size = crypto_ahash_digestsize(tfm);
+
+	switch (rk_digest_size) {
+	case SHA1_DIGEST_SIZE:
+		memcpy(req->result, sha1_zero_message_hash, rk_digest_size);
+		break;
+	case SHA256_DIGEST_SIZE:
+		memcpy(req->result, sha256_zero_message_hash, rk_digest_size);
+		break;
+	case MD5_DIGEST_SIZE:
+		memcpy(req->result, md5_zero_message_hash, rk_digest_size);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void rk_ahash_crypto_complete(struct rk_crypto_info *dev, int err)
+{
+	if (dev->ahash_req->base.complete)
+		dev->ahash_req->base.complete(&dev->ahash_req->base, err);
+}
+
+static void rk_ahash_hw_init(struct rk_crypto_info *dev)
+{
+	int reg_status = 0;
+
+	reg_status = CRYPTO_READ(dev, RK_CRYPTO_CTRL) |
+		     RK_CRYPTO_HASH_FLUSH | _SBF(0xffff, 16);
+	CRYPTO_WRITE(dev, RK_CRYPTO_CTRL, reg_status);
+
+	reg_status = CRYPTO_READ(dev, RK_CRYPTO_CTRL);
+	reg_status &= (~RK_CRYPTO_HASH_FLUSH);
+	reg_status |= _SBF(0xffff, 16);
+	CRYPTO_WRITE(dev, RK_CRYPTO_CTRL, reg_status);
+
+	memset_io(dev->reg + RK_CRYPTO_HASH_DOUT_0, 0, 32);
+}
+
+static void rk_ahash_reg_init(struct rk_crypto_info *dev)
+{
+	rk_ahash_hw_init(dev);
+
+	CRYPTO_WRITE(dev, RK_CRYPTO_INTENA, RK_CRYPTO_HRDMA_ERR_ENA |
+					    RK_CRYPTO_HRDMA_DONE_ENA);
+
+	CRYPTO_WRITE(dev, RK_CRYPTO_INTSTS, RK_CRYPTO_HRDMA_ERR_INT |
+					    RK_CRYPTO_HRDMA_DONE_INT);
+
+	CRYPTO_WRITE(dev, RK_CRYPTO_HASH_CTRL, dev->mode |
+					       RK_CRYPTO_HASH_SWAP_DO);
+
+	CRYPTO_WRITE(dev, RK_CRYPTO_CONF, RK_CRYPTO_BYTESWAP_HRFIFO |
+					  RK_CRYPTO_BYTESWAP_BRFIFO |
+					  RK_CRYPTO_BYTESWAP_BTFIFO);
+}
+
+static int rk_ahash_init(struct ahash_request *req)
+{
+	struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+	struct rk_ahash_ctx *tctx = crypto_tfm_ctx(req->base.tfm);
+	struct rk_crypto_info *dev = NULL;
+
+	dev = tctx->dev;
+	dev->left_bytes = 0;
+	dev->aligned = 0;
+	dev->ahash_req = req;
+	dev->mode = 0;
+	dev->align_size = 4;
+	dev->sg_dst = NULL;
+
+	tctx->first_op = 1;
+
+	switch (crypto_ahash_digestsize(tfm)) {
+	case SHA1_DIGEST_SIZE:
+		dev->mode = RK_CRYPTO_HASH_SHA1;
+		break;
+	case SHA256_DIGEST_SIZE:
+		dev->mode = RK_CRYPTO_HASH_SHA256;
+		break;
+	case MD5_DIGEST_SIZE:
+		dev->mode = RK_CRYPTO_HASH_MD5;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	rk_ahash_reg_init(dev);
+	return 0;
+}
+
+static int rk_ahash_update(struct ahash_request *req)
+{
+	struct rk_ahash_ctx *tctx = crypto_tfm_ctx(req->base.tfm);
+	struct rk_crypto_info *dev = tctx->dev;
+	unsigned long flags;
+	int ret;
+
+	dev->total = req->nbytes;
+	dev->left_bytes = req->nbytes;
+	dev->sg_src = req->src;
+	dev->first = req->src;
+	dev->nents = sg_nents(req->src);
+
+	/* IC can calculate 0 message hash, so it should finish update here */
+	if (!dev->total)
+		return 0;
+
+	if (tctx->first_op) {
+		tctx->first_op = 0;
+		CRYPTO_WRITE(dev, RK_CRYPTO_HASH_MSG_LEN, dev->total);
+	} else {
+		/*
+		 * IC must know the length of total data at first,
+		 * multiple updatings cannot support this variable.
+		 */
+		dev_warn(dev->dev, "Cannot carry multiple updatings!\n");
+		return 0;
+	}
+	spin_lock_irqsave(&dev->lock, flags);
+	ret = crypto_enqueue_request(&dev->queue, &req->base);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	tasklet_schedule(&dev->crypto_tasklet);
+
+	return ret;
+}
+
+static int rk_ahash_final(struct ahash_request *req)
+{
+	struct rk_ahash_ctx *tctx = crypto_tfm_ctx(req->base.tfm);
+	struct rk_crypto_info *dev = tctx->dev;
+	struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+
+	/* If met 0 message, put the fixed hash out.*/
+	if (!dev->total)
+		return zero_message_process(req);
+
+	/*
+	 * IC should process the result again after last dma interrupt.
+	 * And the last processing is very quick so than it may entry
+	 * interrupt before finishing last interrupt.
+	 * So I don't use interrupt finished hash.
+	 */
+	while (!CRYPTO_READ(dev, RK_CRYPTO_HASH_STS))
+		usleep_range(50, 100);
+
+	memcpy_fromio(req->result, dev->reg + RK_CRYPTO_HASH_DOUT_0,
+		      crypto_ahash_digestsize(tfm));
+	return 0;
+}
+
+static int rk_ahash_finup(struct ahash_request *req)
+{
+	struct rk_ahash_ctx *tctx = crypto_tfm_ctx(req->base.tfm);
+	int err;
+
+	/*
+	 * finup should process one updating and final.
+	 * and we have to wait for updating in finup so that we can
+	 * fetching result by calling rk_ahash_final in finup.
+	 */
+	tctx->flag_finup = 1;
+	err = rk_ahash_update(req);
+	if (err == -EINPROGRESS || err == -EBUSY)
+		while (tctx->flag_finup)
+			/*
+			 * waiting time is relative with the last date len,
+			 * so here cannot get a fixed time.
+			 * 50-100 makes system not call here frequently wasting
+			 * efficiency, and make it response quickly when dma
+			 * complete.
+			 */
+			usleep_range(50, 100);
+
+	return rk_ahash_final(req);
+}
+
+static int rk_ahash_digest(struct ahash_request *req)
+{
+	return rk_ahash_init(req) ? -EINVAL : rk_ahash_finup(req);
+}
+
+static void crypto_ahash_dma_start(struct rk_crypto_info *dev)
+{
+	CRYPTO_WRITE(dev, RK_CRYPTO_HRDMAS, dev->addr_in);
+	CRYPTO_WRITE(dev, RK_CRYPTO_HRDMAL, (dev->count + 3) / 4);
+	CRYPTO_WRITE(dev, RK_CRYPTO_CTRL, RK_CRYPTO_HASH_START |
+					  (RK_CRYPTO_HASH_START << 16));
+}
+
+static int rk_ahash_set_data_start(struct rk_crypto_info *dev)
+{
+	int err;
+
+	err = dev->load_data(dev, dev->sg_src, NULL);
+	if (!err)
+		crypto_ahash_dma_start(dev);
+	return err;
+}
+
+static int rk_ahash_start(struct rk_crypto_info *dev)
+{
+	return rk_ahash_set_data_start(dev);
+}
+
+/*
+ * return:
+ * true: some err was occurred
+ * fault: no err, please continue
+ */
+static int rk_ahash_crypto_rx(struct rk_crypto_info *dev)
+{
+	int err = 0;
+	struct rk_ahash_ctx *tctx = crypto_tfm_ctx(dev->ahash_req->base.tfm);
+
+	dev->unload_data(dev);
+	if (dev->left_bytes) {
+		if (dev->aligned) {
+			if (sg_is_last(dev->sg_src)) {
+				dev_warn(dev->dev, "[%s:%d], Lack of data\n",
+					 __func__, __LINE__);
+				err = -ENOMEM;
+				goto out_rx;
+			}
+			dev->sg_src = sg_next(dev->sg_src);
+		}
+		err = rk_ahash_set_data_start(dev);
+	} else {
+		tctx->flag_finup = 0;
+		dev->complete(dev, 0);
+	}
+
+out_rx:
+	return err;
+}
+
+static int rk_cra_hash_init(struct crypto_tfm *tfm)
+{
+	struct rk_ahash_ctx *tctx = crypto_tfm_ctx(tfm);
+	struct rk_crypto_tmp *algt;
+	struct ahash_alg *alg = __crypto_ahash_alg(tfm->__crt_alg);
+
+	algt = container_of(alg, struct rk_crypto_tmp, alg.hash);
+
+	tctx->dev = algt->dev;
+	tctx->dev->addr_vir = (void *)__get_free_page(GFP_KERNEL);
+	if (!tctx->dev->addr_vir) {
+		dev_err(tctx->dev->dev, "failed to kmalloc for addr_vir\n");
+		return -ENOMEM;
+	}
+	tctx->dev->start = rk_ahash_start;
+	tctx->dev->update = rk_ahash_crypto_rx;
+	tctx->dev->complete = rk_ahash_crypto_complete;
+	return tctx->dev->enable_clk(tctx->dev);
+}
+
+static void rk_cra_hash_exit(struct crypto_tfm *tfm)
+{
+	struct rk_ahash_ctx *tctx = crypto_tfm_ctx(tfm);
+
+	free_page((unsigned long)tctx->dev->addr_vir);
+	return tctx->dev->disable_clk(tctx->dev);
+}
+
+struct rk_crypto_tmp rk_ahash_sha1 = {
+	.type = ALG_TYPE_HASH,
+	.alg.hash = {
+		.init = rk_ahash_init,
+		.update = rk_ahash_update,
+		.final = rk_ahash_final,
+		.finup = rk_ahash_finup,
+		.digest = rk_ahash_digest,
+		.halg = {
+			 .digestsize = SHA1_DIGEST_SIZE,
+			 .statesize = sizeof(struct sha1_state),
+			 .base = {
+				  .cra_name = "sha1",
+				  .cra_driver_name = "rk-sha1",
+				  .cra_priority = 300,
+				  .cra_flags = CRYPTO_ALG_ASYNC,
+				  .cra_blocksize = SHA1_BLOCK_SIZE,
+				  .cra_ctxsize = sizeof(struct rk_ahash_ctx),
+				  .cra_alignmask = 3,
+				  .cra_init = rk_cra_hash_init,
+				  .cra_exit = rk_cra_hash_exit,
+				  .cra_module = THIS_MODULE,
+				  }
+			 }
+	}
+};
+
+struct rk_crypto_tmp rk_ahash_sha256 = {
+	.type = ALG_TYPE_HASH,
+	.alg.hash = {
+	.init = rk_ahash_init,
+	.update = rk_ahash_update,
+	.final = rk_ahash_final,
+	.finup = rk_ahash_finup,
+	.digest = rk_ahash_digest,
+		.halg = {
+			 .digestsize = SHA256_DIGEST_SIZE,
+			 .statesize = sizeof(struct sha256_state),
+			 .base = {
+				  .cra_name = "sha256",
+				  .cra_driver_name = "rk-sha256",
+				  .cra_priority = 300,
+				  .cra_flags = CRYPTO_ALG_ASYNC,
+				  .cra_blocksize = SHA256_BLOCK_SIZE,
+				  .cra_ctxsize = sizeof(struct rk_ahash_ctx),
+				  .cra_alignmask = 0,
+				  .cra_init = rk_cra_hash_init,
+				  .cra_exit = rk_cra_hash_exit,
+				  .cra_module = THIS_MODULE,
+				  }
+			 }
+	}
+};
+
+struct rk_crypto_tmp rk_ahash_md5 = {
+	.type = ALG_TYPE_HASH,
+	.alg.hash = {
+		.init = rk_ahash_init,
+		.update = rk_ahash_update,
+		.final = rk_ahash_final,
+		.finup = rk_ahash_finup,
+		.digest = rk_ahash_digest,
+		.halg = {
+			 .digestsize = MD5_DIGEST_SIZE,
+			 .statesize = sizeof(struct md5_state),
+			 .base = {
+				  .cra_name = "md5",
+				  .cra_driver_name = "rk-md5",
+				  .cra_priority = 300,
+				  .cra_flags = CRYPTO_ALG_ASYNC,
+				  .cra_blocksize = SHA1_BLOCK_SIZE,
+				  .cra_ctxsize = sizeof(struct rk_ahash_ctx),
+				  .cra_alignmask = 0,
+				  .cra_init = rk_cra_hash_init,
+				  .cra_exit = rk_cra_hash_exit,
+				  .cra_module = THIS_MODULE,
+				  }
+			}
+	}
+};
-- 
1.9.1




From herbert at gondor.apana.org.au  Mon Jan 11 00:39:25 2016
From: herbert at gondor.apana.org.au (Herbert Xu)
Date: Mon, 11 Jan 2016 16:39:25 +0800
Subject: [RFC PATCH v5] Crypto: rockchip/crypto - add hash support for
 crypto engine in rk3288
In-Reply-To: <1452484647-30260-1-git-send-email-wzz@rock-chips.com>
References: <1452484647-30260-1-git-send-email-wzz@rock-chips.com>
Message-ID: <20160111083925.GA23039@gondor.apana.org.au>

On Mon, Jan 11, 2016 at 11:57:27AM +0800, Zain Wang wrote:
> From: Zain Wang <zain.wang at rock-chips.com>
> 
> Add md5 sha1 sha256 support for crypto engine in rk3288.
> This patch can't support multiple updatings because of limited of IC,
> as result, it can't support import and export too.

As import and export are a required part of the API you have
to implement them or your algorithm can't even be reigstered.

If you the hardware cannot support them then your choices are
limited to a software fallback.

Cheers,
-- 
Email: Herbert Xu <herbert at gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt


From lee.jones at linaro.org  Mon Jan 11 00:46:29 2016
From: lee.jones at linaro.org (Lee Jones)
Date: Mon, 11 Jan 2016 08:46:29 +0000
Subject: [PATCH] rtc: rk808: rename rtc-rk808.c to rtc-rk8xx.c
In-Reply-To: <5684a3c1.060b620a.6fa22.2470SMTPIN_ADDED_BROKEN@mx.google.com>
References: <5684a3c1.060b620a.6fa22.2470SMTPIN_ADDED_BROKEN@mx.google.com>
Message-ID: <20160111084629.GD14104@x1>

On Thu, 31 Dec 2015, zhangqing at 263.net wrote:

> From: zhangqing <zhangqing at rock-chips.com>
> 
> make rtc-rk8xx.c compatible for all pmic chips.
> for pmic chips(rk808\rk807\rk816\rk818) in the future.
> 
> Signed-off-by: zhangqing <zhangqing at rock-chips.com>
> ---
>  drivers/mfd/rk808.c                      |   2 +-

Acked-by: Lee Jones <lee.jones at linaro.org>

>  drivers/rtc/Kconfig                      |   8 +-
>  drivers/rtc/Makefile                     |   2 +-
>  drivers/rtc/{rtc-rk808.c => rtc-rk8xx.c} | 218 ++++++++++++++++++-------------
>  4 files changed, 131 insertions(+), 99 deletions(-)
>  rename drivers/rtc/{rtc-rk808.c => rtc-rk8xx.c} (64%)
> 
> diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c
> index 4b1e439..21da22b 100644
> --- a/drivers/mfd/rk808.c
> +++ b/drivers/mfd/rk808.c
> @@ -77,7 +77,7 @@ static const struct mfd_cell rk808s[] = {
>  	{ .name = "rk808-clkout", },
>  	{ .name = "rk808-regulator", },
>  	{
> -		.name = "rk808-rtc",
> +		.name = "rk8xx-rtc",
>  		.num_resources = ARRAY_SIZE(rtc_resources),
>  		.resources = &rtc_resources[0],
>  	},
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 376322f..d669473d 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -325,15 +325,15 @@ config RTC_DRV_MAX77686
>  	  This driver can also be built as a module. If so, the module
>  	  will be called rtc-max77686.
>  
> -config RTC_DRV_RK808
> -	tristate "Rockchip RK808 RTC"
> +config RTC_DRV_RK8XX
> +	tristate "Rockchip RK8XX RTC"
>  	depends on MFD_RK808
>  	help
>  	  If you say yes here you will get support for the
> -	  RTC of RK808 PMIC.
> +	  RTC of RK8XX PMIC.
>  
>  	  This driver can also be built as a module. If so, the module
> -	  will be called rk808-rtc.
> +	  will be called rk8xx-rtc.
>  
>  config RTC_DRV_MAX77802
>  	tristate "Maxim 77802 RTC"
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 62d61b2..5b1384a 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -120,7 +120,7 @@ obj-$(CONFIG_RTC_DRV_PUV3)	+= rtc-puv3.o
>  obj-$(CONFIG_RTC_DRV_PXA)	+= rtc-pxa.o
>  obj-$(CONFIG_RTC_DRV_R9701)	+= rtc-r9701.o
>  obj-$(CONFIG_RTC_DRV_RC5T583)	+= rtc-rc5t583.o
> -obj-$(CONFIG_RTC_DRV_RK808)	+= rtc-rk808.o
> +obj-$(CONFIG_RTC_DRV_RK8XX)	+= rtc-rk8xx.o
>  obj-$(CONFIG_RTC_DRV_RP5C01)	+= rtc-rp5c01.o
>  obj-$(CONFIG_RTC_DRV_RS5C313)	+= rtc-rs5c313.o
>  obj-$(CONFIG_RTC_DRV_RS5C348)	+= rtc-rs5c348.o
> diff --git a/drivers/rtc/rtc-rk808.c b/drivers/rtc/rtc-rk8xx.c
> similarity index 64%
> rename from drivers/rtc/rtc-rk808.c
> rename to drivers/rtc/rtc-rk8xx.c
> index 35c9aad..5d946bf 100644
> --- a/drivers/rtc/rtc-rk808.c
> +++ b/drivers/rtc/rtc-rk8xx.c
> @@ -1,5 +1,5 @@
>  /*
> - * RTC driver for Rockchip RK808
> + * RTC driver for Rockchip RK8XX
>   *
>   * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
>   *
> @@ -20,14 +20,33 @@
>  #include <linux/kernel.h>
>  #include <linux/rtc.h>
>  #include <linux/bcd.h>
> -#include <linux/mfd/rk808.h>
>  #include <linux/platform_device.h>
>  #include <linux/i2c.h>
> +#include <linux/regmap.h>
> +
> +#define RK8XX_SECONDS_REG			0x00
> +#define RK8XX_MINUTES_REG			0x01
> +#define RK8XX_HOURS_REG				0x02
> +#define RK8XX_DAYS_REG				0x03
> +#define RK8XX_MONTHS_REG			0x04
> +#define RK8XX_YEARS_REG				0x05
> +#define RK8XX_WEEKS_REG				0x06
> +#define RK8XX_ALARM_SECONDS_REG			0x08
> +#define RK8XX_ALARM_MINUTES_REG			0x09
> +#define RK8XX_ALARM_HOURS_REG			0x0A
> +#define RK8XX_ALARM_DAYS_REG			0x0B
> +#define RK8XX_ALARM_MONTHS_REG			0x0C
> +#define RK8XX_ALARM_YEARS_REG			0x0D
> +#define RK8XX_RTC_CTRL_REG			0x10
> +#define RK8XX_RTC_STATUS_REG			0x11
> +#define RK8XX_RTC_INT_REG			0x12
> +#define RK8XX_RTC_COMP_LSB_REG			0x13
> +#define RK8XX_RTC_COMP_MSB_REG			0x14
>  
>  /* RTC_CTRL_REG bitfields */
>  #define BIT_RTC_CTRL_REG_STOP_RTC_M		BIT(0)
>  
> -/* RK808 has a shadowed register for saving a "frozen" RTC time.
> +/* RK8xx has a shadowed register for saving a "frozen" RTC time.
>   * When user setting "GET_TIME" to 1, the time will save in this shadowed
>   * register. If set "READSEL" to 1, user read rtc time register, actually
>   * get the time of that moment. If we need the real time, clr this bit.
> @@ -47,17 +66,25 @@
>  
>  /* REG_SECONDS_REG through REG_YEARS_REG is how many registers? */
>  
> -#define NUM_TIME_REGS	(RK808_WEEKS_REG - RK808_SECONDS_REG + 1)
> -#define NUM_ALARM_REGS	(RK808_ALARM_YEARS_REG - RK808_ALARM_SECONDS_REG + 1)
> +#define NUM_TIME_REGS	(RK8XX_WEEKS_REG - RK8XX_SECONDS_REG + 1)
> +#define NUM_ALARM_REGS	(RK8XX_ALARM_YEARS_REG - RK8XX_ALARM_SECONDS_REG + 1)
>  
> -struct rk808_rtc {
> -	struct rk808 *rk808;
> +static const struct regmap_config rk8xx_rtc_regmap_config = {
> +	.reg_bits = 8,
> +	.val_bits = 8,
> +	.max_register = RK8XX_RTC_COMP_MSB_REG,
> +	.cache_type = REGCACHE_RBTREE,
> +};
> +
> +struct rk8xx_rtc {
>  	struct rtc_device *rtc;
> +	struct i2c_client *i2c;
> +	struct regmap *regmap;
>  	int irq;
>  };
>  
>  /*
> - * The Rockchip calendar used by the RK808 counts November with 31 days. We use
> + * The Rockchip calendar used by the RK8xx counts November with 31 days. We use
>   * these translation functions to convert its dates to/from the Gregorian
>   * calendar used by the rest of the world. We arbitrarily define Jan 1st, 2016
>   * as the day when both calendars were in sync, and treat all other dates
> @@ -74,6 +101,7 @@ static void rockchip_to_gregorian(struct rtc_time *tm)
>  {
>  	/* If it's Nov 31st, rtc_tm_to_time64() will count that like Dec 1st */
>  	time64_t time = rtc_tm_to_time64(tm);
> +
>  	rtc_time64_to_tm(time + nov2dec_transitions(tm) * 86400, tm);
>  }
>  
> @@ -81,6 +109,7 @@ static void gregorian_to_rockchip(struct rtc_time *tm)
>  {
>  	time64_t extra_days = nov2dec_transitions(tm);
>  	time64_t time = rtc_tm_to_time64(tm);
> +
>  	rtc_time64_to_tm(time - extra_days * 86400, tm);
>  
>  	/* Compensate if we went back over Nov 31st (will work up to 2381) */
> @@ -93,15 +122,14 @@ static void gregorian_to_rockchip(struct rtc_time *tm)
>  }
>  
>  /* Read current time and date in RTC */
> -static int rk808_rtc_readtime(struct device *dev, struct rtc_time *tm)
> +static int rk8xx_rtc_readtime(struct device *dev, struct rtc_time *tm)
>  {
> -	struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev);
> -	struct rk808 *rk808 = rk808_rtc->rk808;
> +	struct rk8xx_rtc *rk8xx_rtc = dev_get_drvdata(dev);
>  	u8 rtc_data[NUM_TIME_REGS];
>  	int ret;
>  
>  	/* Force an update of the shadowed registers right now */
> -	ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
> +	ret = regmap_update_bits(rk8xx_rtc->regmap, RK8XX_RTC_CTRL_REG,
>  				 BIT_RTC_CTRL_REG_RTC_GET_TIME,
>  				 BIT_RTC_CTRL_REG_RTC_GET_TIME);
>  	if (ret) {
> @@ -115,7 +143,7 @@ static int rk808_rtc_readtime(struct device *dev, struct rtc_time *tm)
>  	 * 32khz. If we clear the GET_TIME bit here, the time of i2c transfer
>  	 * certainly more than 31.25us: 16 * 2.5us at 400kHz bus frequency.
>  	 */
> -	ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
> +	ret = regmap_update_bits(rk8xx_rtc->regmap, RK8XX_RTC_CTRL_REG,
>  				 BIT_RTC_CTRL_REG_RTC_GET_TIME,
>  				 0);
>  	if (ret) {
> @@ -123,7 +151,7 @@ static int rk808_rtc_readtime(struct device *dev, struct rtc_time *tm)
>  		return ret;
>  	}
>  
> -	ret = regmap_bulk_read(rk808->regmap, RK808_SECONDS_REG,
> +	ret = regmap_bulk_read(rk8xx_rtc->regmap, RK8XX_SECONDS_REG,
>  			       rtc_data, NUM_TIME_REGS);
>  	if (ret) {
>  		dev_err(dev, "Failed to bulk read rtc_data: %d\n", ret);
> @@ -146,10 +174,9 @@ static int rk808_rtc_readtime(struct device *dev, struct rtc_time *tm)
>  }
>  
>  /* Set current time and date in RTC */
> -static int rk808_rtc_set_time(struct device *dev, struct rtc_time *tm)
> +static int rk8xx_rtc_set_time(struct device *dev, struct rtc_time *tm)
>  {
> -	struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev);
> -	struct rk808 *rk808 = rk808_rtc->rk808;
> +	struct rk8xx_rtc *rk8xx_rtc = dev_get_drvdata(dev);
>  	u8 rtc_data[NUM_TIME_REGS];
>  	int ret;
>  
> @@ -166,7 +193,7 @@ static int rk808_rtc_set_time(struct device *dev, struct rtc_time *tm)
>  	rtc_data[6] = bin2bcd(tm->tm_wday);
>  
>  	/* Stop RTC while updating the RTC registers */
> -	ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
> +	ret = regmap_update_bits(rk8xx_rtc->regmap, RK8XX_RTC_CTRL_REG,
>  				 BIT_RTC_CTRL_REG_STOP_RTC_M,
>  				 BIT_RTC_CTRL_REG_STOP_RTC_M);
>  	if (ret) {
> @@ -174,14 +201,14 @@ static int rk808_rtc_set_time(struct device *dev, struct rtc_time *tm)
>  		return ret;
>  	}
>  
> -	ret = regmap_bulk_write(rk808->regmap, RK808_SECONDS_REG,
> +	ret = regmap_bulk_write(rk8xx_rtc->regmap, RK8XX_SECONDS_REG,
>  				rtc_data, NUM_TIME_REGS);
>  	if (ret) {
>  		dev_err(dev, "Failed to bull write rtc_data: %d\n", ret);
>  		return ret;
>  	}
>  	/* Start RTC again */
> -	ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
> +	ret = regmap_update_bits(rk8xx_rtc->regmap, RK8XX_RTC_CTRL_REG,
>  				 BIT_RTC_CTRL_REG_STOP_RTC_M, 0);
>  	if (ret) {
>  		dev_err(dev, "Failed to update RTC control: %d\n", ret);
> @@ -191,15 +218,15 @@ static int rk808_rtc_set_time(struct device *dev, struct rtc_time *tm)
>  }
>  
>  /* Read alarm time and date in RTC */
> -static int rk808_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
> +static int rk8xx_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
>  {
> -	struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev);
> -	struct rk808 *rk808 = rk808_rtc->rk808;
> +	struct rk8xx_rtc *rk8xx_rtc = dev_get_drvdata(dev);
>  	u8 alrm_data[NUM_ALARM_REGS];
>  	uint32_t int_reg;
>  	int ret;
>  
> -	ret = regmap_bulk_read(rk808->regmap, RK808_ALARM_SECONDS_REG,
> +	ret = regmap_bulk_read(rk8xx_rtc->regmap,
> +			       RK8XX_ALARM_SECONDS_REG,
>  			       alrm_data, NUM_ALARM_REGS);
>  
>  	alrm->time.tm_sec = bcd2bin(alrm_data[0] & SECONDS_REG_MSK);
> @@ -210,7 +237,8 @@ static int rk808_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
>  	alrm->time.tm_year = (bcd2bin(alrm_data[5] & YEARS_REG_MSK)) + 100;
>  	rockchip_to_gregorian(&alrm->time);
>  
> -	ret = regmap_read(rk808->regmap, RK808_RTC_INT_REG, &int_reg);
> +	ret = regmap_read(rk8xx_rtc->regmap,
> +			  RK8XX_RTC_INT_REG, &int_reg);
>  	if (ret) {
>  		dev_err(dev, "Failed to read RTC INT REG: %d\n", ret);
>  		return ret;
> @@ -226,37 +254,34 @@ static int rk808_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
>  	return 0;
>  }
>  
> -static int rk808_rtc_stop_alarm(struct rk808_rtc *rk808_rtc)
> +static int rk8xx_rtc_stop_alarm(struct rk8xx_rtc *rk8xx_rtc)
>  {
> -	struct rk808 *rk808 = rk808_rtc->rk808;
>  	int ret;
>  
> -	ret = regmap_update_bits(rk808->regmap, RK808_RTC_INT_REG,
> +	ret = regmap_update_bits(rk8xx_rtc->regmap, RK8XX_RTC_INT_REG,
>  				 BIT_RTC_INTERRUPTS_REG_IT_ALARM_M, 0);
>  
>  	return ret;
>  }
>  
> -static int rk808_rtc_start_alarm(struct rk808_rtc *rk808_rtc)
> +static int rk8xx_rtc_start_alarm(struct rk8xx_rtc *rk8xx_rtc)
>  {
> -	struct rk808 *rk808 = rk808_rtc->rk808;
>  	int ret;
>  
> -	ret = regmap_update_bits(rk808->regmap, RK808_RTC_INT_REG,
> +	ret = regmap_update_bits(rk8xx_rtc->regmap, RK8XX_RTC_INT_REG,
>  				 BIT_RTC_INTERRUPTS_REG_IT_ALARM_M,
>  				 BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
>  
>  	return ret;
>  }
>  
> -static int rk808_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
> +static int rk8xx_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
>  {
> -	struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev);
> -	struct rk808 *rk808 = rk808_rtc->rk808;
> +	struct rk8xx_rtc *rk8xx_rtc = dev_get_drvdata(dev);
>  	u8 alrm_data[NUM_ALARM_REGS];
>  	int ret;
>  
> -	ret = rk808_rtc_stop_alarm(rk808_rtc);
> +	ret = rk8xx_rtc_stop_alarm(rk8xx_rtc);
>  	if (ret) {
>  		dev_err(dev, "Failed to stop alarm: %d\n", ret);
>  		return ret;
> @@ -274,14 +299,15 @@ static int rk808_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
>  	alrm_data[4] = bin2bcd(alrm->time.tm_mon + 1);
>  	alrm_data[5] = bin2bcd(alrm->time.tm_year - 100);
>  
> -	ret = regmap_bulk_write(rk808->regmap, RK808_ALARM_SECONDS_REG,
> +	ret = regmap_bulk_write(rk8xx_rtc->regmap,
> +				RK8XX_ALARM_SECONDS_REG,
>  				alrm_data, NUM_ALARM_REGS);
>  	if (ret) {
>  		dev_err(dev, "Failed to bulk write: %d\n", ret);
>  		return ret;
>  	}
>  	if (alrm->enabled) {
> -		ret = rk808_rtc_start_alarm(rk808_rtc);
> +		ret = rk8xx_rtc_start_alarm(rk8xx_rtc);
>  		if (ret) {
>  			dev_err(dev, "Failed to start alarm: %d\n", ret);
>  			return ret;
> @@ -290,15 +316,15 @@ static int rk808_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
>  	return 0;
>  }
>  
> -static int rk808_rtc_alarm_irq_enable(struct device *dev,
> +static int rk8xx_rtc_alarm_irq_enable(struct device *dev,
>  				      unsigned int enabled)
>  {
> -	struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev);
> +	struct rk8xx_rtc *rk8xx_rtc = dev_get_drvdata(dev);
>  
>  	if (enabled)
> -		return rk808_rtc_start_alarm(rk808_rtc);
> +		return rk8xx_rtc_start_alarm(rk8xx_rtc);
>  
> -	return rk808_rtc_stop_alarm(rk808_rtc);
> +	return rk8xx_rtc_stop_alarm(rk8xx_rtc);
>  }
>  
>  /*
> @@ -311,14 +337,13 @@ static int rk808_rtc_alarm_irq_enable(struct device *dev,
>   * bit 2: every hour
>   * bit 3: every day
>   */
> -static irqreturn_t rk808_alarm_irq(int irq, void *data)
> +static irqreturn_t rk8xx_alarm_irq(int irq, void *data)
>  {
> -	struct rk808_rtc *rk808_rtc = data;
> -	struct rk808 *rk808 = rk808_rtc->rk808;
> -	struct i2c_client *client = rk808->i2c;
> +	struct rk8xx_rtc *rk8xx_rtc = data;
> +	struct i2c_client *client = rk8xx_rtc->i2c;
>  	int ret;
>  
> -	ret = regmap_write(rk808->regmap, RK808_RTC_STATUS_REG,
> +	ret = regmap_write(rk8xx_rtc->regmap, RK8XX_RTC_STATUS_REG,
>  			   RTC_STATUS_MASK);
>  	if (ret) {
>  		dev_err(&client->dev,
> @@ -326,29 +351,29 @@ static irqreturn_t rk808_alarm_irq(int irq, void *data)
>  		return ret;
>  	}
>  
> -	rtc_update_irq(rk808_rtc->rtc, 1, RTC_IRQF | RTC_AF);
> +	rtc_update_irq(rk8xx_rtc->rtc, 1, RTC_IRQF | RTC_AF);
>  	dev_dbg(&client->dev,
> -		 "%s:irq=%d\n", __func__, irq);
> +		"%s:irq=%d\n", __func__, irq);
>  	return IRQ_HANDLED;
>  }
>  
> -static const struct rtc_class_ops rk808_rtc_ops = {
> -	.read_time = rk808_rtc_readtime,
> -	.set_time = rk808_rtc_set_time,
> -	.read_alarm = rk808_rtc_readalarm,
> -	.set_alarm = rk808_rtc_setalarm,
> -	.alarm_irq_enable = rk808_rtc_alarm_irq_enable,
> +static const struct rtc_class_ops rk8xx_rtc_ops = {
> +	.read_time = rk8xx_rtc_readtime,
> +	.set_time = rk8xx_rtc_set_time,
> +	.read_alarm = rk8xx_rtc_readalarm,
> +	.set_alarm = rk8xx_rtc_setalarm,
> +	.alarm_irq_enable = rk8xx_rtc_alarm_irq_enable,
>  };
>  
>  #ifdef CONFIG_PM_SLEEP
>  /* Turn off the alarm if it should not be a wake source. */
> -static int rk808_rtc_suspend(struct device *dev)
> +static int rk8xx_rtc_suspend(struct device *dev)
>  {
>  	struct platform_device *pdev = to_platform_device(dev);
> -	struct rk808_rtc *rk808_rtc = dev_get_drvdata(&pdev->dev);
> +	struct rk8xx_rtc *rk8xx_rtc = dev_get_drvdata(&pdev->dev);
>  
>  	if (device_may_wakeup(dev))
> -		enable_irq_wake(rk808_rtc->irq);
> +		enable_irq_wake(rk8xx_rtc->irq);
>  
>  	return 0;
>  }
> @@ -356,37 +381,44 @@ static int rk808_rtc_suspend(struct device *dev)
>  /* Enable the alarm if it should be enabled (in case it was disabled to
>   * prevent use as a wake source).
>   */
> -static int rk808_rtc_resume(struct device *dev)
> +static int rk8xx_rtc_resume(struct device *dev)
>  {
>  	struct platform_device *pdev = to_platform_device(dev);
> -	struct rk808_rtc *rk808_rtc = dev_get_drvdata(&pdev->dev);
> +	struct rk8xx_rtc *rk8xx_rtc = dev_get_drvdata(&pdev->dev);
>  
>  	if (device_may_wakeup(dev))
> -		disable_irq_wake(rk808_rtc->irq);
> +		disable_irq_wake(rk8xx_rtc->irq);
>  
>  	return 0;
>  }
>  #endif
>  
> -static SIMPLE_DEV_PM_OPS(rk808_rtc_pm_ops,
> -	rk808_rtc_suspend, rk808_rtc_resume);
> +static SIMPLE_DEV_PM_OPS(rk8xx_rtc_pm_ops,
> +	rk8xx_rtc_suspend, rk8xx_rtc_resume);
>  
> -static int rk808_rtc_probe(struct platform_device *pdev)
> +static int rk8xx_rtc_probe(struct platform_device *pdev)
>  {
> -	struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent);
> -	struct rk808_rtc *rk808_rtc;
> +	struct i2c_client *client = to_i2c_client(pdev->dev.parent);
> +	struct rk8xx_rtc *rk8xx_rtc;
>  	struct rtc_time tm;
>  	int ret;
>  
> -	rk808_rtc = devm_kzalloc(&pdev->dev, sizeof(*rk808_rtc), GFP_KERNEL);
> -	if (rk808_rtc == NULL)
> +	rk8xx_rtc = devm_kzalloc(&pdev->dev, sizeof(*rk8xx_rtc), GFP_KERNEL);
> +	if (rk8xx_rtc == NULL)
>  		return -ENOMEM;
>  
> -	platform_set_drvdata(pdev, rk808_rtc);
> -	rk808_rtc->rk808 = rk808;
> +	rk8xx_rtc->regmap = devm_regmap_init_i2c(client,
> +					     &rk8xx_rtc_regmap_config);
> +	if (IS_ERR(rk8xx_rtc->regmap)) {
> +		dev_err(&pdev->dev, "regmap initialization failed\n");
> +		return PTR_ERR(rk8xx_rtc->regmap);
> +	}
> +
> +	platform_set_drvdata(pdev, rk8xx_rtc);
> +	rk8xx_rtc->i2c = client;
>  
>  	/* start rtc running by default, and use shadowed timer. */
> -	ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
> +	ret = regmap_update_bits(rk8xx_rtc->regmap, RK8XX_RTC_CTRL_REG,
>  				 BIT_RTC_CTRL_REG_STOP_RTC_M |
>  				 BIT_RTC_CTRL_REG_RTC_READSEL_M,
>  				 BIT_RTC_CTRL_REG_RTC_READSEL_M);
> @@ -396,7 +428,7 @@ static int rk808_rtc_probe(struct platform_device *pdev)
>  		return ret;
>  	}
>  
> -	ret = regmap_write(rk808->regmap, RK808_RTC_STATUS_REG,
> +	ret = regmap_write(rk8xx_rtc->regmap, RK8XX_RTC_STATUS_REG,
>  			   RTC_STATUS_MASK);
>  	if (ret) {
>  		dev_err(&pdev->dev,
> @@ -405,7 +437,7 @@ static int rk808_rtc_probe(struct platform_device *pdev)
>  	}
>  
>  	/* set init time */
> -	ret = rk808_rtc_readtime(&pdev->dev, &tm);
> +	ret = rk8xx_rtc_readtime(&pdev->dev, &tm);
>  	if (ret) {
>  		dev_err(&pdev->dev, "Failed to read RTC time\n");
>  		return ret;
> @@ -416,45 +448,45 @@ static int rk808_rtc_probe(struct platform_device *pdev)
>  
>  	device_init_wakeup(&pdev->dev, 1);
>  
> -	rk808_rtc->rtc = devm_rtc_device_register(&pdev->dev, "rk808-rtc",
> -						  &rk808_rtc_ops, THIS_MODULE);
> -	if (IS_ERR(rk808_rtc->rtc)) {
> -		ret = PTR_ERR(rk808_rtc->rtc);
> +	rk8xx_rtc->rtc = devm_rtc_device_register(&pdev->dev, "rk8xx-rtc",
> +						  &rk8xx_rtc_ops, THIS_MODULE);
> +	if (IS_ERR(rk8xx_rtc->rtc)) {
> +		ret = PTR_ERR(rk8xx_rtc->rtc);
>  		return ret;
>  	}
>  
> -	rk808_rtc->irq = platform_get_irq(pdev, 0);
> -	if (rk808_rtc->irq < 0) {
> -		if (rk808_rtc->irq != -EPROBE_DEFER)
> +	rk8xx_rtc->irq = platform_get_irq(pdev, 0);
> +	if (rk8xx_rtc->irq < 0) {
> +		if (rk8xx_rtc->irq != -EPROBE_DEFER)
>  			dev_err(&pdev->dev, "Wake up is not possible as irq = %d\n",
> -				rk808_rtc->irq);
> -		return rk808_rtc->irq;
> +				rk8xx_rtc->irq);
> +		return rk8xx_rtc->irq;
>  	}
>  
> -	/* request alarm irq of rk808 */
> -	ret = devm_request_threaded_irq(&pdev->dev, rk808_rtc->irq, NULL,
> -					rk808_alarm_irq, 0,
> -					"RTC alarm", rk808_rtc);
> +	/* request alarm irq of rk8xx */
> +	ret = devm_request_threaded_irq(&pdev->dev, rk8xx_rtc->irq, NULL,
> +					rk8xx_alarm_irq, 0,
> +					"RTC alarm", rk8xx_rtc);
>  	if (ret) {
>  		dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n",
> -			rk808_rtc->irq, ret);
> +			rk8xx_rtc->irq, ret);
>  	}
>  
>  	return ret;
>  }
>  
> -static struct platform_driver rk808_rtc_driver = {
> -	.probe = rk808_rtc_probe,
> +static struct platform_driver rk8xx_rtc_driver = {
> +	.probe = rk8xx_rtc_probe,
>  	.driver = {
> -		.name = "rk808-rtc",
> -		.pm = &rk808_rtc_pm_ops,
> +		.name = "rk8xx-rtc",
> +		.pm = &rk8xx_rtc_pm_ops,
>  	},
>  };
>  
> -module_platform_driver(rk808_rtc_driver);
> +module_platform_driver(rk8xx_rtc_driver);
>  
> -MODULE_DESCRIPTION("RTC driver for the rk808 series PMICs");
> +MODULE_DESCRIPTION("RTC driver for the rk8xx series PMICs");
>  MODULE_AUTHOR("Chris Zhong <zyw at rock-chips.com>");
>  MODULE_AUTHOR("Zhang Qing <zhangqing at rock-chips.com>");
>  MODULE_LICENSE("GPL");
> -MODULE_ALIAS("platform:rk808-rtc");
> +MODULE_ALIAS("platform:rk8xx-rtc");

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog


From zain.wang at rock-chips.com  Mon Jan 11 02:26:03 2016
From: zain.wang at rock-chips.com (zain)
Date: Mon, 11 Jan 2016 18:26:03 +0800
Subject: [RFC PATCH v5] Crypto: rockchip/crypto - add hash support for
 crypto engine in rk3288
In-Reply-To: <20160111083925.GA23039@gondor.apana.org.au>
References: <1452484647-30260-1-git-send-email-wzz@rock-chips.com>
 <20160111083925.GA23039@gondor.apana.org.au>
Message-ID: <5693833B.8050704@rock-chips.com>



On 2016?01?11? 16:39, Herbert Xu wrote:
> On Mon, Jan 11, 2016 at 11:57:27AM +0800, Zain Wang wrote:
>> From: Zain Wang <zain.wang at rock-chips.com>
>>
>> Add md5 sha1 sha256 support for crypto engine in rk3288.
>> This patch can't support multiple updatings because of limited of IC,
>> as result, it can't support import and export too.
> As import and export are a required part of the API you have
> to implement them or your algorithm can't even be reigstered.
>
> If you the hardware cannot support them then your choices are
> limited to a software fallback.
>
> Cheers,
Ok! Thanks your comment.
I will add a software fallback like img-hash.c do



From heiko at sntech.de  Mon Jan 11 03:36:51 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Mon, 11 Jan 2016 12:36:51 +0100
Subject: [PATCH] ARM: dts: rockchip: Add support emac for RK3036
In-Reply-To: <1452217026-21155-1-git-send-email-zhengxing@rock-chips.com>
References: <1452217026-21155-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <2062991.rcWfmTiAzP@phil>

Am Freitag, 8. Januar 2016, 09:37:06 schrieb Xing Zheng:
> This patch describe the emac, and we need to let mac clock under
> the APLL which is able to provide the accurate 50MHz what mac_ref
> need.
> 
> Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>

I've queued this to get applied after 4.5-rc1 is out and I have a clean base 
again to add the needed shared clock-branch.


Heiko


From caesar.upstream at gmail.com  Mon Jan 11 05:27:45 2016
From: caesar.upstream at gmail.com (Caesar Wang)
Date: Mon, 11 Jan 2016 21:27:45 +0800
Subject: [PATCH v3 2/3] soc: rockchip: power-domain: Modify power domain
 driver for rk3368
In-Reply-To: <1452508600-3512-3-git-send-email-zhangqing@rock-chips.com>
References: <1452508600-3512-1-git-send-email-zhangqing@rock-chips.com>
 <1452508600-3512-3-git-send-email-zhangqing@rock-chips.com>
Message-ID: <5693ADD1.4000207@gmail.com>

Hi zhangqin,

 From my datasheet "Rockchip RK3368 TRM V2.0.pdf"
Maybe i'm missing something.


? 2016?01?11? 18:36, zhangqing ??:
> This driver is modified to support RK3368 SoC.
>
> Signed-off-by: zhangqing <zhangqing at rock-chips.com>
> ---
>   drivers/soc/rockchip/pm_domains.c | 33 +++++++++++++++++++++++++++++++++
>   1 file changed, 33 insertions(+)
>
> diff --git a/drivers/soc/rockchip/pm_domains.c b/drivers/soc/rockchip/pm_domains.c
> index 534c589..6cdffb1 100644
> --- a/drivers/soc/rockchip/pm_domains.c
> +++ b/drivers/soc/rockchip/pm_domains.c
> @@ -18,6 +18,7 @@
>   #include <linux/regmap.h>
>   #include <linux/mfd/syscon.h>
>   #include <dt-bindings/power/rk3288-power.h>
> +#include <dt-bindings/power/rk3368-power.h>
>   
>   struct rockchip_domain_info {
>   	int pwr_mask;
> @@ -75,6 +76,9 @@ struct rockchip_pmu {
>   #define DOMAIN_RK3288(pwr, status, req)		\
>   	DOMAIN(pwr, status, req, req, (req) + 16)
>   
> +#define DOMAIN_RK3368(pwr, status, req)		\
> +	DOMAIN(pwr, status, req, (req) + 16, req)
> +

You should  remove it, that's seem same with the rk3288.

The rk3368 datasheet:
PMU_PMU_BUS_IDLE_ST  ----->idle_vio[24]----->ack
PMU_PMU_BUS_IDLE_REQ ---->idle_req_vio[8]----->idle

>   static bool rockchip_pmu_domain_is_idle(struct rockchip_pm_domain *pd)
>   {
>   	struct rockchip_pmu *pmu = pd->pmu;
> @@ -444,6 +448,14 @@ static const struct rockchip_domain_info rk3288_pm_domains[] = {
>   	[RK3288_PD_GPU]		= DOMAIN_RK3288(9, 9, 2),
>   };
>   
> +static const struct rockchip_domain_info rk3368_pm_domains[] = {
> +	[RK3368_PD_PERI]	= DOMAIN_RK3368(13, 12, 6),
> +	[RK3368_PD_VIO]		= DOMAIN_RK3368(15, 14, 8),
> +	[RK3368_PD_VIDEO]	= DOMAIN_RK3368(14, 13, 7),
> +	[RK3368_PD_GPU_0]	= DOMAIN_RK3368(16, 15, 2),
> +	[RK3368_PD_GPU_1]	= DOMAIN_RK3368(17, 16, 2),
> +};
> +
>   static const struct rockchip_pmu_info rk3288_pmu = {
>   	.pwr_offset = 0x08,
>   	.status_offset = 0x0c,
> @@ -461,11 +473,32 @@ static const struct rockchip_pmu_info rk3288_pmu = {
>   	.domain_info = rk3288_pm_domains,
>   };
>   
> +static const struct rockchip_pmu_info rk3368_pmu = {
> +	.pwr_offset = 0x0c,
> +	.status_offset = 0x10,
> +	.req_offset = 0x3c,
> +	.idle_offset = 0x40,
> +	.ack_offset = 0x40,
> +
> +	.core_pwrcnt_offset = 0x48,
> +	.gpu_pwrcnt_offset = 0x50,
> +
> +	.core_power_transition_time = 24,
> +	.gpu_power_transition_time = 24,
> +
> +	.num_domains = ARRAY_SIZE(rk3368_pm_domains),
> +	.domain_info = rk3368_pm_domains,
> +};
> +
>   static const struct of_device_id rockchip_pm_domain_dt_match[] = {
>   	{
>   		.compatible = "rockchip,rk3288-power-controller",
>   		.data = (void *)&rk3288_pmu,
>   	},
> +	{
> +		.compatible = "rockchip,rk3368-power-controller",
> +		.data = (void *)&rk3368_pmu,
> +	},
>   	{ /* sentinel */ },
>   };
>   
>
> -- 
> Thanks,
> Caesar


From caesar.upstream at gmail.com  Mon Jan 11 05:38:30 2016
From: caesar.upstream at gmail.com (Caesar Wang)
Date: Mon, 11 Jan 2016 21:38:30 +0800
Subject: [PATCH v2 03/12] ARM: dts: rockchip: override the clocks in i2s
 for kylin board
In-Reply-To: <1878139.nbddSBf70A@phil>
References: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
 <1452155155-16232-4-git-send-email-wxt@rock-chips.com>
 <1878139.nbddSBf70A@phil>
Message-ID: <5693B056.7010507@gmail.com>

Heiko,

? 2016?01?09? 10:31, Heiko Stuebner ??:
> Hi Caesar,
>
> Am Donnerstag, 7. Januar 2016, 16:25:46 schrieb Caesar Wang:
>> The sysclk seems be incorrect since we use the simple card for kylin
>> board.
>>
>> The simple card call:
>>
>> asoc_simple_card_probe ->
>>   asoc_simple_card_dai_link_of ->
>>    asoc_simple_card_sub_parse_of ->
>> ...
>> clk = of_clk_get(args.np, 0);
>> if (!IS_ERR(clk))
>> dai->sysclk = clk_get_rate(clk);
>>
>> The sysclk come from the first clock, then first clock is hclk_i2s
>> in i2s for rk3036 dtsi.
>> So, we can override the clocks to fit the simple card in here.
> It's not that effective, doing that change on a per-board level - as it
> would possibly need to be repeated for future boards.
> The i2s driver doesn't care, as it uses named clocks, so I instead did
> the swap in the rk3036.dtsi for all boards. Can you check if that is ok
> for you or if I did overlook something?

That's okay for me.

>
> Thanks
> Heiko
>
> ------- 8< -------
> Subject: [PATCH] ARM: dts: rockchip: swap i2s clock ordering on rk3036
>
> For sound setups using the simple-card mechanism, the main clock
> (sysclk) is expected to be the first element. For the i2s-driver
> itself it doesn't matter, as it uses named clocks, so we can just
> swap them.
>
> Reported-by: Caesar Wang <wxt at rock-chips.com>
> Signed-off-by: Heiko Stuebner <heiko at sntech.de>
> ---
>   arch/arm/boot/dts/rk3036.dtsi | 4 ++--
>   1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
> index 8f1bb0f..ee457a2 100644
> --- a/arch/arm/boot/dts/rk3036.dtsi
> +++ b/arch/arm/boot/dts/rk3036.dtsi
> @@ -241,8 +241,8 @@
>   		interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;
>   		#address-cells = <1>;
>   		#size-cells = <0>;
> -		clock-names = "i2s_hclk", "i2s_clk";
> -		clocks = <&cru HCLK_I2S>, <&cru SCLK_I2S>;
> +		clock-names = "i2s_clk", "i2s_hclk";
> +		clocks = <&cru SCLK_I2S>, <&cru HCLK_I2S>;
>   		dmas = <&pdma 0>, <&pdma 1>;
>   		dma-names = "tx", "rx";
>   		pinctrl-names = "default";


-- 
Thanks,
Caesar



From javier at osg.samsung.com  Mon Jan 11 08:02:46 2016
From: javier at osg.samsung.com (Javier Martinez Canillas)
Date: Mon, 11 Jan 2016 13:02:46 -0300
Subject: [PATCH v2 06/12] mmc: pwrseq: add support for power-on sequencing
 through DT
In-Reply-To: <6960259.dpUH8KWsNS@phil>
References: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
 <1452155155-16232-7-git-send-email-wxt@rock-chips.com>
 <568FAA07.1010509@osg.samsung.com> <6960259.dpUH8KWsNS@phil>
Message-ID: <5693D226.2000307@osg.samsung.com>

Hello Heiko,

On 01/08/2016 11:42 PM, Heiko Stuebner wrote:
> Am Freitag, 8. Januar 2016, 09:22:31 schrieb Javier Martinez Canillas:
>>> For example:
>>> we need enable wifi module power to via the WL_REG_ON
>>> pin, we need enable it as the regulator if this pin is connected to
>>> the gpio of cpu.
>>
>> This part confuses me, so does your chip have an actual regulator that
>> needs to be enabled or is just a fake regulator whose gpio property is
>> used not to enable the regulator but to toggle the WL_REG_ON pin of
>> the WiFi chip?
> 
> another option would be to use the reset-gpio-handles. rk3288-veyron and I 
> think some Exynos as well use it that way.
>

Yes I know, my point was that the reset-gpios property should be used
instead of a fake regulator if what's needed is to toggle a chip pin.

>
>>> Maybe, someone will say that can pull up/down from dts.
>>> Unfortunately some SoCs can't support pinctrl pull up/down in
>>> internal.
>>
>> Can you please elaborate on this? AFAIU this limitation is the reason
>> why you went with the regulator approach so I think it deserve a more
>> deep explanation.
> 
> On the rk3036 each pin has an individual unchangable pull direction. So it's 
> either no bias or pulling in the predefined direction (the pin_default bias 
> option).
>

I think each change has to be justified on its own so I would say that
having a regulator enabled as a part of a SDIO chip's power sequencing
is something needed for many platforms, and that this provider should
be extended to support that (something like commit msg in patch 05/12).

And then in the kylin DTS change (patch 08/12), I would explain why a
chained regulators approach is used/needed instead of the reset-gpios
due any platform limitations.

> 
> Heiko
> 

Best regards,
-- 
Javier Martinez Canillas
Open Source Group
Samsung Research America


From sjg at chromium.org  Mon Jan 11 08:58:27 2016
From: sjg at chromium.org (Simon Glass)
Date: Mon, 11 Jan 2016 09:58:27 -0700
Subject: [PATCH v2 2/6] ARM: bootm: Try to use relocated ramdisk
In-Reply-To: <1452249212-25183-3-git-send-email-jeffy.chen@rock-chips.com>
References: <1452249212-25183-1-git-send-email-jeffy.chen@rock-chips.com>
 <1452249212-25183-3-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <CAPnjgZ0S7kzGn9mSqN_1e-zAZDHeRO5V-P27ZvsT9e8nNTyC=w@mail.gmail.com>

On 8 January 2016 at 03:33, Jeffy Chen <jeffy.chen at rock-chips.com> wrote:
> After boot_ramdisk_high(), ramdisk would be relocated to
> initrd_start & initrd_end, so use them instead of rd_start & rd_end.
>
> Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
>
> ---
>
> Changes in v2:
> Add comments.
>
>  arch/arm/lib/bootm.c | 12 +++++++++++-
>  1 file changed, 11 insertions(+), 1 deletion(-)

Acked-by: Simon Glass <sjg at chromium.org>


From sjg at chromium.org  Mon Jan 11 08:58:30 2016
From: sjg at chromium.org (Simon Glass)
Date: Mon, 11 Jan 2016 09:58:30 -0700
Subject: [PATCH v1 5/6] rockchip: kylin: Enable boot with android boot
 image
In-Reply-To: <568F94B8.60101@rock-chips.com>
References: <1451893823-20309-1-git-send-email-jeffy.chen@rock-chips.com>
 <1451893823-20309-6-git-send-email-jeffy.chen@rock-chips.com>
 <CAPnjgZ1G=5=gqy6yGV1NZ13Zv0BACRYMPpdAoFAi+sv2TPXknw@mail.gmail.com>
 <568F94B8.60101@rock-chips.com>
Message-ID: <CAPnjgZ2_wS1N8uO23+M0RJZovVFx7Wc_uMqOC37nGGBQN7NZZA@mail.gmail.com>

Hi Jeffy,

On 8 January 2016 at 03:51, Jeffy Chen <jeffy.chen at rock-chips.com> wrote:
> Hi Simon,
>
> On 2016-1-8 11:34, Simon Glass wrote:
>>
>> Hi Jeffy,
>>
>> On 4 January 2016 at 00:50, Jeffy Chen <jeffy.chen at rock-chips.com> wrote:
>>>
>>> The android kernel is using appended dtb by default, and store
>>> ramdisk right after kernel & dtb.
>>> So we needs to relocate ramdisk, and use atags to pass params.
>>>
>>> Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
>>> ---
>>>
>>>   include/configs/kylin_rk3036.h | 23 +++++++++++++++++++++++
>>>   1 file changed, 23 insertions(+)
>>
>> Acked-by: Simon Glass <sjg at chromium.org>
>>
>> What kernel version are you using?
>
> It's kernel v4.1 for android project:
> git fetch
> https://android.googlesource.com/platform/hardware/bsp/kernel/common/v4.1
> refs/changes/03/188603/5 && git checkout FETCH_HEAD :)

OK - just wondered why it needs ATAGs (CONFIG_SETUP_MEMORY_TAGS) - are
there things that device tree does not support?

>>
>>
>> Regards,
>> Simon
>>
>
> On 2016-1-8 11:34, Simon Glass wrote:
>>
>> Hi Jeffy,
>>
>> On 4 January 2016 at 00:50, Jeffy Chen <jeffy.chen at rock-chips.com> wrote:
>>>
>>> The android kernel is using appended dtb by default, and store
>>> ramdisk right after kernel & dtb.
>>> So we needs to relocate ramdisk, and use atags to pass params.
>>>
>>> Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
>>> ---
>>>
>>>   include/configs/kylin_rk3036.h | 23 +++++++++++++++++++++++
>>>   1 file changed, 23 insertions(+)
>>
>> Acked-by: Simon Glass <sjg at chromium.org>
>>
>> What kernel version are you using?
>>
>> Regards,
>> Simon
>>
>

Regards,
Simon


From zhangqing at rock-chips.com  Tue Jan 12 00:57:08 2016
From: zhangqing at rock-chips.com (zhangqing)
Date: Tue, 12 Jan 2016 00:57:08 -0800
Subject: [PATCH v3 2/3] soc: rockchip: power-domain: Modify power domain
 driver for rk3368
In-Reply-To: <5693ADD1.4000207@gmail.com>
References: <1452508600-3512-1-git-send-email-zhangqing@rock-chips.com>
 <1452508600-3512-3-git-send-email-zhangqing@rock-chips.com>
 <5693ADD1.4000207@gmail.com>
Message-ID: <5694BFE4.1070200@rock-chips.com>

hi Caesar Wang

you need check the datasheet again.
 > The rk3368 datasheet:
 > PMU_PMU_BUS_IDLE_ST  ----->ack_vio[8]----->ack status
			----->idle_vio[24]----->idle status
 > PMU_PMU_BUS_IDLE_REQ ---->idle_req_vio[8]----->idle req

On 01/11/2016 05:27 AM, Caesar Wang wrote:
> Hi zhangqin,
>
>  From my datasheet "Rockchip RK3368 TRM V2.0.pdf"
> Maybe i'm missing something.
>
>
> ? 2016?01?11? 18:36, zhangqing ??:
>> This driver is modified to support RK3368 SoC.
>>
>> Signed-off-by: zhangqing <zhangqing at rock-chips.com>
>> ---
>>   drivers/soc/rockchip/pm_domains.c | 33
>> +++++++++++++++++++++++++++++++++
>>   1 file changed, 33 insertions(+)
>>
>> diff --git a/drivers/soc/rockchip/pm_domains.c
>> b/drivers/soc/rockchip/pm_domains.c
>> index 534c589..6cdffb1 100644
>> --- a/drivers/soc/rockchip/pm_domains.c
>> +++ b/drivers/soc/rockchip/pm_domains.c
>> @@ -18,6 +18,7 @@
>>   #include <linux/regmap.h>
>>   #include <linux/mfd/syscon.h>
>>   #include <dt-bindings/power/rk3288-power.h>
>> +#include <dt-bindings/power/rk3368-power.h>
>>   struct rockchip_domain_info {
>>       int pwr_mask;
>> @@ -75,6 +76,9 @@ struct rockchip_pmu {
>>   #define DOMAIN_RK3288(pwr, status, req)        \
>>       DOMAIN(pwr, status, req, req, (req) + 16)
>> +#define DOMAIN_RK3368(pwr, status, req)        \
>> +    DOMAIN(pwr, status, req, (req) + 16, req)
>> +
>
> You should  remove it, that's seem same with the rk3288.
>
> The rk3368 datasheet:
> PMU_PMU_BUS_IDLE_ST  ----->idle_vio[24]----->ack
> PMU_PMU_BUS_IDLE_REQ ---->idle_req_vio[8]----->idle
>
>>   static bool rockchip_pmu_domain_is_idle(struct rockchip_pm_domain *pd)
>>   {
>>       struct rockchip_pmu *pmu = pd->pmu;
>> @@ -444,6 +448,14 @@ static const struct rockchip_domain_info
>> rk3288_pm_domains[] = {
>>       [RK3288_PD_GPU]        = DOMAIN_RK3288(9, 9, 2),
>>   };
>> +static const struct rockchip_domain_info rk3368_pm_domains[] = {
>> +    [RK3368_PD_PERI]    = DOMAIN_RK3368(13, 12, 6),
>> +    [RK3368_PD_VIO]        = DOMAIN_RK3368(15, 14, 8),
>> +    [RK3368_PD_VIDEO]    = DOMAIN_RK3368(14, 13, 7),
>> +    [RK3368_PD_GPU_0]    = DOMAIN_RK3368(16, 15, 2),
>> +    [RK3368_PD_GPU_1]    = DOMAIN_RK3368(17, 16, 2),
>> +};
>> +
>>   static const struct rockchip_pmu_info rk3288_pmu = {
>>       .pwr_offset = 0x08,
>>       .status_offset = 0x0c,
>> @@ -461,11 +473,32 @@ static const struct rockchip_pmu_info rk3288_pmu
>> = {
>>       .domain_info = rk3288_pm_domains,
>>   };
>> +static const struct rockchip_pmu_info rk3368_pmu = {
>> +    .pwr_offset = 0x0c,
>> +    .status_offset = 0x10,
>> +    .req_offset = 0x3c,
>> +    .idle_offset = 0x40,
>> +    .ack_offset = 0x40,
>> +
>> +    .core_pwrcnt_offset = 0x48,
>> +    .gpu_pwrcnt_offset = 0x50,
>> +
>> +    .core_power_transition_time = 24,
>> +    .gpu_power_transition_time = 24,
>> +
>> +    .num_domains = ARRAY_SIZE(rk3368_pm_domains),
>> +    .domain_info = rk3368_pm_domains,
>> +};
>> +
>>   static const struct of_device_id rockchip_pm_domain_dt_match[] = {
>>       {
>>           .compatible = "rockchip,rk3288-power-controller",
>>           .data = (void *)&rk3288_pmu,
>>       },
>> +    {
>> +        .compatible = "rockchip,rk3368-power-controller",
>> +        .data = (void *)&rk3368_pmu,
>> +    },
>>       { /* sentinel */ },
>>   };
>>
>> --
>> Thanks,
>> Caesar
>
>
>



From caesar.upstream at gmail.com  Mon Jan 11 17:57:31 2016
From: caesar.upstream at gmail.com (Caesar Wang)
Date: Tue, 12 Jan 2016 09:57:31 +0800
Subject: [PATCH v3 2/3] soc: rockchip: power-domain: Modify power domain
 driver for rk3368
In-Reply-To: <5694BFE4.1070200@rock-chips.com>
References: <1452508600-3512-1-git-send-email-zhangqing@rock-chips.com>
 <1452508600-3512-3-git-send-email-zhangqing@rock-chips.com>
 <5693ADD1.4000207@gmail.com> <5694BFE4.1070200@rock-chips.com>
Message-ID: <56945D8B.9080302@gmail.com>



? 2016?01?12? 16:57, zhangqing ??:
> hi Caesar Wang
>
> you need check the datasheet again.
> > The rk3368 datasheet:
> > PMU_PMU_BUS_IDLE_ST  ----->ack_vio[8]----->ack status
>             ----->idle_vio[24]----->idle status
> > PMU_PMU_BUS_IDLE_REQ ---->idle_req_vio[8]----->idle req

You are right from the new datasheet.


>
> On 01/11/2016 05:27 AM, Caesar Wang wrote:
>> Hi zhangqin,
>>
>>  From my datasheet "Rockchip RK3368 TRM V2.0.pdf"
>> Maybe i'm missing something.
>>
>>
>> ? 2016?01?11? 18:36, zhangqing ??:
>>> This driver is modified to support RK3368 SoC.
>>>
>>> Signed-off-by: zhangqing <zhangqing at rock-chips.com>
>>> ---
>>>   drivers/soc/rockchip/pm_domains.c | 33
>>> +++++++++++++++++++++++++++++++++
>>>   1 file changed, 33 insertions(+)
>>>
>>> diff --git a/drivers/soc/rockchip/pm_domains.c
>>> b/drivers/soc/rockchip/pm_domains.c
>>> index 534c589..6cdffb1 100644
>>> --- a/drivers/soc/rockchip/pm_domains.c
>>> +++ b/drivers/soc/rockchip/pm_domains.c
>>> @@ -18,6 +18,7 @@
>>>   #include <linux/regmap.h>
>>>   #include <linux/mfd/syscon.h>
>>>   #include <dt-bindings/power/rk3288-power.h>
>>> +#include <dt-bindings/power/rk3368-power.h>
>>>   struct rockchip_domain_info {
>>>       int pwr_mask;
>>> @@ -75,6 +76,9 @@ struct rockchip_pmu {
>>>   #define DOMAIN_RK3288(pwr, status, req)        \
>>>       DOMAIN(pwr, status, req, req, (req) + 16)
>>> +#define DOMAIN_RK3368(pwr, status, req)        \
>>> +    DOMAIN(pwr, status, req, (req) + 16, req)
>>> +
>>
>> You should  remove it, that's seem same with the rk3288.
>>
>> The rk3368 datasheet:
>> PMU_PMU_BUS_IDLE_ST  ----->idle_vio[24]----->ack
>> PMU_PMU_BUS_IDLE_REQ ---->idle_req_vio[8]----->idle
>>
>>>   static bool rockchip_pmu_domain_is_idle(struct rockchip_pm_domain 
>>> *pd)
>>>   {
>>>       struct rockchip_pmu *pmu = pd->pmu;
>>> @@ -444,6 +448,14 @@ static const struct rockchip_domain_info
>>> rk3288_pm_domains[] = {
>>>       [RK3288_PD_GPU]        = DOMAIN_RK3288(9, 9, 2),
>>>   };
>>> +static const struct rockchip_domain_info rk3368_pm_domains[] = {
>>> +    [RK3368_PD_PERI]    = DOMAIN_RK3368(13, 12, 6),
>>> +    [RK3368_PD_VIO]        = DOMAIN_RK3368(15, 14, 8),
>>> +    [RK3368_PD_VIDEO]    = DOMAIN_RK3368(14, 13, 7),
>>> +    [RK3368_PD_GPU_0]    = DOMAIN_RK3368(16, 15, 2),
>>> +    [RK3368_PD_GPU_1]    = DOMAIN_RK3368(17, 16, 2),
>>> +};
>>> +
>>>   static const struct rockchip_pmu_info rk3288_pmu = {
>>>       .pwr_offset = 0x08,
>>>       .status_offset = 0x0c,
>>> @@ -461,11 +473,32 @@ static const struct rockchip_pmu_info rk3288_pmu
>>> = {
>>>       .domain_info = rk3288_pm_domains,
>>>   };
>>> +static const struct rockchip_pmu_info rk3368_pmu = {
>>> +    .pwr_offset = 0x0c,
>>> +    .status_offset = 0x10,
>>> +    .req_offset = 0x3c,
>>> +    .idle_offset = 0x40,
>>> +    .ack_offset = 0x40,
>>> +
>>> +    .core_pwrcnt_offset = 0x48,
>>> +    .gpu_pwrcnt_offset = 0x50,
>>> +
>>> +    .core_power_transition_time = 24,
>>> +    .gpu_power_transition_time = 24,
>>> +
>>> +    .num_domains = ARRAY_SIZE(rk3368_pm_domains),
>>> +    .domain_info = rk3368_pm_domains,
>>> +};
>>> +
>>>   static const struct of_device_id rockchip_pm_domain_dt_match[] = {
>>>       {
>>>           .compatible = "rockchip,rk3288-power-controller",
>>>           .data = (void *)&rk3288_pmu,
>>>       },
>>> +    {
>>> +        .compatible = "rockchip,rk3368-power-controller",
>>> +        .data = (void *)&rk3368_pmu,
>>> +    },
>>>       { /* sentinel */ },
>>>   };
>>>
>>> -- 
>>> Thanks,
>>> Caesar
>>
>>
>>
>
>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip


-- 
Thanks,
Caesar



From caesar.upstream at gmail.com  Mon Jan 11 18:00:31 2016
From: caesar.upstream at gmail.com (Caesar Wang)
Date: Tue, 12 Jan 2016 10:00:31 +0800
Subject: [PATCH v3 2/3] soc: rockchip: power-domain: Modify power domain
 driver for rk3368
In-Reply-To: <1452508600-3512-3-git-send-email-zhangqing@rock-chips.com>
References: <1452508600-3512-1-git-send-email-zhangqing@rock-chips.com>
 <1452508600-3512-3-git-send-email-zhangqing@rock-chips.com>
Message-ID: <56945E3F.9070404@gmail.com>

Hi

? 2016?01?11? 18:36, zhangqing ??:
> This driver is modified to support RK3368 SoC.
>
> Signed-off-by: zhangqing <zhangqing at rock-chips.com>

Reviewed-by: Caesar Wang <wxt at rock-chips.com>

> ---
>   drivers/soc/rockchip/pm_domains.c | 33 +++++++++++++++++++++++++++++++++
>   1 file changed, 33 insertions(+)
>
> diff --git a/drivers/soc/rockchip/pm_domains.c b/drivers/soc/rockchip/pm_domains.c
> index 534c589..6cdffb1 100644
> --- a/drivers/soc/rockchip/pm_domains.c
> +++ b/drivers/soc/rockchip/pm_domains.c
> @@ -18,6 +18,7 @@
>   #include <linux/regmap.h>
>   #include <linux/mfd/syscon.h>
>   #include <dt-bindings/power/rk3288-power.h>
> +#include <dt-bindings/power/rk3368-power.h>
>   
>   struct rockchip_domain_info {
>   	int pwr_mask;
> @@ -75,6 +76,9 @@ struct rockchip_pmu {
>   #define DOMAIN_RK3288(pwr, status, req)		\
>   	DOMAIN(pwr, status, req, req, (req) + 16)
>   
> +#define DOMAIN_RK3368(pwr, status, req)		\
> +	DOMAIN(pwr, status, req, (req) + 16, req)
> +
>   static bool rockchip_pmu_domain_is_idle(struct rockchip_pm_domain *pd)
>   {
>   	struct rockchip_pmu *pmu = pd->pmu;
> @@ -444,6 +448,14 @@ static const struct rockchip_domain_info rk3288_pm_domains[] = {
>   	[RK3288_PD_GPU]		= DOMAIN_RK3288(9, 9, 2),
>   };
>   
> +static const struct rockchip_domain_info rk3368_pm_domains[] = {
> +	[RK3368_PD_PERI]	= DOMAIN_RK3368(13, 12, 6),
> +	[RK3368_PD_VIO]		= DOMAIN_RK3368(15, 14, 8),
> +	[RK3368_PD_VIDEO]	= DOMAIN_RK3368(14, 13, 7),
> +	[RK3368_PD_GPU_0]	= DOMAIN_RK3368(16, 15, 2),
> +	[RK3368_PD_GPU_1]	= DOMAIN_RK3368(17, 16, 2),
> +};
> +
>   static const struct rockchip_pmu_info rk3288_pmu = {
>   	.pwr_offset = 0x08,
>   	.status_offset = 0x0c,
> @@ -461,11 +473,32 @@ static const struct rockchip_pmu_info rk3288_pmu = {
>   	.domain_info = rk3288_pm_domains,
>   };
>   
> +static const struct rockchip_pmu_info rk3368_pmu = {
> +	.pwr_offset = 0x0c,
> +	.status_offset = 0x10,
> +	.req_offset = 0x3c,
> +	.idle_offset = 0x40,
> +	.ack_offset = 0x40,
> +
> +	.core_pwrcnt_offset = 0x48,
> +	.gpu_pwrcnt_offset = 0x50,
> +
> +	.core_power_transition_time = 24,
> +	.gpu_power_transition_time = 24,
> +
> +	.num_domains = ARRAY_SIZE(rk3368_pm_domains),
> +	.domain_info = rk3368_pm_domains,
> +};
> +
>   static const struct of_device_id rockchip_pm_domain_dt_match[] = {
>   	{
>   		.compatible = "rockchip,rk3288-power-controller",
>   		.data = (void *)&rk3288_pmu,
>   	},
> +	{
> +		.compatible = "rockchip,rk3368-power-controller",
> +		.data = (void *)&rk3368_pmu,
> +	},
>   	{ /* sentinel */ },
>   };
>   


-- 
Thanks,
Caesar



From caesar.upstream at gmail.com  Mon Jan 11 18:03:13 2016
From: caesar.upstream at gmail.com (Caesar Wang)
Date: Tue, 12 Jan 2016 10:03:13 +0800
Subject: [PATCH v3 1/3] dt-bindings: add power-domain header for RK3368
 SoCs
In-Reply-To: <1452508600-3512-2-git-send-email-zhangqing@rock-chips.com>
References: <1452508600-3512-1-git-send-email-zhangqing@rock-chips.com>
 <1452508600-3512-2-git-send-email-zhangqing@rock-chips.com>
Message-ID: <56945EE1.6020602@gmail.com>

Hi

? 2016?01?11? 18:36, zhangqing ??:
> According to a description from TRM, add all the power domains.
>
> Signed-off-by: zhangqing <zhangqing at rock-chips.com>

Reviewed-by: Caesar Wang <wxt at rock-chips.com>

> ---
>   include/dt-bindings/power/rk3368-power.h | 28 ++++++++++++++++++++++++++++
>   1 file changed, 28 insertions(+)
>   create mode 100644 include/dt-bindings/power/rk3368-power.h
>
> diff --git a/include/dt-bindings/power/rk3368-power.h b/include/dt-bindings/power/rk3368-power.h
> new file mode 100644
> index 0000000..93633d5
> --- /dev/null
> +++ b/include/dt-bindings/power/rk3368-power.h
> @@ -0,0 +1,28 @@
> +#ifndef __DT_BINDINGS_POWER_RK3368_POWER_H__
> +#define __DT_BINDINGS_POWER_RK3368_POWER_H__
> +
> +/* VD_CORE */
> +#define RK3368_PD_A53_L0	0
> +#define RK3368_PD_A53_L1	1
> +#define RK3368_PD_A53_L2	2
> +#define RK3368_PD_A53_L3	3
> +#define RK3368_PD_SCU_L		4
> +#define RK3368_PD_A53_B0	5
> +#define RK3368_PD_A53_B1	6
> +#define RK3368_PD_A53_B2	7
> +#define RK3368_PD_A53_B3	8
> +#define RK3368_PD_SCU_B		9
> +
> +/* VD_LOGIC */
> +#define RK3368_PD_BUS		10
> +#define RK3368_PD_PERI		11
> +#define RK3368_PD_VIO		12
> +#define RK3368_PD_ALIVE		13
> +#define RK3368_PD_VIDEO		14
> +#define RK3368_PD_GPU_0		15
> +#define RK3368_PD_GPU_1		16
> +
> +/* VD_PMU */
> +#define RK3368_PD_PMU		17
> +
> +#endif


-- 
Thanks,
Caesar



From jeffy.chen at rock-chips.com  Mon Jan 11 18:36:04 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Tue, 12 Jan 2016 10:36:04 +0800
Subject: [PATCH v1 5/6] rockchip: kylin: Enable boot with android boot
 image
In-Reply-To: <CAPnjgZ2_wS1N8uO23+M0RJZovVFx7Wc_uMqOC37nGGBQN7NZZA@mail.gmail.com>
References: <1451893823-20309-1-git-send-email-jeffy.chen@rock-chips.com>
 <1451893823-20309-6-git-send-email-jeffy.chen@rock-chips.com>
 <CAPnjgZ1G=5=gqy6yGV1NZ13Zv0BACRYMPpdAoFAi+sv2TPXknw@mail.gmail.com>
 <568F94B8.60101@rock-chips.com>
 <CAPnjgZ2_wS1N8uO23+M0RJZovVFx7Wc_uMqOC37nGGBQN7NZZA@mail.gmail.com>
Message-ID: <56946694.40203@rock-chips.com>

Hi Simon,

On 2016-1-12 0:58, Simon Glass wrote:
> Hi Jeffy,
>
> On 8 January 2016 at 03:51, Jeffy Chen <jeffy.chen at rock-chips.com> wrote:
>> Hi Simon,
>>
>> On 2016-1-8 11:34, Simon Glass wrote:
>>> Hi Jeffy,
>>>
>>> On 4 January 2016 at 00:50, Jeffy Chen <jeffy.chen at rock-chips.com> wrote:
>>>> The android kernel is using appended dtb by default, and store
>>>> ramdisk right after kernel & dtb.
>>>> So we needs to relocate ramdisk, and use atags to pass params.
>>>>
>>>> Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
>>>> ---
>>>>
>>>>    include/configs/kylin_rk3036.h | 23 +++++++++++++++++++++++
>>>>    1 file changed, 23 insertions(+)
>>> Acked-by: Simon Glass <sjg at chromium.org>
>>>
>>> What kernel version are you using?
>> It's kernel v4.1 for android project:
>> git fetch
>> https://android.googlesource.com/platform/hardware/bsp/kernel/common/v4.1
>> refs/changes/03/188603/5 && git checkout FETCH_HEAD :)
> OK - just wondered why it needs ATAGs (CONFIG_SETUP_MEMORY_TAGS) - are
> there things that device tree does not support?
I saw our guys used to detect ddr params in the loader, and pass memory 
areas and reserved areas to kernel...
and kernel's device tree would only define a common memory node in the 
dtsi, which is not match the actually memory.
>   used to
>
>>>
>>> Regards,
>>> Simon
>>>
>> On 2016-1-8 11:34, Simon Glass wrote:
>>> Hi Jeffy,
>>>
>>> On 4 January 2016 at 00:50, Jeffy Chen <jeffy.chen at rock-chips.com> wrote:
>>>> The android kernel is using appended dtb by default, and store
>>>> ramdisk right after kernel & dtb.
>>>> So we needs to relocate ramdisk, and use atags to pass params.
>>>>
>>>> Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
>>>> ---
>>>>
>>>>    include/configs/kylin_rk3036.h | 23 +++++++++++++++++++++++
>>>>    1 file changed, 23 insertions(+)
>>> Acked-by: Simon Glass <sjg at chromium.org>
>>>
>>> What kernel version are you using?
>>>
>>> Regards,
>>> Simon
>>>
> Regards,
> Simon
>



From zhangqing at rock-chips.com  Tue Jan 12 03:05:56 2016
From: zhangqing at rock-chips.com (zhangqing)
Date: Tue, 12 Jan 2016 03:05:56 -0800
Subject: [PATCH] regulator: fan53555: fill set_suspend_enable/disable callback
Message-ID: <1452596756-16617-1-git-send-email-zhangqing@rock-chips.com>

Setting the set_suspend_enable/disable callback to support
enable and disable the dcdc when system is suspend.

Signed-off-by: zhangqing <zhangqing at rock-chips.com>
---
 drivers/regulator/fan53555.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c
index 4940e82..2cb5cc3 100644
--- a/drivers/regulator/fan53555.c
+++ b/drivers/regulator/fan53555.c
@@ -114,6 +114,22 @@ static int fan53555_set_suspend_voltage(struct regulator_dev *rdev, int uV)
 	return 0;
 }
 
+static int fan53555_set_suspend_enable(struct regulator_dev *rdev)
+{
+	struct fan53555_device_info *di = rdev_get_drvdata(rdev);
+
+	return regmap_update_bits(di->regmap, di->sleep_reg,
+				  VSEL_BUCK_EN, VSEL_BUCK_EN);
+}
+
+static int fan53555_set_suspend_disable(struct regulator_dev *rdev)
+{
+	struct fan53555_device_info *di = rdev_get_drvdata(rdev);
+
+	return regmap_update_bits(di->regmap, di->sleep_reg,
+				  VSEL_BUCK_EN, 0);
+}
+
 static int fan53555_set_mode(struct regulator_dev *rdev, unsigned int mode)
 {
 	struct fan53555_device_info *di = rdev_get_drvdata(rdev);
@@ -192,6 +208,8 @@ static struct regulator_ops fan53555_regulator_ops = {
 	.set_mode = fan53555_set_mode,
 	.get_mode = fan53555_get_mode,
 	.set_ramp_delay = fan53555_set_ramp,
+	.set_suspend_enable = fan53555_set_suspend_enable,
+	.set_suspend_disable = fan53555_set_suspend_disable,
 };
 
 static int fan53555_voltages_setup_fairchild(struct fan53555_device_info *di)
-- 
1.9.1




From k.kozlowski at samsung.com  Mon Jan 11 21:03:36 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Tue, 12 Jan 2016 14:03:36 +0900
Subject: [PATCH] regulator: fan53555: fill set_suspend_enable/disable
 callback
In-Reply-To: <1452596756-16617-1-git-send-email-zhangqing@rock-chips.com>
References: <1452596756-16617-1-git-send-email-zhangqing@rock-chips.com>
Message-ID: <CAJKOXPcJGFV+jWE4XK_Jt5k=vCcGTOf-7QjR6oW=mFQH8yL7qw@mail.gmail.com>

2016-01-12 20:05 GMT+09:00 zhangqing <zhangqing at rock-chips.com>:
> Setting the set_suspend_enable/disable callback to support
> enable and disable the dcdc when system is suspend.
>
> Signed-off-by: zhangqing <zhangqing at rock-chips.com>
> ---
>  drivers/regulator/fan53555.c | 18 ++++++++++++++++++
>  1 file changed, 18 insertions(+)
>
> diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c
> index 4940e82..2cb5cc3 100644
> --- a/drivers/regulator/fan53555.c
> +++ b/drivers/regulator/fan53555.c
> @@ -114,6 +114,22 @@ static int fan53555_set_suspend_voltage(struct regulator_dev *rdev, int uV)
>         return 0;
>  }
>
> +static int fan53555_set_suspend_enable(struct regulator_dev *rdev)
> +{
> +       struct fan53555_device_info *di = rdev_get_drvdata(rdev);
> +
> +       return regmap_update_bits(di->regmap, di->sleep_reg,
> +                                 VSEL_BUCK_EN, VSEL_BUCK_EN);

You are just writing the enable_mask (BTW, just use the enable_mask,
not the value itself in such case) instead of enabling the suspend
mode. In the disable callback you are just disabling the regulator.

What do you want to achieve with these callbacks?

Best regards,
Krzysztof

> +}
> +
> +static int fan53555_set_suspend_disable(struct regulator_dev *rdev)
> +{
> +       struct fan53555_device_info *di = rdev_get_drvdata(rdev);
> +
> +       return regmap_update_bits(di->regmap, di->sleep_reg,
> +                                 VSEL_BUCK_EN, 0);
> +}
> +
>  static int fan53555_set_mode(struct regulator_dev *rdev, unsigned int mode)
>  {
>         struct fan53555_device_info *di = rdev_get_drvdata(rdev);
> @@ -192,6 +208,8 @@ static struct regulator_ops fan53555_regulator_ops = {
>         .set_mode = fan53555_set_mode,
>         .get_mode = fan53555_get_mode,
>         .set_ramp_delay = fan53555_set_ramp,
> +       .set_suspend_enable = fan53555_set_suspend_enable,
> +       .set_suspend_disable = fan53555_set_suspend_disable,
>  };
>
>  static int fan53555_voltages_setup_fairchild(struct fan53555_device_info *di)
> --
> 1.9.1
>
>


From zhangqing at rock-chips.com  Tue Jan 12 06:28:31 2016
From: zhangqing at rock-chips.com (zhangqing)
Date: Tue, 12 Jan 2016 06:28:31 -0800
Subject: [PATCH] regulator: fan53555: fill set_suspend_enable/disable
 callback
In-Reply-To: <CAJKOXPcJGFV+jWE4XK_Jt5k=vCcGTOf-7QjR6oW=mFQH8yL7qw@mail.gmail.com>
References: <1452596756-16617-1-git-send-email-zhangqing@rock-chips.com>
 <CAJKOXPcJGFV+jWE4XK_Jt5k=vCcGTOf-7QjR6oW=mFQH8yL7qw@mail.gmail.com>
Message-ID: <56950D8F.8080902@rock-chips.com>



On 01/11/2016 09:03 PM, Krzysztof Kozlowski wrote:
> 2016-01-12 20:05 GMT+09:00 zhangqing <zhangqing at rock-chips.com>:
>> Setting the set_suspend_enable/disable callback to support
>> enable and disable the dcdc when system is suspend.
>>
>> Signed-off-by: zhangqing <zhangqing at rock-chips.com>
>> ---
>>   drivers/regulator/fan53555.c | 18 ++++++++++++++++++
>>   1 file changed, 18 insertions(+)
>>
>> diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c
>> index 4940e82..2cb5cc3 100644
>> --- a/drivers/regulator/fan53555.c
>> +++ b/drivers/regulator/fan53555.c
>> @@ -114,6 +114,22 @@ static int fan53555_set_suspend_voltage(struct regulator_dev *rdev, int uV)
>>          return 0;
>>   }
>>
>> +static int fan53555_set_suspend_enable(struct regulator_dev *rdev)
>> +{
>> +       struct fan53555_device_info *di = rdev_get_drvdata(rdev);
>> +
>> +       return regmap_update_bits(di->regmap, di->sleep_reg,
>> +                                 VSEL_BUCK_EN, VSEL_BUCK_EN);
>
> You are just writing the enable_mask (BTW, just use the enable_mask,
> not the value itself in such case) instead of enabling the suspend
> mode. In the disable callback you are just disabling the regulator.
>
> What do you want to achieve with these callbacks?
        return regmap_update_bits(di->regmap, di->sleep_reg,
                                  VSEL_BUCK_EN, VSEL_BUCK_EN);
This callback is setting sleep_reg, setting this dcdc output is enable 
or disable when system enter sleep.
In our system this dcdc need disabled when sleep. But the current 
software not support.

>
> Best regards,
> Krzysztof
>
>> +}
>> +
>> +static int fan53555_set_suspend_disable(struct regulator_dev *rdev)
>> +{
>> +       struct fan53555_device_info *di = rdev_get_drvdata(rdev);
>> +
>> +       return regmap_update_bits(di->regmap, di->sleep_reg,
>> +                                 VSEL_BUCK_EN, 0);
>> +}
>> +
>>   static int fan53555_set_mode(struct regulator_dev *rdev, unsigned int mode)
>>   {
>>          struct fan53555_device_info *di = rdev_get_drvdata(rdev);
>> @@ -192,6 +208,8 @@ static struct regulator_ops fan53555_regulator_ops = {
>>          .set_mode = fan53555_set_mode,
>>          .get_mode = fan53555_get_mode,
>>          .set_ramp_delay = fan53555_set_ramp,
>> +       .set_suspend_enable = fan53555_set_suspend_enable,
>> +       .set_suspend_disable = fan53555_set_suspend_disable,
>>   };
>>
>>   static int fan53555_voltages_setup_fairchild(struct fan53555_device_info *di)
>> --
>> 1.9.1
>>
>>
>
>
>



From k.kozlowski at samsung.com  Mon Jan 11 23:09:34 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Tue, 12 Jan 2016 16:09:34 +0900
Subject: [PATCH] regulator: fan53555: fill set_suspend_enable/disable
 callback
In-Reply-To: <56950D8F.8080902@rock-chips.com>
References: <1452596756-16617-1-git-send-email-zhangqing@rock-chips.com>
 <CAJKOXPcJGFV+jWE4XK_Jt5k=vCcGTOf-7QjR6oW=mFQH8yL7qw@mail.gmail.com>
 <56950D8F.8080902@rock-chips.com>
Message-ID: <5694A6AE.8040404@samsung.com>

On 12.01.2016 23:28, zhangqing wrote:
> 
> 
> On 01/11/2016 09:03 PM, Krzysztof Kozlowski wrote:
>> 2016-01-12 20:05 GMT+09:00 zhangqing <zhangqing at rock-chips.com>:
>>> Setting the set_suspend_enable/disable callback to support
>>> enable and disable the dcdc when system is suspend.
>>>
>>> Signed-off-by: zhangqing <zhangqing at rock-chips.com>
>>> ---
>>>   drivers/regulator/fan53555.c | 18 ++++++++++++++++++
>>>   1 file changed, 18 insertions(+)
>>>
>>> diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c
>>> index 4940e82..2cb5cc3 100644
>>> --- a/drivers/regulator/fan53555.c
>>> +++ b/drivers/regulator/fan53555.c
>>> @@ -114,6 +114,22 @@ static int fan53555_set_suspend_voltage(struct
>>> regulator_dev *rdev, int uV)
>>>          return 0;
>>>   }
>>>
>>> +static int fan53555_set_suspend_enable(struct regulator_dev *rdev)
>>> +{
>>> +       struct fan53555_device_info *di = rdev_get_drvdata(rdev);
>>> +
>>> +       return regmap_update_bits(di->regmap, di->sleep_reg,
>>> +                                 VSEL_BUCK_EN, VSEL_BUCK_EN);
>>
>> You are just writing the enable_mask (BTW, just use the enable_mask,
>> not the value itself in such case) instead of enabling the suspend
>> mode. In the disable callback you are just disabling the regulator.
>>
>> What do you want to achieve with these callbacks?
>        return regmap_update_bits(di->regmap, di->sleep_reg,
>                                  VSEL_BUCK_EN, VSEL_BUCK_EN);
> This callback is setting sleep_reg, setting this dcdc output is enable
> or disable when system enter sleep.
> In our system this dcdc need disabled when sleep. But the current
> software not support.
> 

I missed that the register is different - sleep_reg instead of
enable_reg. It makes sense and as this is separate register then usage
of desc->enable_mask is up to you, I think.

Reviewed-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>

Best regards,
Krzysztof



From andy.yan at rock-chips.com  Tue Jan 12 03:27:09 2016
From: andy.yan at rock-chips.com (Andy Yan)
Date: Tue, 12 Jan 2016 19:27:09 +0800
Subject: [PATCH v2 0/4] add reboot mode driver
Message-ID: <1452598029-8222-1-git-send-email-andy.yan@rock-chips.com>


This driver parse the reboot commands like "reboot loader"
and "reboot recovery" to get a boot mode described in the
device tree , then call the corresponding  write interface
to store the boot mode magic value in some persistent storage
like special register or ram , which can be read by the bootloader
after system reboot.

This is commonly used on Android based devices, which in order
to reboot the device into fastboot or recovery mode.

Before this patch , I have try some hack on[0], and then found
John Stultz also doing the same work[1].

As John is busy these days, I go on with this work.

[0]https://patchwork.kernel.org/patch/7647751/
[1]https://patchwork.kernel.org/patch/7802391/

Changes in v2:
- move to dir drivers/power/reset/
- make syscon-reboot-mode a generic driver
- make this DT node as a subnode of PMU/PMUGRF

Changes in v1:
- fix the embarrassed compile warning
- correct the maskrom magic number
- check for the normal reboot
- correct the maskrom magic number
- use macro defined in rockchip_boot-mode.h for reboot-mode DT node

Andy Yan (4):
  dt-bindings: power: reset: add document for reboot-mode driver
  power: reset: add reboot mode driver
  ARM: dts: rockchip: add syscon-reboot-mode node
  ARM64: dts: rockchip: add syscon-reboot-mode DT node

 .../bindings/power/reset/reboot-mode.txt           |  41 +++++++++
 .../bindings/power/reset/syscon-reboot-mode.txt    |  52 +++++++++++
 arch/arm/boot/dts/rk3288.dtsi                      |  31 +++++++
 arch/arm/boot/dts/rk3xxx.dtsi                      |  33 ++++++-
 arch/arm64/boot/dts/rockchip/rk3368.dtsi           |  33 ++++++-
 drivers/power/reset/Kconfig                        |  16 ++++
 drivers/power/reset/Makefile                       |   2 +
 drivers/power/reset/reboot-mode.c                  | 100 +++++++++++++++++++++
 drivers/power/reset/reboot-mode.h                  |   6 ++
 drivers/power/reset/syscon-reboot-mode.c           |  62 +++++++++++++
 include/dt-bindings/soc/rockchip_boot-mode.h       |  30 +++++++
 11 files changed, 404 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/power/reset/reboot-mode.txt
 create mode 100644 Documentation/devicetree/bindings/power/reset/syscon-reboot-mode.txt
 create mode 100644 drivers/power/reset/reboot-mode.c
 create mode 100644 drivers/power/reset/reboot-mode.h
 create mode 100644 drivers/power/reset/syscon-reboot-mode.c
 create mode 100644 include/dt-bindings/soc/rockchip_boot-mode.h

-- 
1.9.1





From andy.yan at rock-chips.com  Tue Jan 12 03:29:49 2016
From: andy.yan at rock-chips.com (Andy Yan)
Date: Tue, 12 Jan 2016 19:29:49 +0800
Subject: [PATCH v2 1/4] dt-bindings: power: reset: add document for
 reboot-mode driver
In-Reply-To: <1452598029-8222-1-git-send-email-andy.yan@rock-chips.com>
References: <1452598029-8222-1-git-send-email-andy.yan@rock-chips.com>
Message-ID: <1452598189-8272-1-git-send-email-andy.yan@rock-chips.com>

add device tree binding document for reboot-mode driver

Signed-off-by: Andy Yan <andy.yan at rock-chips.com>

---

Changes in v2: None
Changes in v1: None

 .../bindings/power/reset/reboot-mode.txt           | 41 +++++++++++++++++
 .../bindings/power/reset/syscon-reboot-mode.txt    | 52 ++++++++++++++++++++++
 2 files changed, 93 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/power/reset/reboot-mode.txt
 create mode 100644 Documentation/devicetree/bindings/power/reset/syscon-reboot-mode.txt

diff --git a/Documentation/devicetree/bindings/power/reset/reboot-mode.txt b/Documentation/devicetree/bindings/power/reset/reboot-mode.txt
new file mode 100644
index 0000000..81d9f66
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/reset/reboot-mode.txt
@@ -0,0 +1,41 @@
+Generic reboot mode core map driver
+
+This driver get reboot mode arguments and call the write
+interface to stores the magic value in special register
+or ram . Then the bootloader can read it and take different
+action according to the argument stored.
+
+Required properties:
+- compatible: only support "syscon-reboot-mode" now.
+
+Each mode is represented as a sub-node of reboot_mode:
+
+Subnode required properties:
+- linux,mode: reboot mode command,such as "loader", "recovery", "fastboot".
+- loader,magic: magic number for the mode, this is vendor specific.
+
+Example:
+	reboot_mode {
+		compatible = "syscon-reboot-mode";
+		offset = <0x40>;
+
+		loader {
+			linux,mode = "loader";
+			loader,magic = <BOOT_LOADER>;
+		};
+
+		maskrom {
+			linux,mode = "maskrom";
+			loader,magic = <BOOT_MASKROM>;
+		};
+
+		recovery {
+			linux,mode = "recovery";
+			loader,magic = <BOOT_RECOVERY>;
+		};
+
+		fastboot {
+			linux,mode = "fastboot";
+			loader,magic = <BOOT_FASTBOOT>;
+		};
+        };
diff --git a/Documentation/devicetree/bindings/power/reset/syscon-reboot-mode.txt b/Documentation/devicetree/bindings/power/reset/syscon-reboot-mode.txt
new file mode 100644
index 0000000..6bce7dd
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/reset/syscon-reboot-mode.txt
@@ -0,0 +1,52 @@
+SYSCON reboot mode driver
+
+This driver get reboot mode magic value form reboot-mode driver
+and stores it in a SYSCON mapped register. Then the bootloader
+can read it and take different action according to the magic
+value stored.
+
+This DT node should be represented as a sub-node of a "syscon", "simple-mfd"
+node.
+
+Required properties:
+- compatible: should be "syscon-reboot-mode"
+- offset: offset in the register map for the storage register (in bytes)
+
+The rest of the properties should follow the generic reboot-mode discription
+found in reboot-mode.txt
+
+Example:
+	pmu: pmu at 20004000 {
+		compatible = "rockchip,rk3066-pmu", "syscon", "simple-mfd";
+		reg = <0x20004000 0x100>;
+
+		reboot-mode {
+			compatible = "syscon-reboot-mode";
+			offset = <0x40>;
+
+			normal {
+				linux,mode = "normal";
+				loader,magic = <BOOT_NORMAL>;
+			};
+
+			loader {
+				linux,mode = "loader";
+				loader,magic = <BOOT_LOADER>;
+			};
+
+			maskrom {
+				linux,mode = "maskrom";
+				loader,magic = <BOOT_MASKROM>;
+			};
+
+			recovery {
+				linux,mode = "recovery";
+				loader,magic = <BOOT_RECOVERY>;
+			};
+
+			fastboot {
+				linux,mode = "fastboot";
+				loader,magic = <BOOT_FASTBOOT>;
+			};
+		};
+	};
-- 
1.9.1




From andy.yan at rock-chips.com  Tue Jan 12 03:31:59 2016
From: andy.yan at rock-chips.com (Andy Yan)
Date: Tue, 12 Jan 2016 19:31:59 +0800
Subject: [PATCH v2 2/4] power: reset: add reboot mode driver
In-Reply-To: <1452598029-8222-1-git-send-email-andy.yan@rock-chips.com>
References: <1452598029-8222-1-git-send-email-andy.yan@rock-chips.com>
Message-ID: <1452598319-8324-1-git-send-email-andy.yan@rock-chips.com>

This driver parse the reboot commands like "reboot loader"
and "reboot recovery" to get a boot mode described in the
device tree , then call the write interfae to store the boot
mode in some persistent storage  like special register or ram,
which can be read by the bootloader after system reboot, then
the bootloader can take different action according to the mode
stored.

This is commonly used on Android based devices, which in order
to reboot the device into fastboot or recovery mode.

Signed-off-by: Andy Yan <andy.yan at rock-chips.com>

---

Changes in v2:
- move to dir drivers/power/reset/
- make syscon-reboot-mode a generic driver

Changes in v1:
- fix the embarrassed compile warning
- correct the maskrom magic number
- check for the normal reboot

 drivers/power/reset/Kconfig              |  16 +++++
 drivers/power/reset/Makefile             |   2 +
 drivers/power/reset/reboot-mode.c        | 100 +++++++++++++++++++++++++++++++
 drivers/power/reset/reboot-mode.h        |   6 ++
 drivers/power/reset/syscon-reboot-mode.c |  62 +++++++++++++++++++
 5 files changed, 186 insertions(+)
 create mode 100644 drivers/power/reset/reboot-mode.c
 create mode 100644 drivers/power/reset/reboot-mode.h
 create mode 100644 drivers/power/reset/syscon-reboot-mode.c

diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
index 1131cf7..2e6d873 100644
--- a/drivers/power/reset/Kconfig
+++ b/drivers/power/reset/Kconfig
@@ -173,5 +173,21 @@ config POWER_RESET_ZX
 	help
 	  Reboot support for ZTE SoCs.
 
+config REBOOT_MODE
+	tristate
+	depends on OF
+	help
+	 This driver will help to pass the system reboot mode
+	 to bootloader
+
+config SYSCON_REBOOT_MODE
+	bool "Generic SYSCON regmap reboot mode driver"
+	select REBOOT_MODE
+	help
+	 Say y here will enable reboot mode driver. This will
+	 get reboot mode arguments and store it in SYSCON mapped
+	 register, then the bootloader can read it to take different
+	 action according to the mode.
+
 endif
 
diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile
index 096fa67..a63865b 100644
--- a/drivers/power/reset/Makefile
+++ b/drivers/power/reset/Makefile
@@ -20,3 +20,5 @@ obj-$(CONFIG_POWER_RESET_SYSCON) += syscon-reboot.o
 obj-$(CONFIG_POWER_RESET_SYSCON_POWEROFF) += syscon-poweroff.o
 obj-$(CONFIG_POWER_RESET_RMOBILE) += rmobile-reset.o
 obj-$(CONFIG_POWER_RESET_ZX) += zx-reboot.o
+obj-$(CONFIG_REBOOT_MODE) += reboot-mode.o
+obj-$(CONFIG_SYSCON_REBOOT_MODE) += syscon-reboot-mode.o
diff --git a/drivers/power/reset/reboot-mode.c b/drivers/power/reset/reboot-mode.c
new file mode 100644
index 0000000..92b7b4d
--- /dev/null
+++ b/drivers/power/reset/reboot-mode.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/reboot.h>
+#include "reboot-mode.h"
+
+struct mode_info {
+	const char *mode;
+	int magic;
+	struct list_head list;
+};
+
+struct reboot_mode_driver {
+	struct list_head head;
+	int (*write)(int magic);
+	struct notifier_block reboot_notifier;
+};
+
+static int get_reboot_mode_magic(struct reboot_mode_driver *reboot,
+				 const char *cmd)
+{
+	const char *normal = "normal";
+	int magic = 0;
+	struct mode_info *info;
+
+	if (!cmd)
+		cmd = normal;
+
+	list_for_each_entry(info, &reboot->head, list) {
+		if (!strcmp(info->mode, cmd)) {
+			magic = info->magic;
+			break;
+		}
+	}
+
+	return magic;
+}
+
+static int reboot_mode_notify(struct notifier_block *this,
+			      unsigned long mode, void *cmd)
+{
+	struct reboot_mode_driver *reboot;
+	int magic;
+
+	reboot = container_of(this, struct reboot_mode_driver, reboot_notifier);
+	magic = get_reboot_mode_magic(reboot, cmd);
+	if (magic)
+		reboot->write(magic);
+
+	return NOTIFY_DONE;
+}
+
+int reboot_mode_register(struct device *dev, int (*write)(int))
+{
+	struct reboot_mode_driver *reboot;
+	struct mode_info *info;
+	struct device_node *child;
+	int ret;
+
+	reboot = devm_kzalloc(dev, sizeof(*reboot), GFP_KERNEL);
+	if (!reboot)
+		return -ENOMEM;
+
+	reboot->write = write;
+	INIT_LIST_HEAD(&reboot->head);
+	for_each_child_of_node(dev->of_node, child) {
+		info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+		if (!info)
+			return -ENOMEM;
+		info->mode = of_get_property(child, "linux,mode", NULL);
+		if (of_property_read_u32(child, "loader,magic", &info->magic)) {
+			dev_err(dev, "reboot mode %s without magic number\n",
+				info->mode);
+			devm_kfree(dev, info);
+			continue;
+		}
+		list_add_tail(&info->list, &reboot->head);
+	}
+	reboot->reboot_notifier.notifier_call = reboot_mode_notify;
+	ret = register_reboot_notifier(&reboot->reboot_notifier);
+	if (ret)
+		dev_err(dev, "can't register reboot notifier\n");
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(reboot_mode_register);
+
+MODULE_AUTHOR("Andy Yan <andy.yan at rock-chips.com");
+MODULE_DESCRIPTION("System reboot mode driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/reset/reboot-mode.h b/drivers/power/reset/reboot-mode.h
new file mode 100644
index 0000000..76646e7
--- /dev/null
+++ b/drivers/power/reset/reboot-mode.h
@@ -0,0 +1,6 @@
+#ifndef __REBOOT_MODE_H__
+#define __REBOOT_MODE_H__
+
+extern int reboot_mode_register(struct device *dev, int (*write)(int));
+
+#endif
diff --git a/drivers/power/reset/syscon-reboot-mode.c b/drivers/power/reset/syscon-reboot-mode.c
new file mode 100644
index 0000000..b9370f3
--- /dev/null
+++ b/drivers/power/reset/syscon-reboot-mode.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include "reboot-mode.h"
+
+static struct regmap *map;
+static u32 offset;
+
+static int syscon_reboot_mode_write(int magic)
+{
+	regmap_write(map, offset, magic);
+
+	return 0;
+}
+
+static int syscon_reboot_mode_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	map = syscon_node_to_regmap(pdev->dev.parent->of_node);
+	if (IS_ERR(map))
+		return PTR_ERR(map);
+	if (of_property_read_u32(pdev->dev.of_node, "offset", &offset))
+		return -EINVAL;
+	ret = reboot_mode_register(&pdev->dev, syscon_reboot_mode_write);
+	if (ret)
+		dev_err(&pdev->dev, "can't register reboot mode\n");
+
+	return ret;
+}
+
+static const struct of_device_id syscon_reboot_mode_of_match[] = {
+	{ .compatible = "syscon-reboot-mode" },
+	{}
+};
+
+static struct platform_driver syscon_reboot_mode_driver = {
+	.probe = syscon_reboot_mode_probe,
+	.driver = {
+		.name = "syscon-reboot-mode",
+		.of_match_table = syscon_reboot_mode_of_match,
+	},
+};
+module_platform_driver(syscon_reboot_mode_driver);
+
+MODULE_AUTHOR("Andy Yan <andy.yan at rock-chips.com");
+MODULE_DESCRIPTION("SYSCON reboot mode driver");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1





From andy.yan at rock-chips.com  Tue Jan 12 03:32:58 2016
From: andy.yan at rock-chips.com (Andy Yan)
Date: Tue, 12 Jan 2016 19:32:58 +0800
Subject: [PATCH v2 3/4] ARM: dts: rockchip: add syscon-reboot-mode node
In-Reply-To: <1452598029-8222-1-git-send-email-andy.yan@rock-chips.com>
References: <1452598029-8222-1-git-send-email-andy.yan@rock-chips.com>
Message-ID: <1452598378-8371-1-git-send-email-andy.yan@rock-chips.com>

Rockchip platform use a SYSCON mapped register store
the reboot mode magic value for bootloader to use when
system reboot. So add syscon-reboot-mode driver DT node
for rk3xxx,rk3288 platform

Signed-off-by: Andy Yan <andy.yan at rock-chips.com>

---

Changes in v2:
- make this node as a subnode of PMU

Changes in v1:
- correct the maskrom magic number
- use macro defined in rockchip_boot-mode.h for reboot-mode DT node

 arch/arm/boot/dts/rk3288.dtsi                | 31 ++++++++++++++++++++++++++
 arch/arm/boot/dts/rk3xxx.dtsi                | 33 +++++++++++++++++++++++++++-
 include/dt-bindings/soc/rockchip_boot-mode.h | 30 +++++++++++++++++++++++++
 3 files changed, 93 insertions(+), 1 deletion(-)
 create mode 100644 include/dt-bindings/soc/rockchip_boot-mode.h

diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
index 04ea209..4e49fb7 100644
--- a/arch/arm/boot/dts/rk3288.dtsi
+++ b/arch/arm/boot/dts/rk3288.dtsi
@@ -45,6 +45,7 @@
 #include <dt-bindings/clock/rk3288-cru.h>
 #include <dt-bindings/thermal/thermal.h>
 #include <dt-bindings/power/rk3288-power.h>
+#include <dt-bindings/soc/rockchip_boot-mode.h>
 #include "skeleton.dtsi"
 
 / {
@@ -712,6 +713,36 @@
 				clocks = <&cru ACLK_GPU>;
 			};
 		};
+
+		reboot-mode {
+			compatible = "syscon-reboot-mode";
+			offset = <0x94>;
+
+			normal {
+				linux,mode = "normal";
+				loader,magic = <BOOT_NORMAL>;
+			};
+
+			loader {
+				linux,mode = "loader";
+				loader,magic = <BOOT_LOADER>;
+			};
+
+			maskrom {
+				linux,mode = "maskrom";
+				loader,magic = <BOOT_MASKROM>;
+			};
+
+			recovery {
+				linux,mode = "recovery";
+				loader,magic = <BOOT_RECOVERY>;
+			};
+
+			fastboot {
+				linux,mode = "fastboot";
+				loader,magic = <BOOT_FASTBOOT>;
+			};
+		};
 	};
 
 	sgrf: syscon at ff740000 {
diff --git a/arch/arm/boot/dts/rk3xxx.dtsi b/arch/arm/boot/dts/rk3xxx.dtsi
index 4497d28..58af546 100644
--- a/arch/arm/boot/dts/rk3xxx.dtsi
+++ b/arch/arm/boot/dts/rk3xxx.dtsi
@@ -43,6 +43,7 @@
 
 #include <dt-bindings/interrupt-controller/irq.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/soc/rockchip_boot-mode.h>
 #include "skeleton.dtsi"
 
 / {
@@ -242,8 +243,38 @@
 	};
 
 	pmu: pmu at 20004000 {
-		compatible = "rockchip,rk3066-pmu", "syscon";
+		compatible = "rockchip,rk3066-pmu", "syscon", "simple-mfd";
 		reg = <0x20004000 0x100>;
+
+		reboot-mode {
+			compatible = "syscon-reboot-mode";
+			offset = <0x40>;
+
+			normal {
+				linux,mode = "normal";
+				loader,magic = <BOOT_NORMAL>;
+			};
+
+			loader {
+				linux,mode = "loader";
+				loader,magic = <BOOT_LOADER>;
+			};
+
+			maskrom {
+				linux,mode = "maskrom";
+				loader,magic = <BOOT_MASKROM>;
+			};
+
+			recovery {
+				linux,mode = "recovery";
+				loader,magic = <BOOT_RECOVERY>;
+			};
+
+			fastboot {
+				linux,mode = "fastboot";
+				loader,magic = <BOOT_FASTBOOT>;
+			};
+		};
 	};
 
 	grf: grf at 20008000 {
diff --git a/include/dt-bindings/soc/rockchip_boot-mode.h b/include/dt-bindings/soc/rockchip_boot-mode.h
new file mode 100644
index 0000000..eedf113
--- /dev/null
+++ b/include/dt-bindings/soc/rockchip_boot-mode.h
@@ -0,0 +1,30 @@
+#ifndef __ROCKCHIP_BOOT_MODE_H
+#define __ROCKCHIP_BOOT_MODE_H
+
+/*high 24 bits is tag, low 8 bits is type*/
+#define	REBOOT_FLAG		0x5242C300
+/* normal boot */
+#define	BOOT_NORMAL		(REBOOT_FLAG + 0)
+/* enter loader rockusb mode */
+#define	BOOT_LOADER		(REBOOT_FLAG + 1)
+/* enter maskrom rockusb mode */
+#define	BOOT_MASKROM		(0xEF08A53C)
+/* enter recovery */
+#define	BOOT_RECOVERY		(REBOOT_FLAG + 3)
+/* do not enter recover */
+#define	BOOT_NORECOVER		(REBOOT_FLAG + 4)
+/* boot second OS*/
+#define	BOOT_SECONDOS		(REBOOT_FLAG + 5)
+/* enter recover and wipe data. */
+#define	BOOT_WIPEDATA		(REBOOT_FLAG + 6)
+/* enter recover and wipe all data. */
+#define	BOOT_WIPEALL		(REBOOT_FLAG + 7)
+/* check firmware img with backup part*/
+#define	BOOT_CHECKIMG		(REBOOT_FLAG + 8)
+ /* enter fast boot mode */
+#define	BOOT_FASTBOOT		(REBOOT_FLAG + 9)
+#define	BOOT_SECUREBOOT_DISABLE	(REBOOT_FLAG + 10)
+/* enter charge mode */
+#define	BOOT_CHARGING		(REBOOT_FLAG + 11)
+
+#endif
-- 
1.9.1





From andy.yan at rock-chips.com  Tue Jan 12 03:33:47 2016
From: andy.yan at rock-chips.com (Andy Yan)
Date: Tue, 12 Jan 2016 19:33:47 +0800
Subject: [PATCH v2 4/4] ARM64: dts: rockchip: add syscon-reboot-mode DT node
In-Reply-To: <1452598029-8222-1-git-send-email-andy.yan@rock-chips.com>
References: <1452598029-8222-1-git-send-email-andy.yan@rock-chips.com>
Message-ID: <1452598427-8418-1-git-send-email-andy.yan@rock-chips.com>

Add syscon-reboot-mode driver DT node for rk3368 platform

Signed-off-by: Andy Yan <andy.yan at rock-chips.com>

---

Changes in v2:
- make this node as a subnode of pmugrf

Changes in v1: None

 arch/arm64/boot/dts/rockchip/rk3368.dtsi | 33 +++++++++++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/rockchip/rk3368.dtsi b/arch/arm64/boot/dts/rockchip/rk3368.dtsi
index cc093a4..20997a2 100644
--- a/arch/arm64/boot/dts/rockchip/rk3368.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3368.dtsi
@@ -45,6 +45,7 @@
 #include <dt-bindings/interrupt-controller/irq.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 #include <dt-bindings/pinctrl/rockchip.h>
+#include <dt-bindings/soc/rockchip_boot-mode.h>
 
 / {
 	compatible = "rockchip,rk3368";
@@ -485,8 +486,38 @@
 	};
 
 	pmugrf: syscon at ff738000 {
-		compatible = "rockchip,rk3368-pmugrf", "syscon";
+		compatible = "rockchip,rk3368-pmugrf", "syscon", "simple-mfd";
 		reg = <0x0 0xff738000 0x0 0x1000>;
+
+		reboot-mode {
+			compatible = "syscon-reboot-mode";
+			offset = <0x200>;
+
+			normal {
+				linux,mode = "normal";
+				loader,magic = <BOOT_NORMAL>;
+			};
+
+			loader {
+				linux,mode = "loader";
+				loader,magic = <BOOT_LOADER>;
+			};
+
+			maskrom {
+				linux,mode = "maskrom";
+				loader,magic = <BOOT_MASKROM>;
+			};
+
+			recovery {
+				linux,mode = "recovery";
+				loader,magic = <BOOT_RECOVERY>;
+			};
+
+			fastboot {
+				linux,mode = "fastboot";
+				loader,magic = <BOOT_FASTBOOT>;
+			};
+		};
 	};
 
 	cru: clock-controller at ff760000 {
-- 
1.9.1




From john at metanate.com  Tue Jan 12 10:05:18 2016
From: john at metanate.com (John Keeping)
Date: Tue, 12 Jan 2016 18:05:18 +0000
Subject: [PATCH] drm/rockchip: vop: fix mask when updating interrupts
Message-ID: <1452621918-32239-1-git-send-email-john@metanate.com>

Commit dbb3d94 (drm/rockchip: vop: move interrupt registers into
vop_data) introduced new macros for updating the interrupt control
registers but these always use the mask from the register definition
without refining it for the particular bits that are being changed.

This means that whenever we enable/disable a particular interrupt we end
up disabling all of the others as a side effect.

Signed-off-by: John Keeping <john at metanate.com>
---
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 46c2a8d..fd37054 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -43,8 +43,8 @@
 
 #define REG_SET(x, base, reg, v, mode) \
 		__REG_SET_##mode(x, base + reg.offset, reg.mask, reg.shift, v)
-#define REG_SET_MASK(x, base, reg, v, mode) \
-		__REG_SET_##mode(x, base + reg.offset, reg.mask, reg.shift, v)
+#define REG_SET_MASK(x, base, reg, mask, v, mode) \
+		__REG_SET_##mode(x, base + reg.offset, mask, reg.shift, v)
 
 #define VOP_WIN_SET(x, win, name, v) \
 		REG_SET(x, win->base, win->phy->name, v, RELAXED)
@@ -58,16 +58,18 @@
 #define VOP_INTR_GET(vop, name) \
 		vop_read_reg(vop, 0, &vop->data->ctrl->name)
 
-#define VOP_INTR_SET(vop, name, v) \
-		REG_SET(vop, 0, vop->data->intr->name, v, NORMAL)
+#define VOP_INTR_SET(vop, name, mask, v) \
+		REG_SET_MASK(vop, 0, vop->data->intr->name, mask, v, NORMAL)
 #define VOP_INTR_SET_TYPE(vop, name, type, v) \
 	do { \
-		int i, reg = 0; \
+		int i, reg = 0, mask = 0; \
 		for (i = 0; i < vop->data->intr->nintrs; i++) { \
-			if (vop->data->intr->intrs[i] & type) \
+			if (vop->data->intr->intrs[i] & type) { \
 				reg |= (v) << i; \
+				mask |= 1 << i; \
+			} \
 		} \
-		VOP_INTR_SET(vop, name, reg); \
+		VOP_INTR_SET(vop, name, mask, reg); \
 	} while (0)
 #define VOP_INTR_GET_TYPE(vop, name, type) \
 		vop_get_intr_type(vop, &vop->data->intr->name, type)
-- 
2.7.0.rc3.140.g520a093



From jeffy.chen at rock-chips.com  Tue Jan 12 17:59:14 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Wed, 13 Jan 2016 09:59:14 +0800
Subject: [PATCH v3 0/6] rockchip: kylin: Boot with android boot image
Message-ID: <1452650360-24149-1-git-send-email-jeffy.chen@rock-chips.com>

We are porting android to kylin board now.
This series could let it boot up with android's boot image.

Changes in v3:
Use rockchip's legacy reboot mode definitions.

Changes in v2:
Add comments.

Jeffy Chen (6):
  common/image-fdt.c: Make boot_get_fdt() perform a check for Android
    images
  ARM: bootm: Try to use relocated ramdisk
  rockchip: rk3036: Bind GPIO banks
  rockchip: kylin: Add default gpt partition table
  rockchip: kylin: Enable boot with android boot image
  rockchip: kylin: Check fastboot request

 arch/arm/lib/bootm.c                      | 12 ++++++-
 board/kylin/kylin_rk3036/kylin_rk3036.c   | 46 ++++++++++++++++++++++++++
 common/image-fdt.c                        |  4 +++
 drivers/pinctrl/rockchip/pinctrl_rk3036.c |  8 +++++
 include/configs/kylin_rk3036.h            | 55 +++++++++++++++++++++++++++++++
 5 files changed, 124 insertions(+), 1 deletion(-)

-- 
2.1.4




From jeffy.chen at rock-chips.com  Tue Jan 12 17:59:17 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Wed, 13 Jan 2016 09:59:17 +0800
Subject: [PATCH v3 3/6] rockchip: rk3036: Bind GPIO banks
In-Reply-To: <1452650360-24149-1-git-send-email-jeffy.chen@rock-chips.com>
References: <1452650360-24149-1-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <1452650360-24149-4-git-send-email-jeffy.chen@rock-chips.com>

Call dm_scan_fdt_node() in rk3036 pinctrl uclass binding.

Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
Acked-by: Simon Glass <sjg at chromium.org>
---

Changes in v3: None
Changes in v2: None

 drivers/pinctrl/rockchip/pinctrl_rk3036.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/pinctrl/rockchip/pinctrl_rk3036.c b/drivers/pinctrl/rockchip/pinctrl_rk3036.c
index 581b096..1f78bf8 100644
--- a/drivers/pinctrl/rockchip/pinctrl_rk3036.c
+++ b/drivers/pinctrl/rockchip/pinctrl_rk3036.c
@@ -15,6 +15,7 @@
 #include <asm/arch/hardware.h>
 #include <asm/arch/periph.h>
 #include <dm/pinctrl.h>
+#include <dm/root.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -252,6 +253,12 @@ static struct pinctrl_ops rk3036_pinctrl_ops = {
 	.get_periph_id	= rk3036_pinctrl_get_periph_id,
 };
 
+static int rk3036_pinctrl_bind(struct udevice *dev)
+{
+	/* scan child GPIO banks */
+	return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false);
+}
+
 static int rk3036_pinctrl_probe(struct udevice *dev)
 {
 	struct rk3036_pinctrl_priv *priv = dev_get_priv(dev);
@@ -272,5 +279,6 @@ U_BOOT_DRIVER(pinctrl_rk3036) = {
 	.of_match	= rk3036_pinctrl_ids,
 	.priv_auto_alloc_size = sizeof(struct rk3036_pinctrl_priv),
 	.ops		= &rk3036_pinctrl_ops,
+	.bind		= rk3036_pinctrl_bind,
 	.probe		= rk3036_pinctrl_probe,
 };
-- 
2.1.4




From jeffy.chen at rock-chips.com  Tue Jan 12 17:59:15 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Wed, 13 Jan 2016 09:59:15 +0800
Subject: [PATCH v3 1/6] common/image-fdt.c: Make boot_get_fdt() perform a
 check for Android images
In-Reply-To: <1452650360-24149-1-git-send-email-jeffy.chen@rock-chips.com>
References: <1452650360-24149-1-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <1452650360-24149-2-git-send-email-jeffy.chen@rock-chips.com>

Android images don't have a fdt.

Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
Acked-by: Simon Glass <sjg at chromium.org>
---

Changes in v3: None
Changes in v2: None

 common/image-fdt.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/common/image-fdt.c b/common/image-fdt.c
index 5e4e5bd..41aaa0d 100644
--- a/common/image-fdt.c
+++ b/common/image-fdt.c
@@ -379,6 +379,10 @@ int boot_get_fdt(int flag, int argc, char * const argv[], uint8_t arch,
 				       (long)fdt_addr);
 			}
 			break;
+#ifdef CONFIG_ANDROID_BOOT_IMAGE
+		case IMAGE_FORMAT_ANDROID:
+			goto no_fdt;
+#endif
 		default:
 			puts("ERROR: Did not find a cmdline Flattened Device Tree\n");
 			goto no_fdt;
-- 
2.1.4




From jeffy.chen at rock-chips.com  Tue Jan 12 17:59:16 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Wed, 13 Jan 2016 09:59:16 +0800
Subject: [PATCH v3 2/6] ARM: bootm: Try to use relocated ramdisk
In-Reply-To: <1452650360-24149-1-git-send-email-jeffy.chen@rock-chips.com>
References: <1452650360-24149-1-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <1452650360-24149-3-git-send-email-jeffy.chen@rock-chips.com>

After boot_ramdisk_high(), ramdisk would be relocated to
initrd_start & initrd_end, so use them instead of rd_start & rd_end.

Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
Acked-by: Simon Glass <sjg at chromium.org>

---

Changes in v3: None
Changes in v2:
Add comments.

 arch/arm/lib/bootm.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c
index a477cae..0838d89 100644
--- a/arch/arm/lib/bootm.c
+++ b/arch/arm/lib/bootm.c
@@ -225,7 +225,17 @@ static void boot_prep_linux(bootm_headers_t *images)
 		if (BOOTM_ENABLE_MEMORY_TAGS)
 			setup_memory_tags(gd->bd);
 		if (BOOTM_ENABLE_INITRD_TAG) {
-			if (images->rd_start && images->rd_end) {
+			/*
+			 * In boot_ramdisk_high(), it may relocate ramdisk to
+			 * a specified location. And set images->initrd_start &
+			 * images->initrd_end to relocated ramdisk's start/end
+			 * addresses. So use them instead of images->rd_start &
+			 * images->rd_end when possible.
+			 */
+			if (images->initrd_start && images->initrd_end) {
+				setup_initrd_tag(gd->bd, images->initrd_start,
+						 images->initrd_end);
+			} else if (images->rd_start && images->rd_end) {
 				setup_initrd_tag(gd->bd, images->rd_start,
 						 images->rd_end);
 			}
-- 
2.1.4




From jeffy.chen at rock-chips.com  Tue Jan 12 17:59:18 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Wed, 13 Jan 2016 09:59:18 +0800
Subject: [PATCH v3 4/6] rockchip: kylin: Add default gpt partition table
In-Reply-To: <1452650360-24149-1-git-send-email-jeffy.chen@rock-chips.com>
References: <1452650360-24149-1-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <1452650360-24149-5-git-send-email-jeffy.chen@rock-chips.com>

Add default android gpt partition table for kylin board.

Use "gpt write mmc 0 $partitions" to apply.

Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
Acked-by: Simon Glass <sjg at chromium.org>
---

Changes in v3: None
Changes in v2: None

 include/configs/kylin_rk3036.h | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/include/configs/kylin_rk3036.h b/include/configs/kylin_rk3036.h
index aa07889..b750b26 100644
--- a/include/configs/kylin_rk3036.h
+++ b/include/configs/kylin_rk3036.h
@@ -9,4 +9,33 @@
 
 #include <configs/rk3036_common.h>
 
+#ifndef CONFIG_SPL_BUILD
+
+/* Enable gpt partition table */
+#define CONFIG_CMD_GPT
+#define CONFIG_RANDOM_UUID
+#define CONFIG_EFI_PARTITION
+#define PARTS_DEFAULT \
+	"uuid_disk=${uuid_gpt_disk};" \
+	"name=loader,start=32K,size=4000K,uuid=${uuid_gpt_loader};" \
+	"name=reserved,size=64K,uuid=${uuid_gpt_reserved};" \
+	"name=misc,size=4M,uuid=${uuid_gpt_misc};" \
+	"name=recovery,size=32M,uuid=${uuid_gpt_recovery};" \
+	"name=boot_a,size=32M,uuid=${uuid_gpt_boot_a};" \
+	"name=boot_b,size=32M,uuid=${uuid_gpt_boot_b};" \
+	"name=system_a,size=818M,uuid=${uuid_gpt_system_a};" \
+	"name=system_b,size=818M,uuid=${uuid_gpt_system_b};" \
+	"name=vendor_a,size=50M,uuid=${uuid_gpt_vendor_a};" \
+	"name=vendor_b,size=50M,uuid=${uuid_gpt_vendor_b};" \
+	"name=cache,size=100M,uuid=${uuid_gpt_cache};" \
+	"name=metadata,size=16M,uuid=${uuid_gpt_metadata};" \
+	"name=persist,size=4M,uuid=${uuid_gpt_persist};" \
+	"name=userdata,size=-,uuid=${uuid_gpt_userdata};\0" \
+
+#undef CONFIG_EXTRA_ENV_SETTINGS
+#define CONFIG_EXTRA_ENV_SETTINGS \
+	"partitions=" PARTS_DEFAULT \
+
+#endif
+
 #endif
-- 
2.1.4




From jeffy.chen at rock-chips.com  Tue Jan 12 17:59:19 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Wed, 13 Jan 2016 09:59:19 +0800
Subject: [PATCH v3 5/6] rockchip: kylin: Enable boot with android boot image
In-Reply-To: <1452650360-24149-1-git-send-email-jeffy.chen@rock-chips.com>
References: <1452650360-24149-1-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <1452650360-24149-6-git-send-email-jeffy.chen@rock-chips.com>

The android kernel is using appended dtb by default, and store
ramdisk right after kernel & dtb.
So we needs to relocate ramdisk, and use atags to pass params.

Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
Acked-by: Simon Glass <sjg at chromium.org>
---

Changes in v3: None
Changes in v2: None

 include/configs/kylin_rk3036.h | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/include/configs/kylin_rk3036.h b/include/configs/kylin_rk3036.h
index b750b26..49997ec 100644
--- a/include/configs/kylin_rk3036.h
+++ b/include/configs/kylin_rk3036.h
@@ -35,6 +35,29 @@
 #undef CONFIG_EXTRA_ENV_SETTINGS
 #define CONFIG_EXTRA_ENV_SETTINGS \
 	"partitions=" PARTS_DEFAULT \
+	"mmcdev=0\0" \
+	"mmcpart=5\0" \
+	"loadaddr=" __stringify(CONFIG_SYS_LOAD_ADDR) "\0" \
+
+#define CONFIG_ANDROID_BOOT_IMAGE
+#define CONFIG_SYS_BOOT_RAMDISK_HIGH
+#define CONFIG_SYS_HUSH_PARSER
+
+#undef CONFIG_BOOTCOMMAND
+#define CONFIG_BOOTCOMMAND \
+	"mmc dev ${mmcdev}; if mmc rescan; then " \
+		"part start mmc ${mmcdev} ${mmcpart} boot_start;" \
+		"part size mmc ${mmcdev} ${mmcpart} boot_size;" \
+		"mmc read ${loadaddr} ${boot_start} ${boot_size};" \
+		"bootm start ${loadaddr}; bootm ramdisk;" \
+		"bootm prep; bootm go;" \
+	"fi;" \
+
+/* Enable atags */
+#define CONFIG_SYS_BOOTPARAMS_LEN	(64*1024)
+#define CONFIG_INITRD_TAG
+#define CONFIG_SETUP_MEMORY_TAGS
+#define CONFIG_CMDLINE_TAG
 
 #endif
 
-- 
2.1.4




From jeffy.chen at rock-chips.com  Tue Jan 12 17:59:20 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Wed, 13 Jan 2016 09:59:20 +0800
Subject: [PATCH v3 6/6] rockchip: kylin: Check fastboot request
In-Reply-To: <1452650360-24149-1-git-send-email-jeffy.chen@rock-chips.com>
References: <1452650360-24149-1-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <1452650360-24149-7-git-send-email-jeffy.chen@rock-chips.com>

We will save boot mode flag in grf's os_reg[0], if fastboot
requested or fastboot key pressed, try to enter fastboot mode
at preboot stage.

Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>

---

Changes in v3:
Use rockchip's legacy reboot mode definitions.

Changes in v2: None

 board/kylin/kylin_rk3036/kylin_rk3036.c | 46 +++++++++++++++++++++++++++++++++
 include/configs/kylin_rk3036.h          |  3 +++
 2 files changed, 49 insertions(+)

diff --git a/board/kylin/kylin_rk3036/kylin_rk3036.c b/board/kylin/kylin_rk3036/kylin_rk3036.c
index 40d6b52..4fc31b1 100644
--- a/board/kylin/kylin_rk3036/kylin_rk3036.c
+++ b/board/kylin/kylin_rk3036/kylin_rk3036.c
@@ -8,10 +8,15 @@
 #include <dm.h>
 #include <asm/io.h>
 #include <asm/arch/uart.h>
+#include <asm/arch-rockchip/grf_rk3036.h>
 #include <asm/arch/sdram_rk3036.h>
+#include <asm/gpio.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
+#define GRF_BASE	0x20008000
+static struct rk3036_grf * const grf = (void *)GRF_BASE;
+
 void get_ddr_config(struct rk3036_ddr_config *config)
 {
 	/* K4B4G1646Q config */
@@ -28,6 +33,47 @@ void get_ddr_config(struct rk3036_ddr_config *config)
 	config->bw = 1;
 }
 
+#define FASTBOOT_KEY_GPIO 93
+
+int fastboot_key_pressed(void)
+{
+	gpio_request(FASTBOOT_KEY_GPIO, "fastboot_key");
+	gpio_direction_input(FASTBOOT_KEY_GPIO);
+	return !gpio_get_value(FASTBOOT_KEY_GPIO);
+}
+
+enum rockchip_boot_mode {
+	ROCKCHIP_BOOT_MODE_NORMAL = 0x5242C300,
+	ROCKCHIP_BOOT_MODE_LOADER,
+	ROCKCHIP_BOOT_MODE_MASKROM,
+	ROCKCHIP_BOOT_MODE_RECOVER,
+	ROCKCHIP_BOOT_MODE_NORECOVER,
+	ROCKCHIP_BOOT_MODE_SECONDOS,
+	ROCKCHIP_BOOT_MODE_WIPEDATA,
+	ROCKCHIP_BOOT_MODE_WIPEALL,
+	ROCKCHIP_BOOT_MODE_CHECKIMG,
+	ROCKCHIP_BOOT_MODE_FASTBOOT,
+	ROCKCHIP_BOOT_MODE_SECUREBOOT_DISABLE,
+	ROCKCHIP_BOOT_MODE_CHARGING,
+	ROCKCHIP_BOOT_MODE_MAX,
+};
+
+int board_late_init(void)
+{
+	int boot_mode = readl(&grf->os_reg[4]);
+
+	/* Clear boot mode */
+	writel(0, &grf->os_reg[4]);
+
+	if (boot_mode == ROCKCHIP_BOOT_MODE_FASTBOOT ||
+	    fastboot_key_pressed()) {
+		printf("enter fastboot!\n");
+		setenv("preboot", "setenv preboot; fastboot usb0");
+	}
+
+	return 0;
+}
+
 int board_init(void)
 {
 	return 0;
diff --git a/include/configs/kylin_rk3036.h b/include/configs/kylin_rk3036.h
index 49997ec..424e81b 100644
--- a/include/configs/kylin_rk3036.h
+++ b/include/configs/kylin_rk3036.h
@@ -39,6 +39,9 @@
 	"mmcpart=5\0" \
 	"loadaddr=" __stringify(CONFIG_SYS_LOAD_ADDR) "\0" \
 
+#define CONFIG_BOARD_LATE_INIT
+#define CONFIG_PREBOOT
+
 #define CONFIG_ANDROID_BOOT_IMAGE
 #define CONFIG_SYS_BOOT_RAMDISK_HIGH
 #define CONFIG_SYS_HUSH_PARSER
-- 
2.1.4




From wxt at rock-chips.com  Tue Jan 12 18:08:49 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Wed, 13 Jan 2016 10:08:49 +0800
Subject: [PATCH 0/1] Add the reboot mode dts for RK3036 SoCs
Message-ID: <1452650930-22883-1-git-send-email-wxt@rock-chips.com>


This patch is depend on the Andy' patches.
http://www.spinics.net/lists/kernel/msg2160362.html

This series patches are verified by the following URL:
https://github.com/Caesar-github/rockchip/commits/kylin-develop4.4

The base version is kernel 4.4 release version.



Caesar Wang (1):
  ARM: dts: rockchip: add reboot-mode node for rk3036 SoCs

 arch/arm/boot/dts/rk3036.dtsi | 33 ++++++++++++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

-- 
1.9.1



From wxt at rock-chips.com  Tue Jan 12 18:08:50 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Wed, 13 Jan 2016 10:08:50 +0800
Subject: [PATCH] ARM: dts: rockchip: add reboot-mode node for rk3036 SoCs
In-Reply-To: <1452650930-22883-1-git-send-email-wxt@rock-chips.com>
References: <1452650930-22883-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1452650930-22883-2-git-send-email-wxt@rock-chips.com>

Add reboot mode driver DT node for rk3036 platform.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

 arch/arm/boot/dts/rk3036.dtsi | 33 ++++++++++++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
index ccc69ab..2e583f5 100644
--- a/arch/arm/boot/dts/rk3036.dtsi
+++ b/arch/arm/boot/dts/rk3036.dtsi
@@ -43,6 +43,7 @@
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 #include <dt-bindings/pinctrl/rockchip.h>
 #include <dt-bindings/clock/rk3036-cru.h>
+#include <dt-bindings/soc/rockchip_boot-mode.h>
 #include "skeleton.dtsi"
 
 / {
@@ -307,8 +308,38 @@
 	};
 
 	grf: syscon at 20008000 {
-		compatible = "rockchip,rk3036-grf", "syscon";
+		compatible = "rockchip,rk3036-grf", "syscon", "simple-mfd";
 		reg = <0x20008000 0x1000>;
+
+		reboot-mode {
+			compatible = "syscon-reboot-mode";
+			offset = <0x1d8>;
+
+			normal {
+				linux,mode = "normal";
+				loader,magic = <BOOT_NORMAL>;
+			};
+
+			loader {
+				linux,mode = "loader";
+				loader,magic = <BOOT_LOADER>;
+			};
+
+			maskrom {
+				linux,mode = "maskrom";
+				loader,magic = <BOOT_MASKROM>;
+			};
+
+			recovery {
+				linux,mode = "recovery";
+				loader,magic = <BOOT_RECOVERY>;
+			};
+
+			fastboot {
+				linux,mode = "fastboot";
+				loader,magic = <BOOT_FASTBOOT>;
+			};
+		};
 	};
 
 	acodec: acodec-ana at 20030000 {
-- 
1.9.1



From caesar.upstream at gmail.com  Tue Jan 12 18:17:35 2016
From: caesar.upstream at gmail.com (Caesar Wang)
Date: Wed, 13 Jan 2016 10:17:35 +0800
Subject: [PATCH v2 0/4] add reboot mode driver
In-Reply-To: <1452598029-8222-1-git-send-email-andy.yan@rock-chips.com>
References: <1452598029-8222-1-git-send-email-andy.yan@rock-chips.com>
Message-ID: <5695B3BF.4030505@gmail.com>

Hi ,

? 2016?01?12? 19:27, Andy Yan ??:
> This driver parse the reboot commands like "reboot loader"
> and "reboot recovery" to get a boot mode described in the
> device tree , then call the corresponding  write interface
> to store the boot mode magic value in some persistent storage
> like special register or ram , which can be read by the bootloader
> after system reboot.
>
> This is commonly used on Android based devices, which in order
> to reboot the device into fastboot or recovery mode.
>
> Before this patch , I have try some hack on[0], and then found
> John Stultz also doing the same work[1].
>
> As John is busy these days, I go on with this work.
>
> [0]https://patchwork.kernel.org/patch/7647751/
> [1]https://patchwork.kernel.org/patch/7802391/
>
> Changes in v2:
> - move to dir drivers/power/reset/
> - make syscon-reboot-mode a generic driver
> - make this DT node as a subnode of PMU/PMUGRF
>
> Changes in v1:
> - fix the embarrassed compile warning
> - correct the maskrom magic number
> - check for the normal reboot
> - correct the maskrom magic number
> - use macro defined in rockchip_boot-mode.h for reboot-mode DT node
>
> Andy Yan (4):
>    dt-bindings: power: reset: add document for reboot-mode driver
>    power: reset: add reboot mode driver
>    ARM: dts: rockchip: add syscon-reboot-mode node
>    ARM64: dts: rockchip: add syscon-reboot-mode DT node

This series patches are verified by my github based on kernel 4.4 release.
https://github.com/Caesar-github/rockchip/commits/kylin-develop4.4

reboot loader, reboot recovery, reboot fastboot..... these are okay for 
rk3288, rk3368. rk3036 Socs on my hand board.
(Note: the loader need that jeffy send patches to support uboot on 
upstream,  that's also okay if you are using the RK loader from Rockchip)

I just send the patch to support the rk3036 dts. 
<https://patchwork.kernel.org/patch/8021581/>

So feel free add my test tags:

Tested-by: Caesar Wang <wxt at rock-chips.com>

>
>   .../bindings/power/reset/reboot-mode.txt           |  41 +++++++++
>   .../bindings/power/reset/syscon-reboot-mode.txt    |  52 +++++++++++
>   arch/arm/boot/dts/rk3288.dtsi                      |  31 +++++++
>   arch/arm/boot/dts/rk3xxx.dtsi                      |  33 ++++++-
>   arch/arm64/boot/dts/rockchip/rk3368.dtsi           |  33 ++++++-
>   drivers/power/reset/Kconfig                        |  16 ++++
>   drivers/power/reset/Makefile                       |   2 +
>   drivers/power/reset/reboot-mode.c                  | 100 +++++++++++++++++++++
>   drivers/power/reset/reboot-mode.h                  |   6 ++
>   drivers/power/reset/syscon-reboot-mode.c           |  62 +++++++++++++
>   include/dt-bindings/soc/rockchip_boot-mode.h       |  30 +++++++
>   11 files changed, 404 insertions(+), 2 deletions(-)
>   create mode 100644 Documentation/devicetree/bindings/power/reset/reboot-mode.txt
>   create mode 100644 Documentation/devicetree/bindings/power/reset/syscon-reboot-mode.txt
>   create mode 100644 drivers/power/reset/reboot-mode.c
>   create mode 100644 drivers/power/reset/reboot-mode.h
>   create mode 100644 drivers/power/reset/syscon-reboot-mode.c
>   create mode 100644 include/dt-bindings/soc/rockchip_boot-mode.h
>


-- 
Thanks,
Caesar



From jeffy.chen at rock-chips.com  Tue Jan 12 18:40:57 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Wed, 13 Jan 2016 10:40:57 +0800
Subject: [PATCH v2 6/6] rockchip: kylin: Check fastboot request
In-Reply-To: <1452249212-25183-7-git-send-email-jeffy.chen@rock-chips.com>
References: <1452249212-25183-1-git-send-email-jeffy.chen@rock-chips.com>
 <1452249212-25183-7-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <5695B939.8060202@rock-chips.com>

Hi Simon,

Just know our guys are upstreaming our reboot mode support with our 
legacy reboot mode definitions(in our legacy loader & kernel):
https://lkml.org/lkml/2015/12/21/161

and that serial seems close to get accepted, so i think maybe i should 
use that definitions too...

On 2016-1-8 18:33, Jeffy Chen wrote:
> We will save boot mode flag in grf's os_reg[0], if fastboot
> requested or fastboot key pressed, try to enter fastboot mode
> at preboot stage.
>
> Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
> Acked-by: Simon Glass <sjg at chromium.org>
> ---
>
> Changes in v2: None
>
>   board/kylin/kylin_rk3036/kylin_rk3036.c | 32 ++++++++++++++++++++++++++++++++
>   include/configs/kylin_rk3036.h          |  3 +++
>   2 files changed, 35 insertions(+)
>
> diff --git a/board/kylin/kylin_rk3036/kylin_rk3036.c b/board/kylin/kylin_rk3036/kylin_rk3036.c
> index 40d6b52..df2c123 100644
> --- a/board/kylin/kylin_rk3036/kylin_rk3036.c
> +++ b/board/kylin/kylin_rk3036/kylin_rk3036.c
> @@ -8,10 +8,15 @@
>   #include <dm.h>
>   #include <asm/io.h>
>   #include <asm/arch/uart.h>
> +#include <asm/arch-rockchip/grf_rk3036.h>
>   #include <asm/arch/sdram_rk3036.h>
> +#include <asm/gpio.h>
>   
>   DECLARE_GLOBAL_DATA_PTR;
>   
> +#define GRF_BASE	0x20008000
> +static struct rk3036_grf * const grf = (void *)GRF_BASE;
> +
>   void get_ddr_config(struct rk3036_ddr_config *config)
>   {
>   	/* K4B4G1646Q config */
> @@ -28,6 +33,33 @@ void get_ddr_config(struct rk3036_ddr_config *config)
>   	config->bw = 1;
>   }
>   
> +#define FASTBOOT_KEY_GPIO 93
> +#define ROCKCHIP_BOOT_MODE_NORMAL	0
> +#define ROCKCHIP_BOOT_MODE_FASTBOOT	1
> +
> +int fastboot_key_pressed(void)
> +{
> +	gpio_request(FASTBOOT_KEY_GPIO, "fastboot_key");
> +	gpio_direction_input(FASTBOOT_KEY_GPIO);
> +	return !gpio_get_value(FASTBOOT_KEY_GPIO);
> +}
> +
> +int board_late_init(void)
> +{
> +	int boot_mode = readl(&grf->os_reg[0]);
> +
> +	/* Clear boot mode */
> +	writel(ROCKCHIP_BOOT_MODE_NORMAL, &grf->os_reg[0]);
> +
> +	if (boot_mode == ROCKCHIP_BOOT_MODE_FASTBOOT ||
> +	    fastboot_key_pressed()) {
> +		printf("enter fastboot!\n");
> +		setenv("preboot", "setenv preboot; fastboot usb0");
> +	}
> +
> +	return 0;
> +}
> +
>   int board_init(void)
>   {
>   	return 0;
> diff --git a/include/configs/kylin_rk3036.h b/include/configs/kylin_rk3036.h
> index 49997ec..424e81b 100644
> --- a/include/configs/kylin_rk3036.h
> +++ b/include/configs/kylin_rk3036.h
> @@ -39,6 +39,9 @@
>   	"mmcpart=5\0" \
>   	"loadaddr=" __stringify(CONFIG_SYS_LOAD_ADDR) "\0" \
>   
> +#define CONFIG_BOARD_LATE_INIT
> +#define CONFIG_PREBOOT
> +
>   #define CONFIG_ANDROID_BOOT_IMAGE
>   #define CONFIG_SYS_BOOT_RAMDISK_HIGH
>   #define CONFIG_SYS_HUSH_PARSER



From caesar.upstream at gmail.com  Wed Jan 13 00:10:46 2016
From: caesar.upstream at gmail.com (Caesar Wang)
Date: Wed, 13 Jan 2016 16:10:46 +0800
Subject: [PATCH v2 04/12] clk: rockchip: rk3036: enable the
 CLK_IGNORE_UNUSED flag for sclk_i2s_out
In-Reply-To: <20757797.djuaztrzGE@phil>
References: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
 <290434099.8XFZ9qmNRz@phil> <568F80BA.6060209@gmail.com>
 <20757797.djuaztrzGE@phil>
Message-ID: <56960686.7030903@gmail.com>

Heiko,

? 2016?01?08? 17:46, Heiko Stuebner ??:
> Hi Caesar,
>
> Am Freitag, 8. Januar 2016, 17:26:18 schrieb Caesar Wang:
>> ? 2016?01?07? 18:05, Heiko Stuebner ??:
>>> Am Donnerstag, 7. Januar 2016, 16:25:47 schrieb Caesar Wang:
>>>> SCLk_I2S_OUT is the noc bus clock for i2s module, this clock is used by
>>>> extra codecs.
>>>>
>>>> Due to it shouldn't belong to any driver, but we need it enabled,
>>>> so just mark it as the CLK_IGNORE_UNUSED flag.
>>> What makes you think it shouldn't belong to any driver?
>>>
>>> In most schematics I have, i2s_clkout is going to some clock-input of
>>> the
>>> audio codec - probably the MCLK input on your rt5616. And while the new
>>> rt5616 driver does not seem to do clock handling, it should just handle
>>> the clock using the normal APIs.
>> You are right,  that's seem has to find the root cause.
>> I'm wonder that the codec/i2s is working in kernel v4.1 patch without it.
>>
>> In general,  the MLCK will be handled in i2s driver if the cpu side is
>> master.
> looking at the rt5616 datasheet, that would be BCLK1 instead I think. This
> one does differentiate between being master/slave.
>
> The master/sysclk (mclk) is also handled in other drivers in all cases.
> (da7213.c, da7219.c, max98090.c, max98095.c, probably more)

Saw the patch:
https://patchwork.kernel.org/patch/5427131/

Add the follow that's working.

--- a/arch/arm/boot/dts/rk3036.dtsi
+++ b/arch/arm/boot/dts/rk3036.dtsi
@@ -288,8 +288,8 @@
                 interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;
                 #address-cells = <1>;
                 #size-cells = <0>;
-               clock-names = "i2s_clk", "i2s_hclk";
-               clocks = <&cru SCLK_I2S>, <&cru HCLK_I2S>;
+               clock-names = "i2s_clk", "i2s_hclk", "i2s_clk_out";
+               clocks = <&cru SCLK_I2S>, <&cru HCLK_I2S>, <&cru 
SCLK_I2S_OUT>;

Okay, I believe Mark likes that works in codec driver.

>
>
> Heiko
>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip


-- 
Thanks,
Caesar



From mark.yao at rock-chips.com  Wed Jan 13 00:12:08 2016
From: mark.yao at rock-chips.com (Mark yao)
Date: Wed, 13 Jan 2016 16:12:08 +0800
Subject: [PATCH] drm/rockchip: vop: fix mask when updating interrupts
In-Reply-To: <1452621918-32239-1-git-send-email-john@metanate.com>
References: <1452621918-32239-1-git-send-email-john@metanate.com>
Message-ID: <569606D8.8060309@rock-chips.com>

On 2016?01?13? 02:05, John Keeping wrote:
> Commit dbb3d94 (drm/rockchip: vop: move interrupt registers into
> vop_data) introduced new macros for updating the interrupt control
> registers but these always use the mask from the register definition
> without refining it for the particular bits that are being changed.
>
> This means that whenever we enable/disable a particular interrupt we end
> up disabling all of the others as a side effect.
>
> Signed-off-by: John Keeping <john at metanate.com>
> ---
>   drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 16 +++++++++-------
>   1 file changed, 9 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> index 46c2a8d..fd37054 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> @@ -43,8 +43,8 @@
>   
>   #define REG_SET(x, base, reg, v, mode) \
>   		__REG_SET_##mode(x, base + reg.offset, reg.mask, reg.shift, v)
> -#define REG_SET_MASK(x, base, reg, v, mode) \
> -		__REG_SET_##mode(x, base + reg.offset, reg.mask, reg.shift, v)
> +#define REG_SET_MASK(x, base, reg, mask, v, mode) \
> +		__REG_SET_##mode(x, base + reg.offset, mask, reg.shift, v)
>   
>   #define VOP_WIN_SET(x, win, name, v) \
>   		REG_SET(x, win->base, win->phy->name, v, RELAXED)
> @@ -58,16 +58,18 @@
>   #define VOP_INTR_GET(vop, name) \
>   		vop_read_reg(vop, 0, &vop->data->ctrl->name)
>   
> -#define VOP_INTR_SET(vop, name, v) \
> -		REG_SET(vop, 0, vop->data->intr->name, v, NORMAL)
> +#define VOP_INTR_SET(vop, name, mask, v) \
> +		REG_SET_MASK(vop, 0, vop->data->intr->name, mask, v, NORMAL)
>   #define VOP_INTR_SET_TYPE(vop, name, type, v) \
>   	do { \
> -		int i, reg = 0; \
> +		int i, reg = 0, mask = 0; \
>   		for (i = 0; i < vop->data->intr->nintrs; i++) { \
> -			if (vop->data->intr->intrs[i] & type) \
> +			if (vop->data->intr->intrs[i] & type) { \
>   				reg |= (v) << i; \
> +				mask |= 1 << i; \
> +			} \
>   		} \
> -		VOP_INTR_SET(vop, name, reg); \
> +		VOP_INTR_SET(vop, name, mask, reg); \
>   	} while (0)
>   #define VOP_INTR_GET_TYPE(vop, name, type) \
>   		vop_get_intr_type(vop, &vop->data->intr->name, type)
Hi John

Great, it works for me, Thanks for this fix.

Applied to my drm-next branch.

-- ?ark Yao



From mark.yao at rock-chips.com  Wed Jan 13 00:22:07 2016
From: mark.yao at rock-chips.com (Mark Yao)
Date: Wed, 13 Jan 2016 16:22:07 +0800
Subject: [PATCH 1/2] drm/rockchip: Don't build rockchip_drm_vop as modules
In-Reply-To: <1452673328-29188-1-git-send-email-mark.yao@rock-chips.com>
References: <1452673328-29188-1-git-send-email-mark.yao@rock-chips.com>
Message-ID: <1452673328-29188-2-git-send-email-mark.yao@rock-chips.com>

rockchip_drm_vop's module init had moved to rockchip_vop_reg.c
so no need to build rockchip_drm_vop.ko

Signed-off-by: Mark Yao <mark.yao at rock-chips.com>
---
 drivers/gpu/drm/rockchip/Makefile |    5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index a9d380f..9e6e992 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -3,9 +3,8 @@
 # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
 
 rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
-		rockchip_drm_gem.o
+		rockchip_drm_gem.o rockchip_drm_vop.o
 
 obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
 
-obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_drm_vop.o \
-				rockchip_vop_reg.o
+obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_vop_reg.o
-- 
1.7.9.5




From mark.yao at rock-chips.com  Wed Jan 13 00:22:06 2016
From: mark.yao at rock-chips.com (Mark Yao)
Date: Wed, 13 Jan 2016 16:22:06 +0800
Subject: [PATCH 0/2] drm/rockchip: cleanup unnecessary modules and symbol
Message-ID: <1452673328-29188-1-git-send-email-mark.yao@rock-chips.com>

Since a67719d (drm/rockchip: vop: spilt register related into
rockchip_reg_vop.c), rockchip_drm_vop's module_init moved to
rockchip_vop_reg.c, then no need to build rockchip_drm_vop.ko

After build rockchip_drm_vop.c into rockchipdrm.ko, following
export symbol is also no needed:
    rockchip_drm_dma_attach_device
    rockchip_drm_dma_detach_device
    rockchip_drm_dma_attach_device
    rockchip_drm_dma_detach_device
    rockchip_register_crtc_funcs
    rockchip_unregister_crtc_funcs
    rockchip_fb_get_gem_obj

Mark Yao (2):
  drm/rockchip: Don't build rockchip_drm_vop as modules
  drm/rockchip: cleanup unnecessary export symbol

 drivers/gpu/drm/rockchip/Makefile           |    5 ++---
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c |    4 ----
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c  |    1 -
 3 files changed, 2 insertions(+), 8 deletions(-)

-- 
1.7.9.5




From mark.yao at rock-chips.com  Wed Jan 13 00:22:08 2016
From: mark.yao at rock-chips.com (Mark Yao)
Date: Wed, 13 Jan 2016 16:22:08 +0800
Subject: [PATCH 2/2] drm/rockchip: cleanup unnecessary export symbol
In-Reply-To: <1452673328-29188-1-git-send-email-mark.yao@rock-chips.com>
References: <1452673328-29188-1-git-send-email-mark.yao@rock-chips.com>
Message-ID: <1452673328-29188-3-git-send-email-mark.yao@rock-chips.com>

Now rockchip_drm_vop.c is build into rockchipdrm.ko, so
no need to export following symbol anymore:
    rockchip_drm_dma_attach_device
    rockchip_drm_dma_detach_device
    rockchip_drm_dma_attach_device
    rockchip_drm_dma_detach_device
    rockchip_register_crtc_funcs
    rockchip_unregister_crtc_funcs
    rockchip_fb_get_gem_obj

Signed-off-by: Mark Yao <mark.yao at rock-chips.com>
---
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c |    4 ----
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c  |    1 -
 2 files changed, 5 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index 8397d1b..a0d51cc 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -55,14 +55,12 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
 
 	return arm_iommu_attach_device(dev, mapping);
 }
-EXPORT_SYMBOL_GPL(rockchip_drm_dma_attach_device);
 
 void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
 				    struct device *dev)
 {
 	arm_iommu_detach_device(dev);
 }
-EXPORT_SYMBOL_GPL(rockchip_drm_dma_detach_device);
 
 int rockchip_register_crtc_funcs(struct drm_crtc *crtc,
 				 const struct rockchip_crtc_funcs *crtc_funcs)
@@ -77,7 +75,6 @@ int rockchip_register_crtc_funcs(struct drm_crtc *crtc,
 
 	return 0;
 }
-EXPORT_SYMBOL_GPL(rockchip_register_crtc_funcs);
 
 void rockchip_unregister_crtc_funcs(struct drm_crtc *crtc)
 {
@@ -89,7 +86,6 @@ void rockchip_unregister_crtc_funcs(struct drm_crtc *crtc)
 
 	priv->crtc_funcs[pipe] = NULL;
 }
-EXPORT_SYMBOL_GPL(rockchip_unregister_crtc_funcs);
 
 static struct drm_crtc *rockchip_crtc_from_pipe(struct drm_device *drm,
 						int pipe)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
index f784488..dd1f8d3 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -39,7 +39,6 @@ struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb,
 
 	return rk_fb->obj[plane];
 }
-EXPORT_SYMBOL_GPL(rockchip_fb_get_gem_obj);
 
 static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb)
 {
-- 
1.7.9.5




From jeffy.chen at rock-chips.com  Wed Jan 13 00:53:14 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Wed, 13 Jan 2016 16:53:14 +0800
Subject: [PATCH v4 0/6] rockchip: kylin: Boot with android boot image
Message-ID: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>

We are porting android to kylin board now.
This series could let it boot up with android's boot image.

Changes in v4:
Remove unused reboot mode definitions.

Changes in v3:
Use rockchip's legacy reboot mode definitions.

Changes in v2:
Add comments.

Jeffy Chen (6):
  common/image-fdt.c: Make boot_get_fdt() perform a check for Android
    images
  ARM: bootm: Try to use relocated ramdisk
  rockchip: rk3036: Bind GPIO banks
  rockchip: kylin: Add default gpt partition table
  rockchip: kylin: Enable boot with android boot image
  rockchip: kylin: Check fastboot request

 arch/arm/lib/bootm.c                      | 12 ++++++-
 board/kylin/kylin_rk3036/kylin_rk3036.c   | 32 ++++++++++++++++++
 common/image-fdt.c                        |  4 +++
 drivers/pinctrl/rockchip/pinctrl_rk3036.c |  8 +++++
 include/configs/kylin_rk3036.h            | 55 +++++++++++++++++++++++++++++++
 5 files changed, 110 insertions(+), 1 deletion(-)

-- 
2.1.4




From jeffy.chen at rock-chips.com  Wed Jan 13 00:53:15 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Wed, 13 Jan 2016 16:53:15 +0800
Subject: [PATCH v4 1/6] common/image-fdt.c: Make boot_get_fdt() perform a
 check for Android images
In-Reply-To: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
References: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <1452675200-15941-2-git-send-email-jeffy.chen@rock-chips.com>

Android images don't have a fdt.

Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
Acked-by: Simon Glass <sjg at chromium.org>
---

Changes in v4: None
Changes in v3: None
Changes in v2: None

 common/image-fdt.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/common/image-fdt.c b/common/image-fdt.c
index 5e4e5bd..41aaa0d 100644
--- a/common/image-fdt.c
+++ b/common/image-fdt.c
@@ -379,6 +379,10 @@ int boot_get_fdt(int flag, int argc, char * const argv[], uint8_t arch,
 				       (long)fdt_addr);
 			}
 			break;
+#ifdef CONFIG_ANDROID_BOOT_IMAGE
+		case IMAGE_FORMAT_ANDROID:
+			goto no_fdt;
+#endif
 		default:
 			puts("ERROR: Did not find a cmdline Flattened Device Tree\n");
 			goto no_fdt;
-- 
2.1.4




From jeffy.chen at rock-chips.com  Wed Jan 13 00:53:16 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Wed, 13 Jan 2016 16:53:16 +0800
Subject: [PATCH v4 2/6] ARM: bootm: Try to use relocated ramdisk
In-Reply-To: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
References: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <1452675200-15941-3-git-send-email-jeffy.chen@rock-chips.com>

After boot_ramdisk_high(), ramdisk would be relocated to
initrd_start & initrd_end, so use them instead of rd_start & rd_end.

Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
Acked-by: Simon Glass <sjg at chromium.org>

---

Changes in v4: None
Changes in v3: None
Changes in v2:
Add comments.

 arch/arm/lib/bootm.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c
index a477cae..0838d89 100644
--- a/arch/arm/lib/bootm.c
+++ b/arch/arm/lib/bootm.c
@@ -225,7 +225,17 @@ static void boot_prep_linux(bootm_headers_t *images)
 		if (BOOTM_ENABLE_MEMORY_TAGS)
 			setup_memory_tags(gd->bd);
 		if (BOOTM_ENABLE_INITRD_TAG) {
-			if (images->rd_start && images->rd_end) {
+			/*
+			 * In boot_ramdisk_high(), it may relocate ramdisk to
+			 * a specified location. And set images->initrd_start &
+			 * images->initrd_end to relocated ramdisk's start/end
+			 * addresses. So use them instead of images->rd_start &
+			 * images->rd_end when possible.
+			 */
+			if (images->initrd_start && images->initrd_end) {
+				setup_initrd_tag(gd->bd, images->initrd_start,
+						 images->initrd_end);
+			} else if (images->rd_start && images->rd_end) {
 				setup_initrd_tag(gd->bd, images->rd_start,
 						 images->rd_end);
 			}
-- 
2.1.4




From jeffy.chen at rock-chips.com  Wed Jan 13 00:53:17 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Wed, 13 Jan 2016 16:53:17 +0800
Subject: [PATCH v4 3/6] rockchip: rk3036: Bind GPIO banks
In-Reply-To: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
References: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <1452675200-15941-4-git-send-email-jeffy.chen@rock-chips.com>

Call dm_scan_fdt_node() in rk3036 pinctrl uclass binding.

Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
Acked-by: Simon Glass <sjg at chromium.org>
---

Changes in v4: None
Changes in v3: None
Changes in v2: None

 drivers/pinctrl/rockchip/pinctrl_rk3036.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/pinctrl/rockchip/pinctrl_rk3036.c b/drivers/pinctrl/rockchip/pinctrl_rk3036.c
index 581b096..1f78bf8 100644
--- a/drivers/pinctrl/rockchip/pinctrl_rk3036.c
+++ b/drivers/pinctrl/rockchip/pinctrl_rk3036.c
@@ -15,6 +15,7 @@
 #include <asm/arch/hardware.h>
 #include <asm/arch/periph.h>
 #include <dm/pinctrl.h>
+#include <dm/root.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -252,6 +253,12 @@ static struct pinctrl_ops rk3036_pinctrl_ops = {
 	.get_periph_id	= rk3036_pinctrl_get_periph_id,
 };
 
+static int rk3036_pinctrl_bind(struct udevice *dev)
+{
+	/* scan child GPIO banks */
+	return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false);
+}
+
 static int rk3036_pinctrl_probe(struct udevice *dev)
 {
 	struct rk3036_pinctrl_priv *priv = dev_get_priv(dev);
@@ -272,5 +279,6 @@ U_BOOT_DRIVER(pinctrl_rk3036) = {
 	.of_match	= rk3036_pinctrl_ids,
 	.priv_auto_alloc_size = sizeof(struct rk3036_pinctrl_priv),
 	.ops		= &rk3036_pinctrl_ops,
+	.bind		= rk3036_pinctrl_bind,
 	.probe		= rk3036_pinctrl_probe,
 };
-- 
2.1.4




From jeffy.chen at rock-chips.com  Wed Jan 13 00:53:19 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Wed, 13 Jan 2016 16:53:19 +0800
Subject: [PATCH v4 5/6] rockchip: kylin: Enable boot with android boot image
In-Reply-To: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
References: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <1452675200-15941-6-git-send-email-jeffy.chen@rock-chips.com>

The android kernel is using appended dtb by default, and store
ramdisk right after kernel & dtb.
So we needs to relocate ramdisk, and use atags to pass params.

Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
Acked-by: Simon Glass <sjg at chromium.org>
---

Changes in v4: None
Changes in v3: None
Changes in v2: None

 include/configs/kylin_rk3036.h | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/include/configs/kylin_rk3036.h b/include/configs/kylin_rk3036.h
index b750b26..49997ec 100644
--- a/include/configs/kylin_rk3036.h
+++ b/include/configs/kylin_rk3036.h
@@ -35,6 +35,29 @@
 #undef CONFIG_EXTRA_ENV_SETTINGS
 #define CONFIG_EXTRA_ENV_SETTINGS \
 	"partitions=" PARTS_DEFAULT \
+	"mmcdev=0\0" \
+	"mmcpart=5\0" \
+	"loadaddr=" __stringify(CONFIG_SYS_LOAD_ADDR) "\0" \
+
+#define CONFIG_ANDROID_BOOT_IMAGE
+#define CONFIG_SYS_BOOT_RAMDISK_HIGH
+#define CONFIG_SYS_HUSH_PARSER
+
+#undef CONFIG_BOOTCOMMAND
+#define CONFIG_BOOTCOMMAND \
+	"mmc dev ${mmcdev}; if mmc rescan; then " \
+		"part start mmc ${mmcdev} ${mmcpart} boot_start;" \
+		"part size mmc ${mmcdev} ${mmcpart} boot_size;" \
+		"mmc read ${loadaddr} ${boot_start} ${boot_size};" \
+		"bootm start ${loadaddr}; bootm ramdisk;" \
+		"bootm prep; bootm go;" \
+	"fi;" \
+
+/* Enable atags */
+#define CONFIG_SYS_BOOTPARAMS_LEN	(64*1024)
+#define CONFIG_INITRD_TAG
+#define CONFIG_SETUP_MEMORY_TAGS
+#define CONFIG_CMDLINE_TAG
 
 #endif
 
-- 
2.1.4




From jeffy.chen at rock-chips.com  Wed Jan 13 00:53:18 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Wed, 13 Jan 2016 16:53:18 +0800
Subject: [PATCH v4 4/6] rockchip: kylin: Add default gpt partition table
In-Reply-To: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
References: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <1452675200-15941-5-git-send-email-jeffy.chen@rock-chips.com>

Add default android gpt partition table for kylin board.

Use "gpt write mmc 0 $partitions" to apply.

Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
Acked-by: Simon Glass <sjg at chromium.org>
---

Changes in v4: None
Changes in v3: None
Changes in v2: None

 include/configs/kylin_rk3036.h | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/include/configs/kylin_rk3036.h b/include/configs/kylin_rk3036.h
index aa07889..b750b26 100644
--- a/include/configs/kylin_rk3036.h
+++ b/include/configs/kylin_rk3036.h
@@ -9,4 +9,33 @@
 
 #include <configs/rk3036_common.h>
 
+#ifndef CONFIG_SPL_BUILD
+
+/* Enable gpt partition table */
+#define CONFIG_CMD_GPT
+#define CONFIG_RANDOM_UUID
+#define CONFIG_EFI_PARTITION
+#define PARTS_DEFAULT \
+	"uuid_disk=${uuid_gpt_disk};" \
+	"name=loader,start=32K,size=4000K,uuid=${uuid_gpt_loader};" \
+	"name=reserved,size=64K,uuid=${uuid_gpt_reserved};" \
+	"name=misc,size=4M,uuid=${uuid_gpt_misc};" \
+	"name=recovery,size=32M,uuid=${uuid_gpt_recovery};" \
+	"name=boot_a,size=32M,uuid=${uuid_gpt_boot_a};" \
+	"name=boot_b,size=32M,uuid=${uuid_gpt_boot_b};" \
+	"name=system_a,size=818M,uuid=${uuid_gpt_system_a};" \
+	"name=system_b,size=818M,uuid=${uuid_gpt_system_b};" \
+	"name=vendor_a,size=50M,uuid=${uuid_gpt_vendor_a};" \
+	"name=vendor_b,size=50M,uuid=${uuid_gpt_vendor_b};" \
+	"name=cache,size=100M,uuid=${uuid_gpt_cache};" \
+	"name=metadata,size=16M,uuid=${uuid_gpt_metadata};" \
+	"name=persist,size=4M,uuid=${uuid_gpt_persist};" \
+	"name=userdata,size=-,uuid=${uuid_gpt_userdata};\0" \
+
+#undef CONFIG_EXTRA_ENV_SETTINGS
+#define CONFIG_EXTRA_ENV_SETTINGS \
+	"partitions=" PARTS_DEFAULT \
+
+#endif
+
 #endif
-- 
2.1.4




From jeffy.chen at rock-chips.com  Wed Jan 13 00:53:20 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Wed, 13 Jan 2016 16:53:20 +0800
Subject: [PATCH v4 6/6] rockchip: kylin: Check fastboot request
In-Reply-To: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
References: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <1452675200-15941-7-git-send-email-jeffy.chen@rock-chips.com>

We will save boot mode flag in grf's os_reg[4], if fastboot
requested or fastboot key pressed, try to enter fastboot mode
at preboot stage.

Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>

---

Changes in v4:
Remove unused reboot mode definitions.

Changes in v3:
Use rockchip's legacy reboot mode definitions.

Changes in v2: None

 board/kylin/kylin_rk3036/kylin_rk3036.c | 32 ++++++++++++++++++++++++++++++++
 include/configs/kylin_rk3036.h          |  3 +++
 2 files changed, 35 insertions(+)

diff --git a/board/kylin/kylin_rk3036/kylin_rk3036.c b/board/kylin/kylin_rk3036/kylin_rk3036.c
index 40d6b52..a730d7c 100644
--- a/board/kylin/kylin_rk3036/kylin_rk3036.c
+++ b/board/kylin/kylin_rk3036/kylin_rk3036.c
@@ -8,10 +8,15 @@
 #include <dm.h>
 #include <asm/io.h>
 #include <asm/arch/uart.h>
+#include <asm/arch-rockchip/grf_rk3036.h>
 #include <asm/arch/sdram_rk3036.h>
+#include <asm/gpio.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
+#define GRF_BASE	0x20008000
+static struct rk3036_grf * const grf = (void *)GRF_BASE;
+
 void get_ddr_config(struct rk3036_ddr_config *config)
 {
 	/* K4B4G1646Q config */
@@ -28,6 +33,33 @@ void get_ddr_config(struct rk3036_ddr_config *config)
 	config->bw = 1;
 }
 
+#define FASTBOOT_KEY_GPIO 93
+
+int fastboot_key_pressed(void)
+{
+	gpio_request(FASTBOOT_KEY_GPIO, "fastboot_key");
+	gpio_direction_input(FASTBOOT_KEY_GPIO);
+	return !gpio_get_value(FASTBOOT_KEY_GPIO);
+}
+
+#define ROCKCHIP_BOOT_MODE_FASTBOOT	0x5242C309
+
+int board_late_init(void)
+{
+	int boot_mode = readl(&grf->os_reg[4]);
+
+	/* Clear boot mode */
+	writel(0, &grf->os_reg[4]);
+
+	if (boot_mode == ROCKCHIP_BOOT_MODE_FASTBOOT ||
+	    fastboot_key_pressed()) {
+		printf("enter fastboot!\n");
+		setenv("preboot", "setenv preboot; fastboot usb0");
+	}
+
+	return 0;
+}
+
 int board_init(void)
 {
 	return 0;
diff --git a/include/configs/kylin_rk3036.h b/include/configs/kylin_rk3036.h
index 49997ec..424e81b 100644
--- a/include/configs/kylin_rk3036.h
+++ b/include/configs/kylin_rk3036.h
@@ -39,6 +39,9 @@
 	"mmcpart=5\0" \
 	"loadaddr=" __stringify(CONFIG_SYS_LOAD_ADDR) "\0" \
 
+#define CONFIG_BOARD_LATE_INIT
+#define CONFIG_PREBOOT
+
 #define CONFIG_ANDROID_BOOT_IMAGE
 #define CONFIG_SYS_BOOT_RAMDISK_HIGH
 #define CONFIG_SYS_HUSH_PARSER
-- 
2.1.4




From john at metanate.com  Wed Jan 13 04:53:34 2016
From: john at metanate.com (John Keeping)
Date: Wed, 13 Jan 2016 12:53:34 +0000
Subject: [RFC/PATCH] drm/rockchip: don't wait for vblank if fb hasn't changed
Message-ID: <1452689614-6570-1-git-send-email-john@metanate.com>

As commented in drm_atomic_helper_wait_for_vblanks(), userspace relies
on cursor ioctls being unsynced.  Converting the rockchip driver to
atomic has significantly impacted cursor performance by making every
cursor update wait for vblank.

By skipping the vblank sync when the framebuffer has not changed (as is
done in drm_atomic_helper_wait_for_vblanks()) we can avoid this for the
common case of moving the cursor and only need to delay the cursor ioctl
when the cursor icon changes.

I originally inserted a check on legacy_cursor_update as well, but that
caused a storm of iommu page faults.  I didn't investigate the cause of
those since this change gives enough of a performance improvement for my
use case.

This is RFC because of that and because the framebuffer_changed()
function is copied from drm_atomic_helper.c as a quick way to test the
result.

Signed-off-by: John Keeping <john at metanate.com>
---
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 27 +++++++++++++++++++++++++--
 1 file changed, 25 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
index f784488..8fd9821 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -177,8 +177,28 @@ static void rockchip_crtc_wait_for_update(struct drm_crtc *crtc)
 		crtc_funcs->wait_for_update(crtc);
 }
 
+static bool framebuffer_changed(struct drm_device *dev,
+				struct drm_atomic_state *old_state,
+				struct drm_crtc *crtc)
+{
+	struct drm_plane *plane;
+	struct drm_plane_state *old_plane_state;
+	int i;
+
+	for_each_plane_in_state(old_state, plane, old_plane_state, i) {
+		if (plane->state->crtc != crtc &&
+		    old_plane_state->crtc != crtc)
+			continue;
+
+		if (plane->state->fb != old_plane_state->fb)
+			return true;
+	}
+
+	return false;
+}
+
 static void
-rockchip_atomic_wait_for_complete(struct drm_atomic_state *old_state)
+rockchip_atomic_wait_for_complete(struct drm_device *dev, struct drm_atomic_state *old_state)
 {
 	struct drm_crtc_state *old_crtc_state;
 	struct drm_crtc *crtc;
@@ -194,6 +214,9 @@ rockchip_atomic_wait_for_complete(struct drm_atomic_state *old_state)
 		if (!crtc->state->active)
 			continue;
 
+		if (!framebuffer_changed(dev, old_state, crtc))
+			continue;
+
 		ret = drm_crtc_vblank_get(crtc);
 		if (ret != 0)
 			continue;
@@ -241,7 +264,7 @@ rockchip_atomic_commit_complete(struct rockchip_atomic_commit *commit)
 
 	drm_atomic_helper_commit_planes(dev, state, true);
 
-	rockchip_atomic_wait_for_complete(state);
+	rockchip_atomic_wait_for_complete(dev, state);
 
 	drm_atomic_helper_cleanup_planes(dev, state);
 
-- 
2.7.0.rc3.140.g520a093



From daniel at ffwll.ch  Wed Jan 13 06:23:20 2016
From: daniel at ffwll.ch (Daniel Vetter)
Date: Wed, 13 Jan 2016 15:23:20 +0100
Subject: [RFC/PATCH] drm/rockchip: don't wait for vblank if fb hasn't
 changed
In-Reply-To: <1452689614-6570-1-git-send-email-john@metanate.com>
References: <1452689614-6570-1-git-send-email-john@metanate.com>
Message-ID: <20160113142320.GQ19130@phenom.ffwll.local>

On Wed, Jan 13, 2016 at 12:53:34PM +0000, John Keeping wrote:
> As commented in drm_atomic_helper_wait_for_vblanks(), userspace relies
> on cursor ioctls being unsynced.  Converting the rockchip driver to
> atomic has significantly impacted cursor performance by making every
> cursor update wait for vblank.
> 
> By skipping the vblank sync when the framebuffer has not changed (as is
> done in drm_atomic_helper_wait_for_vblanks()) we can avoid this for the
> common case of moving the cursor and only need to delay the cursor ioctl
> when the cursor icon changes.
> 
> I originally inserted a check on legacy_cursor_update as well, but that
> caused a storm of iommu page faults.  I didn't investigate the cause of
> those since this change gives enough of a performance improvement for my
> use case.
> 
> This is RFC because of that and because the framebuffer_changed()
> function is copied from drm_atomic_helper.c as a quick way to test the
> result.
> 
> Signed-off-by: John Keeping <john at metanate.com>
> ---
>  drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 27 +++++++++++++++++++++++++--
>  1 file changed, 25 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> index f784488..8fd9821 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> @@ -177,8 +177,28 @@ static void rockchip_crtc_wait_for_update(struct drm_crtc *crtc)
>  		crtc_funcs->wait_for_update(crtc);
>  }
>  
> +static bool framebuffer_changed(struct drm_device *dev,
> +				struct drm_atomic_state *old_state,
> +				struct drm_crtc *crtc)
> +{
> +	struct drm_plane *plane;
> +	struct drm_plane_state *old_plane_state;
> +	int i;
> +
> +	for_each_plane_in_state(old_state, plane, old_plane_state, i) {
> +		if (plane->state->crtc != crtc &&
> +		    old_plane_state->crtc != crtc)
> +			continue;
> +
> +		if (plane->state->fb != old_plane_state->fb)
> +			return true;
> +	}
> +
> +	return false;
> +}

Please don't hand-roll logic that affects semantics like this. Instead
please use drm_atomic_helper_wait_for_vblanks(), which should do this
correctly for you.

If that's not the case then we need to improve the generic helper, or
figure out what's different with rockhip.

Thanks, Daniel

> +
>  static void
> -rockchip_atomic_wait_for_complete(struct drm_atomic_state *old_state)
> +rockchip_atomic_wait_for_complete(struct drm_device *dev, struct drm_atomic_state *old_state)
>  {
>  	struct drm_crtc_state *old_crtc_state;
>  	struct drm_crtc *crtc;
> @@ -194,6 +214,9 @@ rockchip_atomic_wait_for_complete(struct drm_atomic_state *old_state)
>  		if (!crtc->state->active)
>  			continue;
>  
> +		if (!framebuffer_changed(dev, old_state, crtc))
> +			continue;
> +
>  		ret = drm_crtc_vblank_get(crtc);
>  		if (ret != 0)
>  			continue;
> @@ -241,7 +264,7 @@ rockchip_atomic_commit_complete(struct rockchip_atomic_commit *commit)
>  
>  	drm_atomic_helper_commit_planes(dev, state, true);
>  
> -	rockchip_atomic_wait_for_complete(state);
> +	rockchip_atomic_wait_for_complete(dev, state);
>  
>  	drm_atomic_helper_cleanup_planes(dev, state);
>  
> -- 
> 2.7.0.rc3.140.g520a093
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


From john at metanate.com  Wed Jan 13 06:34:25 2016
From: john at metanate.com (John Keeping)
Date: Wed, 13 Jan 2016 14:34:25 +0000
Subject: [RFC/PATCH] drm/rockchip: don't wait for vblank if fb hasn't
 changed
In-Reply-To: <20160113142320.GQ19130@phenom.ffwll.local>
References: <1452689614-6570-1-git-send-email-john@metanate.com>
 <20160113142320.GQ19130@phenom.ffwll.local>
Message-ID: <20160113143425.363ce07a.john@metanate.com>

On Wed, 13 Jan 2016 15:23:20 +0100, Daniel Vetter wrote:

> On Wed, Jan 13, 2016 at 12:53:34PM +0000, John Keeping wrote:
> > As commented in drm_atomic_helper_wait_for_vblanks(), userspace
> > relies on cursor ioctls being unsynced.  Converting the rockchip
> > driver to atomic has significantly impacted cursor performance by
> > making every cursor update wait for vblank.
> > 
> > By skipping the vblank sync when the framebuffer has not changed
> > (as is done in drm_atomic_helper_wait_for_vblanks()) we can avoid
> > this for the common case of moving the cursor and only need to
> > delay the cursor ioctl when the cursor icon changes.
> > 
> > I originally inserted a check on legacy_cursor_update as well, but
> > that caused a storm of iommu page faults.  I didn't investigate the
> > cause of those since this change gives enough of a performance
> > improvement for my use case.
> > 
> > This is RFC because of that and because the framebuffer_changed()
> > function is copied from drm_atomic_helper.c as a quick way to test
> > the result.
> > 
> > Signed-off-by: John Keeping <john at metanate.com>
> > ---
> >  drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 27
> > +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2
> > deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index f784488..8fd9821
> > 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > @@ -177,8 +177,28 @@ static void
> > rockchip_crtc_wait_for_update(struct drm_crtc *crtc)
> > crtc_funcs->wait_for_update(crtc); }
> >  
> > +static bool framebuffer_changed(struct drm_device *dev,
> > +				struct drm_atomic_state *old_state,
> > +				struct drm_crtc *crtc)
> > +{
> > +	struct drm_plane *plane;
> > +	struct drm_plane_state *old_plane_state;
> > +	int i;
> > +
> > +	for_each_plane_in_state(old_state, plane, old_plane_state,
> > i) {
> > +		if (plane->state->crtc != crtc &&
> > +		    old_plane_state->crtc != crtc)
> > +			continue;
> > +
> > +		if (plane->state->fb != old_plane_state->fb)
> > +			return true;
> > +	}
> > +
> > +	return false;
> > +}  
> 
> Please don't hand-roll logic that affects semantics like this. Instead
> please use drm_atomic_helper_wait_for_vblanks(), which should do this
> correctly for you.
> 
> If that's not the case then we need to improve the generic helper, or
> figure out what's different with rockhip.

According to commit 63ebb9f (drm/rockchip: Convert to support atomic
API) it's because rockchip doesn't have a hardware vblank counter.

I'm not entirely clear on why this prevents the use of
drm_atomic_helper_wait_for_vblanks().

> > +
> >  static void
> > -rockchip_atomic_wait_for_complete(struct drm_atomic_state
> > *old_state) +rockchip_atomic_wait_for_complete(struct drm_device
> > *dev, struct drm_atomic_state *old_state) {
> >  	struct drm_crtc_state *old_crtc_state;
> >  	struct drm_crtc *crtc;
> > @@ -194,6 +214,9 @@ rockchip_atomic_wait_for_complete(struct
> > drm_atomic_state *old_state) if (!crtc->state->active)
> >  			continue;
> >  
> > +		if (!framebuffer_changed(dev, old_state, crtc))
> > +			continue;
> > +
> >  		ret = drm_crtc_vblank_get(crtc);
> >  		if (ret != 0)
> >  			continue;
> > @@ -241,7 +264,7 @@ rockchip_atomic_commit_complete(struct
> > rockchip_atomic_commit *commit) 
> >  	drm_atomic_helper_commit_planes(dev, state, true);
> >  
> > -	rockchip_atomic_wait_for_complete(state);
> > +	rockchip_atomic_wait_for_complete(dev, state);
> >  
> >  	drm_atomic_helper_cleanup_planes(dev, state);
> >  
> > -- 
> > 2.7.0.rc3.140.g520a093
> > 
> > _______________________________________________
> > dri-devel mailing list
> > dri-devel at lists.freedesktop.org
> > http://lists.freedesktop.org/mailman/listinfo/dri-devel  
> 


From heiko at sntech.de  Wed Jan 13 06:59:51 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Wed, 13 Jan 2016 15:59:51 +0100
Subject: [PATCH v12 0/18] Add Analogix Core Display Port Driver
In-Reply-To: <1450873538-18304-1-git-send-email-ykk@rock-chips.com>
References: <1450873538-18304-1-git-send-email-ykk@rock-chips.com>
Message-ID: <2322926.s1viZ6tSS3@phil>

Hi Yakir,

Am Mittwoch, 23. Dezember 2015, 20:25:38 schrieb Yakir Yang:
>    The Samsung Exynos eDP controller and Rockchip RK3288 eDP controller
> share the same IP, so a lot of parts can be re-used. I split the common
> code into bridge directory, then rk3288 and exynos only need to keep
> some platform code. Cause I can't find the exact IP name of exynos dp
> controller, so I decide to name dp core driver with "analogix" which I
> find in rk3288 eDP TRM

could you rebase your patches on top of Dave's drm-next branch [0] please?
The exynos part got some const attributes for the *_func_ops structs and 
drm_encoder_init got an additional parameter.
I'm still hoping that we can get this finally committed once 4.5-rc1 is 
released in 1.5 weeks :-) .

I did try to merge things together [1], the system at least comes up and 
detects the panel (EDID is read correctly and it turns the backlight on) but 
I don't see any output on the display - hdmi works fine though and X11 is 
claiming everything to be fine in both cases.

So I don't know yet if I made a mistake when putting this together or there 
is an issue on the driver-side.


Thanks
Heiko


[0] http://cgit.freedesktop.org/~airlied/linux/log/?h=drm-next
[1] https://github.com/mmind/linux-rockchip/tree/tmp/analogixdp_v12-veyron
I left out patch16, per your talk with Jingoo and me not seeing any AUX CH 
errors without it.



From trini at konsulko.com  Wed Jan 13 07:21:57 2016
From: trini at konsulko.com (Tom Rini)
Date: Wed, 13 Jan 2016 10:21:57 -0500
Subject: [U-Boot] [PATCH v4 1/6] common/image-fdt.c: Make boot_get_fdt()
 perform a check for Android images
In-Reply-To: <1452675200-15941-2-git-send-email-jeffy.chen@rock-chips.com>
References: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
 <1452675200-15941-2-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <20160113152157.GY3359@bill-the-cat>

On Wed, Jan 13, 2016 at 04:53:15PM +0800, Jeffy Chen wrote:

> Android images don't have a fdt.
> 
> Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
> Acked-by: Simon Glass <sjg at chromium.org>
> ---
> 
> Changes in v4: None
> Changes in v3: None
> Changes in v2: None
> 
>  common/image-fdt.c | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/common/image-fdt.c b/common/image-fdt.c
> index 5e4e5bd..41aaa0d 100644
> --- a/common/image-fdt.c
> +++ b/common/image-fdt.c
> @@ -379,6 +379,10 @@ int boot_get_fdt(int flag, int argc, char * const argv[], uint8_t arch,
>  				       (long)fdt_addr);
>  			}
>  			break;
> +#ifdef CONFIG_ANDROID_BOOT_IMAGE
> +		case IMAGE_FORMAT_ANDROID:
> +			goto no_fdt;
> +#endif
>  		default:
>  			puts("ERROR: Did not find a cmdline Flattened Device Tree\n");
>  			goto no_fdt;

Hang on, this doesn't seem right.  Rob or Paul can you comment more
here?  I know we've gone through some issues in the past with respect to
booting Android and FDT.  Thanks!

-- 
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160113/f80d1ba1/attachment.sig>

From trini at konsulko.com  Wed Jan 13 07:27:55 2016
From: trini at konsulko.com (Tom Rini)
Date: Wed, 13 Jan 2016 10:27:55 -0500
Subject: [U-Boot] [PATCH v4 2/6] ARM: bootm: Try to use relocated ramdisk
In-Reply-To: <1452675200-15941-3-git-send-email-jeffy.chen@rock-chips.com>
References: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
 <1452675200-15941-3-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <20160113152755.GZ3359@bill-the-cat>

On Wed, Jan 13, 2016 at 04:53:16PM +0800, Jeffy Chen wrote:

> After boot_ramdisk_high(), ramdisk would be relocated to
> initrd_start & initrd_end, so use them instead of rd_start & rd_end.
> 
> Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
> Acked-by: Simon Glass <sjg at chromium.org>
> 

Reviewed-by: Tom Rini <trini at konsulko.com>

-- 
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160113/27299b8b/attachment.sig>

From trini at konsulko.com  Wed Jan 13 07:28:00 2016
From: trini at konsulko.com (Tom Rini)
Date: Wed, 13 Jan 2016 10:28:00 -0500
Subject: [U-Boot] [PATCH v4 3/6] rockchip: rk3036: Bind GPIO banks
In-Reply-To: <1452675200-15941-4-git-send-email-jeffy.chen@rock-chips.com>
References: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
 <1452675200-15941-4-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <20160113152800.GA3359@bill-the-cat>

On Wed, Jan 13, 2016 at 04:53:17PM +0800, Jeffy Chen wrote:

> Call dm_scan_fdt_node() in rk3036 pinctrl uclass binding.
> 
> Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
> Acked-by: Simon Glass <sjg at chromium.org>

Reviewed-by: Tom Rini <trini at konsulko.com>

-- 
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160113/b98b0fe5/attachment.sig>

From trini at konsulko.com  Wed Jan 13 07:28:06 2016
From: trini at konsulko.com (Tom Rini)
Date: Wed, 13 Jan 2016 10:28:06 -0500
Subject: [U-Boot] [PATCH v4 4/6] rockchip: kylin: Add default gpt
 partition table
In-Reply-To: <1452675200-15941-5-git-send-email-jeffy.chen@rock-chips.com>
References: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
 <1452675200-15941-5-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <20160113152806.GB3359@bill-the-cat>

On Wed, Jan 13, 2016 at 04:53:18PM +0800, Jeffy Chen wrote:

> Add default android gpt partition table for kylin board.
> 
> Use "gpt write mmc 0 $partitions" to apply.
> 
> Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
> Acked-by: Simon Glass <sjg at chromium.org>

Reviewed-by: Tom Rini <trini at konsulko.com>

-- 
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160113/f8ce9c9e/attachment.sig>

From trini at konsulko.com  Wed Jan 13 07:28:11 2016
From: trini at konsulko.com (Tom Rini)
Date: Wed, 13 Jan 2016 10:28:11 -0500
Subject: [U-Boot] [PATCH v4 5/6] rockchip: kylin: Enable boot with
 android boot image
In-Reply-To: <1452675200-15941-6-git-send-email-jeffy.chen@rock-chips.com>
References: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
 <1452675200-15941-6-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <20160113152811.GC3359@bill-the-cat>

On Wed, Jan 13, 2016 at 04:53:19PM +0800, Jeffy Chen wrote:

> The android kernel is using appended dtb by default, and store
> ramdisk right after kernel & dtb.
> So we needs to relocate ramdisk, and use atags to pass params.
> 
> Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
> Acked-by: Simon Glass <sjg at chromium.org>
> ---
> 
> Changes in v4: None
> Changes in v3: None
> Changes in v2: None
> 
>  include/configs/kylin_rk3036.h | 23 +++++++++++++++++++++++
>  1 file changed, 23 insertions(+)
> 
> diff --git a/include/configs/kylin_rk3036.h b/include/configs/kylin_rk3036.h
> index b750b26..49997ec 100644
> --- a/include/configs/kylin_rk3036.h
> +++ b/include/configs/kylin_rk3036.h
> @@ -35,6 +35,29 @@
>  #undef CONFIG_EXTRA_ENV_SETTINGS
>  #define CONFIG_EXTRA_ENV_SETTINGS \
>  	"partitions=" PARTS_DEFAULT \
> +	"mmcdev=0\0" \
> +	"mmcpart=5\0" \
> +	"loadaddr=" __stringify(CONFIG_SYS_LOAD_ADDR) "\0" \
> +
> +#define CONFIG_ANDROID_BOOT_IMAGE
> +#define CONFIG_SYS_BOOT_RAMDISK_HIGH

This should already be set.

> +#define CONFIG_SYS_HUSH_PARSER
> +
> +#undef CONFIG_BOOTCOMMAND
> +#define CONFIG_BOOTCOMMAND \
> +	"mmc dev ${mmcdev}; if mmc rescan; then " \
> +		"part start mmc ${mmcdev} ${mmcpart} boot_start;" \
> +		"part size mmc ${mmcdev} ${mmcpart} boot_size;" \
> +		"mmc read ${loadaddr} ${boot_start} ${boot_size};" \
> +		"bootm start ${loadaddr}; bootm ramdisk;" \
> +		"bootm prep; bootm go;" \
> +	"fi;" \
> +
> +/* Enable atags */
> +#define CONFIG_SYS_BOOTPARAMS_LEN	(64*1024)
> +#define CONFIG_INITRD_TAG
> +#define CONFIG_SETUP_MEMORY_TAGS
> +#define CONFIG_CMDLINE_TAG

But I'm confused as to what exactly is going on here.  Appended dtb is
not the same as ATAGS.  And you shouldn't need to split up bootm like
that.  Can you please explain a bit more?  Thanks!

-- 
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160113/71fd91f8/attachment-0001.sig>

From trini at konsulko.com  Wed Jan 13 07:28:16 2016
From: trini at konsulko.com (Tom Rini)
Date: Wed, 13 Jan 2016 10:28:16 -0500
Subject: [U-Boot] [PATCH v4 6/6] rockchip: kylin: Check fastboot request
In-Reply-To: <1452675200-15941-7-git-send-email-jeffy.chen@rock-chips.com>
References: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
 <1452675200-15941-7-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <20160113152816.GD3359@bill-the-cat>

On Wed, Jan 13, 2016 at 04:53:20PM +0800, Jeffy Chen wrote:

> We will save boot mode flag in grf's os_reg[4], if fastboot
> requested or fastboot key pressed, try to enter fastboot mode
> at preboot stage.
> 
> Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
> 

Reviewed-by: Tom Rini <trini at konsulko.com>

-- 
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160113/6c8e0da9/attachment.sig>

From daniel at ffwll.ch  Wed Jan 13 07:40:05 2016
From: daniel at ffwll.ch (Daniel Vetter)
Date: Wed, 13 Jan 2016 16:40:05 +0100
Subject: [RFC/PATCH] drm/rockchip: don't wait for vblank if fb hasn't
 changed
In-Reply-To: <20160113143425.363ce07a.john@metanate.com>
References: <1452689614-6570-1-git-send-email-john@metanate.com>
 <20160113142320.GQ19130@phenom.ffwll.local>
 <20160113143425.363ce07a.john@metanate.com>
Message-ID: <20160113154005.GT19130@phenom.ffwll.local>

On Wed, Jan 13, 2016 at 02:34:25PM +0000, John Keeping wrote:
> On Wed, 13 Jan 2016 15:23:20 +0100, Daniel Vetter wrote:
> 
> > On Wed, Jan 13, 2016 at 12:53:34PM +0000, John Keeping wrote:
> > > As commented in drm_atomic_helper_wait_for_vblanks(), userspace
> > > relies on cursor ioctls being unsynced.  Converting the rockchip
> > > driver to atomic has significantly impacted cursor performance by
> > > making every cursor update wait for vblank.
> > > 
> > > By skipping the vblank sync when the framebuffer has not changed
> > > (as is done in drm_atomic_helper_wait_for_vblanks()) we can avoid
> > > this for the common case of moving the cursor and only need to
> > > delay the cursor ioctl when the cursor icon changes.
> > > 
> > > I originally inserted a check on legacy_cursor_update as well, but
> > > that caused a storm of iommu page faults.  I didn't investigate the
> > > cause of those since this change gives enough of a performance
> > > improvement for my use case.
> > > 
> > > This is RFC because of that and because the framebuffer_changed()
> > > function is copied from drm_atomic_helper.c as a quick way to test
> > > the result.
> > > 
> > > Signed-off-by: John Keeping <john at metanate.com>
> > > ---
> > >  drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 27
> > > +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2
> > > deletions(-)
> > > 
> > > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > > b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index f784488..8fd9821
> > > 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > > @@ -177,8 +177,28 @@ static void
> > > rockchip_crtc_wait_for_update(struct drm_crtc *crtc)
> > > crtc_funcs->wait_for_update(crtc); }
> > >  
> > > +static bool framebuffer_changed(struct drm_device *dev,
> > > +				struct drm_atomic_state *old_state,
> > > +				struct drm_crtc *crtc)
> > > +{
> > > +	struct drm_plane *plane;
> > > +	struct drm_plane_state *old_plane_state;
> > > +	int i;
> > > +
> > > +	for_each_plane_in_state(old_state, plane, old_plane_state,
> > > i) {
> > > +		if (plane->state->crtc != crtc &&
> > > +		    old_plane_state->crtc != crtc)
> > > +			continue;
> > > +
> > > +		if (plane->state->fb != old_plane_state->fb)
> > > +			return true;
> > > +	}
> > > +
> > > +	return false;
> > > +}  
> > 
> > Please don't hand-roll logic that affects semantics like this. Instead
> > please use drm_atomic_helper_wait_for_vblanks(), which should do this
> > correctly for you.
> > 
> > If that's not the case then we need to improve the generic helper, or
> > figure out what's different with rockhip.
> 
> According to commit 63ebb9f (drm/rockchip: Convert to support atomic
> API) it's because rockchip doesn't have a hardware vblank counter.
> 
> I'm not entirely clear on why this prevents the use of
> drm_atomic_helper_wait_for_vblanks().

Hm, that commit isn't terribly helpful. If that's really needed then imo I
think we should extract a "drm_atomic_helper_plane_needs_vblank_wait()"
helper that's used by both. But since rockchip does vblank_get/put calls
I'd hope vblanks actually work correctly. And then the helper should work
too.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


From john at metanate.com  Wed Jan 13 07:55:29 2016
From: john at metanate.com (John Keeping)
Date: Wed, 13 Jan 2016 15:55:29 +0000
Subject: [RFC/PATCH] drm/rockchip: don't wait for vblank if fb hasn't
 changed
In-Reply-To: <20160113154005.GT19130@phenom.ffwll.local>
References: <1452689614-6570-1-git-send-email-john@metanate.com>
 <20160113142320.GQ19130@phenom.ffwll.local>
 <20160113143425.363ce07a.john@metanate.com>
 <20160113154005.GT19130@phenom.ffwll.local>
Message-ID: <20160113155529.6048b2c1.john@metanate.com>

On Wed, 13 Jan 2016 16:40:05 +0100, Daniel Vetter wrote:

> On Wed, Jan 13, 2016 at 02:34:25PM +0000, John Keeping wrote:
> > On Wed, 13 Jan 2016 15:23:20 +0100, Daniel Vetter wrote:
> >   
> > > On Wed, Jan 13, 2016 at 12:53:34PM +0000, John Keeping wrote:  
> > > > As commented in drm_atomic_helper_wait_for_vblanks(), userspace
> > > > relies on cursor ioctls being unsynced.  Converting the rockchip
> > > > driver to atomic has significantly impacted cursor performance by
> > > > making every cursor update wait for vblank.
> > > > 
> > > > By skipping the vblank sync when the framebuffer has not changed
> > > > (as is done in drm_atomic_helper_wait_for_vblanks()) we can avoid
> > > > this for the common case of moving the cursor and only need to
> > > > delay the cursor ioctl when the cursor icon changes.
> > > > 
> > > > I originally inserted a check on legacy_cursor_update as well, but
> > > > that caused a storm of iommu page faults.  I didn't investigate the
> > > > cause of those since this change gives enough of a performance
> > > > improvement for my use case.
> > > > 
> > > > This is RFC because of that and because the framebuffer_changed()
> > > > function is copied from drm_atomic_helper.c as a quick way to test
> > > > the result.
> > > > 
> > > > Signed-off-by: John Keeping <john at metanate.com>
> > > > ---
> > > >  drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 27
> > > > +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2
> > > > deletions(-)
> > > > 
> > > > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > > > b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index f784488..8fd9821
> > > > 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > > > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > > > @@ -177,8 +177,28 @@ static void
> > > > rockchip_crtc_wait_for_update(struct drm_crtc *crtc)
> > > > crtc_funcs->wait_for_update(crtc); }
> > > >  
> > > > +static bool framebuffer_changed(struct drm_device *dev,
> > > > +				struct drm_atomic_state *old_state,
> > > > +				struct drm_crtc *crtc)
> > > > +{
> > > > +	struct drm_plane *plane;
> > > > +	struct drm_plane_state *old_plane_state;
> > > > +	int i;
> > > > +
> > > > +	for_each_plane_in_state(old_state, plane, old_plane_state,
> > > > i) {
> > > > +		if (plane->state->crtc != crtc &&
> > > > +		    old_plane_state->crtc != crtc)
> > > > +			continue;
> > > > +
> > > > +		if (plane->state->fb != old_plane_state->fb)
> > > > +			return true;
> > > > +	}
> > > > +
> > > > +	return false;
> > > > +}    
> > > 
> > > Please don't hand-roll logic that affects semantics like this. Instead
> > > please use drm_atomic_helper_wait_for_vblanks(), which should do this
> > > correctly for you.
> > > 
> > > If that's not the case then we need to improve the generic helper, or
> > > figure out what's different with rockhip.  
> > 
> > According to commit 63ebb9f (drm/rockchip: Convert to support atomic
> > API) it's because rockchip doesn't have a hardware vblank counter.
> > 
> > I'm not entirely clear on why this prevents the use of
> > drm_atomic_helper_wait_for_vblanks().  
> 
> Hm, that commit isn't terribly helpful. If that's really needed then imo I
> think we should extract a "drm_atomic_helper_plane_needs_vblank_wait()"
> helper that's used by both. But since rockchip does vblank_get/put calls
> I'd hope vblanks actually work correctly. And then the helper should work
> too.

I tried switching the call to rockchip_crtc_wait_for_update() to
drm_atomic_helper_wait_for_vblanks() and it works fine until I switch
the buffer associated with a cursor, at which point I get iommu page
faults, presumably because the GEM buffer is unreferenced too early.

AFAICT the buffer will be released via drm_atomic_state_free()
unconditionally, but I suspect I'm missing something since that would
mean every driver would hit a similar problem.


From daniel at ffwll.ch  Wed Jan 13 08:21:56 2016
From: daniel at ffwll.ch (Daniel Vetter)
Date: Wed, 13 Jan 2016 17:21:56 +0100
Subject: [RFC/PATCH] drm/rockchip: don't wait for vblank if fb hasn't
 changed
In-Reply-To: <20160113155529.6048b2c1.john@metanate.com>
References: <1452689614-6570-1-git-send-email-john@metanate.com>
 <20160113142320.GQ19130@phenom.ffwll.local>
 <20160113143425.363ce07a.john@metanate.com>
 <20160113154005.GT19130@phenom.ffwll.local>
 <20160113155529.6048b2c1.john@metanate.com>
Message-ID: <20160113162156.GA19130@phenom.ffwll.local>

On Wed, Jan 13, 2016 at 03:55:29PM +0000, John Keeping wrote:
> On Wed, 13 Jan 2016 16:40:05 +0100, Daniel Vetter wrote:
> 
> > On Wed, Jan 13, 2016 at 02:34:25PM +0000, John Keeping wrote:
> > > On Wed, 13 Jan 2016 15:23:20 +0100, Daniel Vetter wrote:
> > >   
> > > > On Wed, Jan 13, 2016 at 12:53:34PM +0000, John Keeping wrote:  
> > > > > As commented in drm_atomic_helper_wait_for_vblanks(), userspace
> > > > > relies on cursor ioctls being unsynced.  Converting the rockchip
> > > > > driver to atomic has significantly impacted cursor performance by
> > > > > making every cursor update wait for vblank.
> > > > > 
> > > > > By skipping the vblank sync when the framebuffer has not changed
> > > > > (as is done in drm_atomic_helper_wait_for_vblanks()) we can avoid
> > > > > this for the common case of moving the cursor and only need to
> > > > > delay the cursor ioctl when the cursor icon changes.
> > > > > 
> > > > > I originally inserted a check on legacy_cursor_update as well, but
> > > > > that caused a storm of iommu page faults.  I didn't investigate the
> > > > > cause of those since this change gives enough of a performance
> > > > > improvement for my use case.
> > > > > 
> > > > > This is RFC because of that and because the framebuffer_changed()
> > > > > function is copied from drm_atomic_helper.c as a quick way to test
> > > > > the result.
> > > > > 
> > > > > Signed-off-by: John Keeping <john at metanate.com>
> > > > > ---
> > > > >  drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 27
> > > > > +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2
> > > > > deletions(-)
> > > > > 
> > > > > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > > > > b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index f784488..8fd9821
> > > > > 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > > > > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > > > > @@ -177,8 +177,28 @@ static void
> > > > > rockchip_crtc_wait_for_update(struct drm_crtc *crtc)
> > > > > crtc_funcs->wait_for_update(crtc); }
> > > > >  
> > > > > +static bool framebuffer_changed(struct drm_device *dev,
> > > > > +				struct drm_atomic_state *old_state,
> > > > > +				struct drm_crtc *crtc)
> > > > > +{
> > > > > +	struct drm_plane *plane;
> > > > > +	struct drm_plane_state *old_plane_state;
> > > > > +	int i;
> > > > > +
> > > > > +	for_each_plane_in_state(old_state, plane, old_plane_state,
> > > > > i) {
> > > > > +		if (plane->state->crtc != crtc &&
> > > > > +		    old_plane_state->crtc != crtc)
> > > > > +			continue;
> > > > > +
> > > > > +		if (plane->state->fb != old_plane_state->fb)
> > > > > +			return true;
> > > > > +	}
> > > > > +
> > > > > +	return false;
> > > > > +}    
> > > > 
> > > > Please don't hand-roll logic that affects semantics like this. Instead
> > > > please use drm_atomic_helper_wait_for_vblanks(), which should do this
> > > > correctly for you.
> > > > 
> > > > If that's not the case then we need to improve the generic helper, or
> > > > figure out what's different with rockhip.  
> > > 
> > > According to commit 63ebb9f (drm/rockchip: Convert to support atomic
> > > API) it's because rockchip doesn't have a hardware vblank counter.
> > > 
> > > I'm not entirely clear on why this prevents the use of
> > > drm_atomic_helper_wait_for_vblanks().  
> > 
> > Hm, that commit isn't terribly helpful. If that's really needed then imo I
> > think we should extract a "drm_atomic_helper_plane_needs_vblank_wait()"
> > helper that's used by both. But since rockchip does vblank_get/put calls
> > I'd hope vblanks actually work correctly. And then the helper should work
> > too.
> 
> I tried switching the call to rockchip_crtc_wait_for_update() to
> drm_atomic_helper_wait_for_vblanks() and it works fine until I switch
> the buffer associated with a cursor, at which point I get iommu page
> faults, presumably because the GEM buffer is unreferenced too early.
> 
> AFAICT the buffer will be released via drm_atomic_state_free()
> unconditionally, but I suspect I'm missing something since that would
> mean every driver would hit a similar problem.

Yeah, with the helper we always skip, which means when the cursor bo
changes you indeed unmap too early. So can't even share the overall
condition, but we could definitely share the little framebuffer_changed
helper. Plus rockchip_crtc_wait_for_update should have a big comment
explaining why we have different rules than core helpers!

Cheers, Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


From john at metanate.com  Wed Jan 13 08:40:38 2016
From: john at metanate.com (John Keeping)
Date: Wed, 13 Jan 2016 16:40:38 +0000
Subject: [RFC/PATCH] drm/rockchip: don't wait for vblank if fb hasn't
 changed
In-Reply-To: <20160113162156.GA19130@phenom.ffwll.local>
References: <1452689614-6570-1-git-send-email-john@metanate.com>
 <20160113142320.GQ19130@phenom.ffwll.local>
 <20160113143425.363ce07a.john@metanate.com>
 <20160113154005.GT19130@phenom.ffwll.local>
 <20160113155529.6048b2c1.john@metanate.com>
 <20160113162156.GA19130@phenom.ffwll.local>
Message-ID: <20160113164038.5fcf670f.john@metanate.com>

On Wed, 13 Jan 2016 17:21:56 +0100, Daniel Vetter wrote:

> On Wed, Jan 13, 2016 at 03:55:29PM +0000, John Keeping wrote:
> > On Wed, 13 Jan 2016 16:40:05 +0100, Daniel Vetter wrote:
> >   
> > > On Wed, Jan 13, 2016 at 02:34:25PM +0000, John Keeping wrote:  
> > > > On Wed, 13 Jan 2016 15:23:20 +0100, Daniel Vetter wrote:
> > > >     
> > > > > On Wed, Jan 13, 2016 at 12:53:34PM +0000, John Keeping wrote:    
> > > > > > As commented in drm_atomic_helper_wait_for_vblanks(), userspace
> > > > > > relies on cursor ioctls being unsynced.  Converting the rockchip
> > > > > > driver to atomic has significantly impacted cursor performance by
> > > > > > making every cursor update wait for vblank.
> > > > > > 
> > > > > > By skipping the vblank sync when the framebuffer has not changed
> > > > > > (as is done in drm_atomic_helper_wait_for_vblanks()) we can avoid
> > > > > > this for the common case of moving the cursor and only need to
> > > > > > delay the cursor ioctl when the cursor icon changes.
> > > > > > 
> > > > > > I originally inserted a check on legacy_cursor_update as well, but
> > > > > > that caused a storm of iommu page faults.  I didn't investigate the
> > > > > > cause of those since this change gives enough of a performance
> > > > > > improvement for my use case.
> > > > > > 
> > > > > > This is RFC because of that and because the framebuffer_changed()
> > > > > > function is copied from drm_atomic_helper.c as a quick way to test
> > > > > > the result.
> > > > > > 
> > > > > > Signed-off-by: John Keeping <john at metanate.com>
> > > > > > ---
> > > > > >  drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 27
> > > > > > +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2
> > > > > > deletions(-)
> > > > > > 
> > > > > > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > > > > > b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index f784488..8fd9821
> > > > > > 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > > > > > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > > > > > @@ -177,8 +177,28 @@ static void
> > > > > > rockchip_crtc_wait_for_update(struct drm_crtc *crtc)
> > > > > > crtc_funcs->wait_for_update(crtc); }
> > > > > >  
> > > > > > +static bool framebuffer_changed(struct drm_device *dev,
> > > > > > +				struct drm_atomic_state *old_state,
> > > > > > +				struct drm_crtc *crtc)
> > > > > > +{
> > > > > > +	struct drm_plane *plane;
> > > > > > +	struct drm_plane_state *old_plane_state;
> > > > > > +	int i;
> > > > > > +
> > > > > > +	for_each_plane_in_state(old_state, plane, old_plane_state,
> > > > > > i) {
> > > > > > +		if (plane->state->crtc != crtc &&
> > > > > > +		    old_plane_state->crtc != crtc)
> > > > > > +			continue;
> > > > > > +
> > > > > > +		if (plane->state->fb != old_plane_state->fb)
> > > > > > +			return true;
> > > > > > +	}
> > > > > > +
> > > > > > +	return false;
> > > > > > +}      
> > > > > 
> > > > > Please don't hand-roll logic that affects semantics like this. Instead
> > > > > please use drm_atomic_helper_wait_for_vblanks(), which should do this
> > > > > correctly for you.
> > > > > 
> > > > > If that's not the case then we need to improve the generic helper, or
> > > > > figure out what's different with rockhip.    
> > > > 
> > > > According to commit 63ebb9f (drm/rockchip: Convert to support atomic
> > > > API) it's because rockchip doesn't have a hardware vblank counter.
> > > > 
> > > > I'm not entirely clear on why this prevents the use of
> > > > drm_atomic_helper_wait_for_vblanks().    
> > > 
> > > Hm, that commit isn't terribly helpful. If that's really needed then imo I
> > > think we should extract a "drm_atomic_helper_plane_needs_vblank_wait()"
> > > helper that's used by both. But since rockchip does vblank_get/put calls
> > > I'd hope vblanks actually work correctly. And then the helper should work
> > > too.  
> > 
> > I tried switching the call to rockchip_crtc_wait_for_update() to
> > drm_atomic_helper_wait_for_vblanks() and it works fine until I switch
> > the buffer associated with a cursor, at which point I get iommu page
> > faults, presumably because the GEM buffer is unreferenced too early.
> > 
> > AFAICT the buffer will be released via drm_atomic_state_free()
> > unconditionally, but I suspect I'm missing something since that would
> > mean every driver would hit a similar problem.  
> 
> Yeah, with the helper we always skip, which means when the cursor bo
> changes you indeed unmap too early. So can't even share the overall
> condition, but we could definitely share the little framebuffer_changed
> helper.

That leaves me with the question: why do other atomic drivers work?

If drm_atomic_helper_wait_for_vblanks() skipping vblanks results in the
cursor bo being unmapped too early for rockchip, why is it not unmapped
too early for all of the other drivers using that helper?


From daniel at ffwll.ch  Wed Jan 13 09:19:17 2016
From: daniel at ffwll.ch (Daniel Vetter)
Date: Wed, 13 Jan 2016 18:19:17 +0100
Subject: [RFC/PATCH] drm/rockchip: don't wait for vblank if fb hasn't
 changed
In-Reply-To: <20160113164038.5fcf670f.john@metanate.com>
References: <1452689614-6570-1-git-send-email-john@metanate.com>
 <20160113142320.GQ19130@phenom.ffwll.local>
 <20160113143425.363ce07a.john@metanate.com>
 <20160113154005.GT19130@phenom.ffwll.local>
 <20160113155529.6048b2c1.john@metanate.com>
 <20160113162156.GA19130@phenom.ffwll.local>
 <20160113164038.5fcf670f.john@metanate.com>
Message-ID: <20160113171917.GC19130@phenom.ffwll.local>

On Wed, Jan 13, 2016 at 04:40:38PM +0000, John Keeping wrote:
> On Wed, 13 Jan 2016 17:21:56 +0100, Daniel Vetter wrote:
> 
> > On Wed, Jan 13, 2016 at 03:55:29PM +0000, John Keeping wrote:
> > > On Wed, 13 Jan 2016 16:40:05 +0100, Daniel Vetter wrote:
> > >   
> > > > On Wed, Jan 13, 2016 at 02:34:25PM +0000, John Keeping wrote:  
> > > > > On Wed, 13 Jan 2016 15:23:20 +0100, Daniel Vetter wrote:
> > > > >     
> > > > > > On Wed, Jan 13, 2016 at 12:53:34PM +0000, John Keeping wrote:    
> > > > > > > As commented in drm_atomic_helper_wait_for_vblanks(), userspace
> > > > > > > relies on cursor ioctls being unsynced.  Converting the rockchip
> > > > > > > driver to atomic has significantly impacted cursor performance by
> > > > > > > making every cursor update wait for vblank.
> > > > > > > 
> > > > > > > By skipping the vblank sync when the framebuffer has not changed
> > > > > > > (as is done in drm_atomic_helper_wait_for_vblanks()) we can avoid
> > > > > > > this for the common case of moving the cursor and only need to
> > > > > > > delay the cursor ioctl when the cursor icon changes.
> > > > > > > 
> > > > > > > I originally inserted a check on legacy_cursor_update as well, but
> > > > > > > that caused a storm of iommu page faults.  I didn't investigate the
> > > > > > > cause of those since this change gives enough of a performance
> > > > > > > improvement for my use case.
> > > > > > > 
> > > > > > > This is RFC because of that and because the framebuffer_changed()
> > > > > > > function is copied from drm_atomic_helper.c as a quick way to test
> > > > > > > the result.
> > > > > > > 
> > > > > > > Signed-off-by: John Keeping <john at metanate.com>
> > > > > > > ---
> > > > > > >  drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 27
> > > > > > > +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2
> > > > > > > deletions(-)
> > > > > > > 
> > > > > > > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > > > > > > b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index f784488..8fd9821
> > > > > > > 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > > > > > > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > > > > > > @@ -177,8 +177,28 @@ static void
> > > > > > > rockchip_crtc_wait_for_update(struct drm_crtc *crtc)
> > > > > > > crtc_funcs->wait_for_update(crtc); }
> > > > > > >  
> > > > > > > +static bool framebuffer_changed(struct drm_device *dev,
> > > > > > > +				struct drm_atomic_state *old_state,
> > > > > > > +				struct drm_crtc *crtc)
> > > > > > > +{
> > > > > > > +	struct drm_plane *plane;
> > > > > > > +	struct drm_plane_state *old_plane_state;
> > > > > > > +	int i;
> > > > > > > +
> > > > > > > +	for_each_plane_in_state(old_state, plane, old_plane_state,
> > > > > > > i) {
> > > > > > > +		if (plane->state->crtc != crtc &&
> > > > > > > +		    old_plane_state->crtc != crtc)
> > > > > > > +			continue;
> > > > > > > +
> > > > > > > +		if (plane->state->fb != old_plane_state->fb)
> > > > > > > +			return true;
> > > > > > > +	}
> > > > > > > +
> > > > > > > +	return false;
> > > > > > > +}      
> > > > > > 
> > > > > > Please don't hand-roll logic that affects semantics like this. Instead
> > > > > > please use drm_atomic_helper_wait_for_vblanks(), which should do this
> > > > > > correctly for you.
> > > > > > 
> > > > > > If that's not the case then we need to improve the generic helper, or
> > > > > > figure out what's different with rockhip.    
> > > > > 
> > > > > According to commit 63ebb9f (drm/rockchip: Convert to support atomic
> > > > > API) it's because rockchip doesn't have a hardware vblank counter.
> > > > > 
> > > > > I'm not entirely clear on why this prevents the use of
> > > > > drm_atomic_helper_wait_for_vblanks().    
> > > > 
> > > > Hm, that commit isn't terribly helpful. If that's really needed then imo I
> > > > think we should extract a "drm_atomic_helper_plane_needs_vblank_wait()"
> > > > helper that's used by both. But since rockchip does vblank_get/put calls
> > > > I'd hope vblanks actually work correctly. And then the helper should work
> > > > too.  
> > > 
> > > I tried switching the call to rockchip_crtc_wait_for_update() to
> > > drm_atomic_helper_wait_for_vblanks() and it works fine until I switch
> > > the buffer associated with a cursor, at which point I get iommu page
> > > faults, presumably because the GEM buffer is unreferenced too early.
> > > 
> > > AFAICT the buffer will be released via drm_atomic_state_free()
> > > unconditionally, but I suspect I'm missing something since that would
> > > mean every driver would hit a similar problem.  
> > 
> > Yeah, with the helper we always skip, which means when the cursor bo
> > changes you indeed unmap too early. So can't even share the overall
> > condition, but we could definitely share the little framebuffer_changed
> > helper.
> 
> That leaves me with the question: why do other atomic drivers work?
> 
> If drm_atomic_helper_wait_for_vblanks() skipping vblanks results in the
> cursor bo being unmapped too early for rockchip, why is it not unmapped
> too early for all of the other drivers using that helper?

It's unmapped too early for everyone, it's just that normally that doesn't
result in a fireworks show. What we maybe could/should do is do the
unmapping asynchronously, but that runs into the overall "current atomic
helpers don't do async yet" problem. Might be a good point to start fixing
this up though.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


From john at metanate.com  Wed Jan 13 09:39:54 2016
From: john at metanate.com (John Keeping)
Date: Wed, 13 Jan 2016 17:39:54 +0000
Subject: [RFC/PATCH] drm/rockchip: don't wait for vblank if fb hasn't
 changed
In-Reply-To: <20160113171917.GC19130@phenom.ffwll.local>
References: <1452689614-6570-1-git-send-email-john@metanate.com>
 <20160113142320.GQ19130@phenom.ffwll.local>
 <20160113143425.363ce07a.john@metanate.com>
 <20160113154005.GT19130@phenom.ffwll.local>
 <20160113155529.6048b2c1.john@metanate.com>
 <20160113162156.GA19130@phenom.ffwll.local>
 <20160113164038.5fcf670f.john@metanate.com>
 <20160113171917.GC19130@phenom.ffwll.local>
Message-ID: <20160113173954.52e688fc.john@metanate.com>

On Wed, 13 Jan 2016 18:19:17 +0100, Daniel Vetter wrote:

> On Wed, Jan 13, 2016 at 04:40:38PM +0000, John Keeping wrote:
> > On Wed, 13 Jan 2016 17:21:56 +0100, Daniel Vetter wrote:
> >   
> > > On Wed, Jan 13, 2016 at 03:55:29PM +0000, John Keeping wrote:  
> > > > On Wed, 13 Jan 2016 16:40:05 +0100, Daniel Vetter wrote:
> > > >     
> > > > > On Wed, Jan 13, 2016 at 02:34:25PM +0000, John Keeping wrote:    
> > > > > > On Wed, 13 Jan 2016 15:23:20 +0100, Daniel Vetter wrote:
> > > > > >       
> > > > > > > On Wed, Jan 13, 2016 at 12:53:34PM +0000, John Keeping wrote:      
> > > > > > > > As commented in drm_atomic_helper_wait_for_vblanks(), userspace
> > > > > > > > relies on cursor ioctls being unsynced.  Converting the rockchip
> > > > > > > > driver to atomic has significantly impacted cursor performance by
> > > > > > > > making every cursor update wait for vblank.
> > > > > > > > 
> > > > > > > > By skipping the vblank sync when the framebuffer has not changed
> > > > > > > > (as is done in drm_atomic_helper_wait_for_vblanks()) we can avoid
> > > > > > > > this for the common case of moving the cursor and only need to
> > > > > > > > delay the cursor ioctl when the cursor icon changes.
> > > > > > > > 
> > > > > > > > I originally inserted a check on legacy_cursor_update as well, but
> > > > > > > > that caused a storm of iommu page faults.  I didn't investigate the
> > > > > > > > cause of those since this change gives enough of a performance
> > > > > > > > improvement for my use case.
> > > > > > > > 
> > > > > > > > This is RFC because of that and because the framebuffer_changed()
> > > > > > > > function is copied from drm_atomic_helper.c as a quick way to test
> > > > > > > > the result.
> > > > > > > > 
> > > > > > > > Signed-off-by: John Keeping <john at metanate.com>
> > > > > > > > ---
> > > > > > > >  drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 27
> > > > > > > > +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2
> > > > > > > > deletions(-)
> > > > > > > > 
> > > > > > > > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > > > > > > > b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index f784488..8fd9821
> > > > > > > > 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > > > > > > > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > > > > > > > @@ -177,8 +177,28 @@ static void
> > > > > > > > rockchip_crtc_wait_for_update(struct drm_crtc *crtc)
> > > > > > > > crtc_funcs->wait_for_update(crtc); }
> > > > > > > >  
> > > > > > > > +static bool framebuffer_changed(struct drm_device *dev,
> > > > > > > > +				struct drm_atomic_state *old_state,
> > > > > > > > +				struct drm_crtc *crtc)
> > > > > > > > +{
> > > > > > > > +	struct drm_plane *plane;
> > > > > > > > +	struct drm_plane_state *old_plane_state;
> > > > > > > > +	int i;
> > > > > > > > +
> > > > > > > > +	for_each_plane_in_state(old_state, plane, old_plane_state,
> > > > > > > > i) {
> > > > > > > > +		if (plane->state->crtc != crtc &&
> > > > > > > > +		    old_plane_state->crtc != crtc)
> > > > > > > > +			continue;
> > > > > > > > +
> > > > > > > > +		if (plane->state->fb != old_plane_state->fb)
> > > > > > > > +			return true;
> > > > > > > > +	}
> > > > > > > > +
> > > > > > > > +	return false;
> > > > > > > > +}        
> > > > > > > 
> > > > > > > Please don't hand-roll logic that affects semantics like this. Instead
> > > > > > > please use drm_atomic_helper_wait_for_vblanks(), which should do this
> > > > > > > correctly for you.
> > > > > > > 
> > > > > > > If that's not the case then we need to improve the generic helper, or
> > > > > > > figure out what's different with rockhip.      
> > > > > > 
> > > > > > According to commit 63ebb9f (drm/rockchip: Convert to support atomic
> > > > > > API) it's because rockchip doesn't have a hardware vblank counter.
> > > > > > 
> > > > > > I'm not entirely clear on why this prevents the use of
> > > > > > drm_atomic_helper_wait_for_vblanks().      
> > > > > 
> > > > > Hm, that commit isn't terribly helpful. If that's really needed then imo I
> > > > > think we should extract a "drm_atomic_helper_plane_needs_vblank_wait()"
> > > > > helper that's used by both. But since rockchip does vblank_get/put calls
> > > > > I'd hope vblanks actually work correctly. And then the helper should work
> > > > > too.    
> > > > 
> > > > I tried switching the call to rockchip_crtc_wait_for_update() to
> > > > drm_atomic_helper_wait_for_vblanks() and it works fine until I switch
> > > > the buffer associated with a cursor, at which point I get iommu page
> > > > faults, presumably because the GEM buffer is unreferenced too early.
> > > > 
> > > > AFAICT the buffer will be released via drm_atomic_state_free()
> > > > unconditionally, but I suspect I'm missing something since that would
> > > > mean every driver would hit a similar problem.    
> > > 
> > > Yeah, with the helper we always skip, which means when the cursor bo
> > > changes you indeed unmap too early. So can't even share the overall
> > > condition, but we could definitely share the little framebuffer_changed
> > > helper.  
> > 
> > That leaves me with the question: why do other atomic drivers work?
> > 
> > If drm_atomic_helper_wait_for_vblanks() skipping vblanks results in the
> > cursor bo being unmapped too early for rockchip, why is it not unmapped
> > too early for all of the other drivers using that helper?  
> 
> It's unmapped too early for everyone, it's just that normally that doesn't
> result in a fireworks show. What we maybe could/should do is do the
> unmapping asynchronously, but that runs into the overall "current atomic
> helpers don't do async yet" problem. Might be a good point to start fixing
> this up though.

OK, thanks, I think I'm beginning to understand how this all fits
together.

It looks like there are two options for me to get reasonable cursor
performance on rockchip in the short term:

1) Export the current framebuffer_changed() function as
   drm_atomic_helper_framebuffer_changed() and use it in
   rockchip_crtc_wait_for_update().

2) Add a mechanism to suppress the legacy_cursor_update check in
   drm_atomic_helper_wait_for_vblanks() and switch the rockchip driver
   over to it.

In both of these cases we're only restoring the unsynced cursor ioctls
behaviour when the cursor is moved but it will still be expensive when
the cursor bo changes.  That gives sufficient performance in my testing.


From sjg at chromium.org  Wed Jan 13 12:10:35 2016
From: sjg at chromium.org (Simon Glass)
Date: Wed, 13 Jan 2016 13:10:35 -0700
Subject: [PATCH v3 6/6] rockchip: kylin: Check fastboot request
In-Reply-To: <1452650360-24149-7-git-send-email-jeffy.chen@rock-chips.com>
References: <1452650360-24149-1-git-send-email-jeffy.chen@rock-chips.com>
 <1452650360-24149-7-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <CAPnjgZ1pQ4nvwj+6WgrL+tb2bGPyJJHsNC7S-AnkEyWYp4y4Bw@mail.gmail.com>

Hi Jeffy,

On 12 January 2016 at 18:59, Jeffy Chen <jeffy.chen at rock-chips.com> wrote:
> We will save boot mode flag in grf's os_reg[0], if fastboot
> requested or fastboot key pressed, try to enter fastboot mode
> at preboot stage.
>
> Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
>
> ---
>
> Changes in v3:
> Use rockchip's legacy reboot mode definitions.
>
> Changes in v2: None
>
>  board/kylin/kylin_rk3036/kylin_rk3036.c | 46 +++++++++++++++++++++++++++++++++
>  include/configs/kylin_rk3036.h          |  3 +++
>  2 files changed, 49 insertions(+)
>
> diff --git a/board/kylin/kylin_rk3036/kylin_rk3036.c b/board/kylin/kylin_rk3036/kylin_rk3036.c
> index 40d6b52..4fc31b1 100644
> --- a/board/kylin/kylin_rk3036/kylin_rk3036.c
> +++ b/board/kylin/kylin_rk3036/kylin_rk3036.c
> @@ -8,10 +8,15 @@
>  #include <dm.h>
>  #include <asm/io.h>
>  #include <asm/arch/uart.h>
> +#include <asm/arch-rockchip/grf_rk3036.h>
>  #include <asm/arch/sdram_rk3036.h>
> +#include <asm/gpio.h>
>
>  DECLARE_GLOBAL_DATA_PTR;
>
> +#define GRF_BASE       0x20008000
> +static struct rk3036_grf * const grf = (void *)GRF_BASE;

Can you make this a local var in board_late_init()? I does not seem to
need to be static.

> +
>  void get_ddr_config(struct rk3036_ddr_config *config)
>  {
>         /* K4B4G1646Q config */
> @@ -28,6 +33,47 @@ void get_ddr_config(struct rk3036_ddr_config *config)
>         config->bw = 1;
>  }
>
> +#define FASTBOOT_KEY_GPIO 93
> +
> +int fastboot_key_pressed(void)
> +{
> +       gpio_request(FASTBOOT_KEY_GPIO, "fastboot_key");
> +       gpio_direction_input(FASTBOOT_KEY_GPIO);
> +       return !gpio_get_value(FASTBOOT_KEY_GPIO);
> +}
> +
> +enum rockchip_boot_mode {
> +       ROCKCHIP_BOOT_MODE_NORMAL = 0x5242C300,
> +       ROCKCHIP_BOOT_MODE_LOADER,
> +       ROCKCHIP_BOOT_MODE_MASKROM,
> +       ROCKCHIP_BOOT_MODE_RECOVER,
> +       ROCKCHIP_BOOT_MODE_NORECOVER,
> +       ROCKCHIP_BOOT_MODE_SECONDOS,
> +       ROCKCHIP_BOOT_MODE_WIPEDATA,
> +       ROCKCHIP_BOOT_MODE_WIPEALL,
> +       ROCKCHIP_BOOT_MODE_CHECKIMG,
> +       ROCKCHIP_BOOT_MODE_FASTBOOT,
> +       ROCKCHIP_BOOT_MODE_SECUREBOOT_DISABLE,
> +       ROCKCHIP_BOOT_MODE_CHARGING,
> +       ROCKCHIP_BOOT_MODE_MAX,
> +};
> +
> +int board_late_init(void)
> +{
> +       int boot_mode = readl(&grf->os_reg[4]);
> +
> +       /* Clear boot mode */
> +       writel(0, &grf->os_reg[4]);
> +
> +       if (boot_mode == ROCKCHIP_BOOT_MODE_FASTBOOT ||
> +           fastboot_key_pressed()) {
> +               printf("enter fastboot!\n");
> +               setenv("preboot", "setenv preboot; fastboot usb0");
> +       }
> +
> +       return 0;
> +}
> +
>  int board_init(void)
>  {
>         return 0;
> diff --git a/include/configs/kylin_rk3036.h b/include/configs/kylin_rk3036.h
> index 49997ec..424e81b 100644
> --- a/include/configs/kylin_rk3036.h
> +++ b/include/configs/kylin_rk3036.h
> @@ -39,6 +39,9 @@
>         "mmcpart=5\0" \
>         "loadaddr=" __stringify(CONFIG_SYS_LOAD_ADDR) "\0" \
>
> +#define CONFIG_BOARD_LATE_INIT
> +#define CONFIG_PREBOOT
> +
>  #define CONFIG_ANDROID_BOOT_IMAGE
>  #define CONFIG_SYS_BOOT_RAMDISK_HIGH
>  #define CONFIG_SYS_HUSH_PARSER
> --
> 2.1.4
>
>

Regards,
Simon


From wxt at rock-chips.com  Wed Jan 13 17:08:41 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Thu, 14 Jan 2016 09:08:41 +0800
Subject: [PATCH] ARM: dts: rockchip: make more compatible for rk3036 SoCs
Message-ID: <1452733721-28110-1-git-send-email-wxt@rock-chips.com>

This patch will be better to support and compatible for rk3036.
Although some other SoCs drivers also can work for rk3036, that will
prevent some mutatin factors for drivers.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
---

 arch/arm/boot/dts/rk3036.dtsi | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
index ee457a2..7897449 100644
--- a/arch/arm/boot/dts/rk3036.dtsi
+++ b/arch/arm/boot/dts/rk3036.dtsi
@@ -161,7 +161,7 @@
 	};
 
 	usb_otg: usb at 10180000 {
-		compatible = "rockchip,rk3288-usb", "rockchip,rk3066-usb",
+		compatible = "rockchip,rk3036-usb", "rockchip,rk3066-usb",
 				"snps,dwc2";
 		reg = <0x10180000 0x40000>;
 		interrupts = <GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>;
@@ -176,7 +176,7 @@
 	};
 
 	usb_host: usb at 101c0000 {
-		compatible = "rockchip,rk3288-usb", "rockchip,rk3066-usb",
+		compatible = "rockchip,rk3036-usb", "rockchip,rk3066-usb",
 				"snps,dwc2";
 		reg = <0x101c0000 0x40000>;
 		interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
@@ -211,7 +211,7 @@
 	};
 
 	emmc: dwmmc at 1021c000 {
-		compatible = "rockchip,rk3288-dw-mshc";
+		compatible = "rockchip,rk3036-dw-mshc", "rockchip,rk3288-dw-mshc";
 		reg = <0x1021c000 0x4000>;
 		interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
 		broken-cd;
@@ -327,7 +327,7 @@
 	};
 
 	i2c1: i2c at 20056000 {
-		compatible = "rockchip,rk3288-i2c";
+		compatible = "rockchip,rk3036-i2c", "rockchip,rk3288-i2c";
 		reg = <0x20056000 0x1000>;
 		interrupts = <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>;
 		#address-cells = <1>;
@@ -340,7 +340,7 @@
 	};
 
 	i2c2: i2c at 2005a000 {
-		compatible = "rockchip,rk3288-i2c";
+		compatible = "rockchip,rk3036-i2c", "rockchip,rk3288-i2c";
 		reg = <0x2005a000 0x1000>;
 		interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
 		#address-cells = <1>;
@@ -395,7 +395,7 @@
 	};
 
 	i2c0: i2c at 20072000 {
-		compatible = "rockchip,rk3288-i2c";
+		compatible = "rockchip,rk3036-i2c", "rockchip,rk3288-i2c";
 		reg = <0x20072000 0x1000>;
 		interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>;
 		#address-cells = <1>;
-- 
1.9.1



From mark.yao at rock-chips.com  Wed Jan 13 17:16:36 2016
From: mark.yao at rock-chips.com (Mark yao)
Date: Thu, 14 Jan 2016 09:16:36 +0800
Subject: [RFC/PATCH] drm/rockchip: don't wait for vblank if fb hasn't
 changed
In-Reply-To: <20160113173954.52e688fc.john@metanate.com>
References: <1452689614-6570-1-git-send-email-john@metanate.com>	<20160113142320.GQ19130@phenom.ffwll.local>	<20160113143425.363ce07a.john@metanate.com>	<20160113154005.GT19130@phenom.ffwll.local>	<20160113155529.6048b2c1.john@metanate.com>	<20160113162156.GA19130@phenom.ffwll.local>	<20160113164038.5fcf670f.john@metanate.com>	<20160113171917.GC19130@phenom.ffwll.local>
 <20160113173954.52e688fc.john@metanate.com>
Message-ID: <5696F6F4.5060908@rock-chips.com>

On 2016?01?14? 01:39, John Keeping wrote:
> On Wed, 13 Jan 2016 18:19:17 +0100, Daniel Vetter wrote:
>
>> On Wed, Jan 13, 2016 at 04:40:38PM +0000, John Keeping wrote:
>>> On Wed, 13 Jan 2016 17:21:56 +0100, Daniel Vetter wrote:
>>>    
>>>> On Wed, Jan 13, 2016 at 03:55:29PM +0000, John Keeping wrote:
>>>>> On Wed, 13 Jan 2016 16:40:05 +0100, Daniel Vetter wrote:
>>>>>      
>>>>>> On Wed, Jan 13, 2016 at 02:34:25PM +0000, John Keeping wrote:
>>>>>>> On Wed, 13 Jan 2016 15:23:20 +0100, Daniel Vetter wrote:
>>>>>>>        
>>>>>>>> On Wed, Jan 13, 2016 at 12:53:34PM +0000, John Keeping wrote:
>>>>>>>>> As commented in drm_atomic_helper_wait_for_vblanks(), userspace
>>>>>>>>> relies on cursor ioctls being unsynced.  Converting the rockchip
>>>>>>>>> driver to atomic has significantly impacted cursor performance by
>>>>>>>>> making every cursor update wait for vblank.
>>>>>>>>>
>>>>>>>>> By skipping the vblank sync when the framebuffer has not changed
>>>>>>>>> (as is done in drm_atomic_helper_wait_for_vblanks()) we can avoid
>>>>>>>>> this for the common case of moving the cursor and only need to
>>>>>>>>> delay the cursor ioctl when the cursor icon changes.
>>>>>>>>>
>>>>>>>>> I originally inserted a check on legacy_cursor_update as well, but
>>>>>>>>> that caused a storm of iommu page faults.  I didn't investigate the
>>>>>>>>> cause of those since this change gives enough of a performance
>>>>>>>>> improvement for my use case.
>>>>>>>>>
>>>>>>>>> This is RFC because of that and because the framebuffer_changed()
>>>>>>>>> function is copied from drm_atomic_helper.c as a quick way to test
>>>>>>>>> the result.
>>>>>>>>>
>>>>>>>>> Signed-off-by: John Keeping <john at metanate.com>
>>>>>>>>> ---
>>>>>>>>>   drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 27
>>>>>>>>> +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2
>>>>>>>>> deletions(-)
>>>>>>>>>
>>>>>>>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>>>>>>>>> b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index f784488..8fd9821
>>>>>>>>> 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>>>>>>>>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>>>>>>>>> @@ -177,8 +177,28 @@ static void
>>>>>>>>> rockchip_crtc_wait_for_update(struct drm_crtc *crtc)
>>>>>>>>> crtc_funcs->wait_for_update(crtc); }
>>>>>>>>>   
>>>>>>>>> +static bool framebuffer_changed(struct drm_device *dev,
>>>>>>>>> +				struct drm_atomic_state *old_state,
>>>>>>>>> +				struct drm_crtc *crtc)
>>>>>>>>> +{
>>>>>>>>> +	struct drm_plane *plane;
>>>>>>>>> +	struct drm_plane_state *old_plane_state;
>>>>>>>>> +	int i;
>>>>>>>>> +
>>>>>>>>> +	for_each_plane_in_state(old_state, plane, old_plane_state,
>>>>>>>>> i) {
>>>>>>>>> +		if (plane->state->crtc != crtc &&
>>>>>>>>> +		    old_plane_state->crtc != crtc)
>>>>>>>>> +			continue;
>>>>>>>>> +
>>>>>>>>> +		if (plane->state->fb != old_plane_state->fb)
>>>>>>>>> +			return true;
>>>>>>>>> +	}
>>>>>>>>> +
>>>>>>>>> +	return false;
>>>>>>>>> +}
>>>>>>>> Please don't hand-roll logic that affects semantics like this. Instead
>>>>>>>> please use drm_atomic_helper_wait_for_vblanks(), which should do this
>>>>>>>> correctly for you.
>>>>>>>>
>>>>>>>> If that's not the case then we need to improve the generic helper, or
>>>>>>>> figure out what's different with rockhip.
>>>>>>> According to commit 63ebb9f (drm/rockchip: Convert to support atomic
>>>>>>> API) it's because rockchip doesn't have a hardware vblank counter.
>>>>>>>
>>>>>>> I'm not entirely clear on why this prevents the use of
>>>>>>> drm_atomic_helper_wait_for_vblanks().
>>>>>> Hm, that commit isn't terribly helpful. If that's really needed then imo I
>>>>>> think we should extract a "drm_atomic_helper_plane_needs_vblank_wait()"
>>>>>> helper that's used by both. But since rockchip does vblank_get/put calls
>>>>>> I'd hope vblanks actually work correctly. And then the helper should work
>>>>>> too.
>>>>> I tried switching the call to rockchip_crtc_wait_for_update() to
>>>>> drm_atomic_helper_wait_for_vblanks() and it works fine until I switch
>>>>> the buffer associated with a cursor, at which point I get iommu page
>>>>> faults, presumably because the GEM buffer is unreferenced too early.
>>>>>
>>>>> AFAICT the buffer will be released via drm_atomic_state_free()
>>>>> unconditionally, but I suspect I'm missing something since that would
>>>>> mean every driver would hit a similar problem.
>>>> Yeah, with the helper we always skip, which means when the cursor bo
>>>> changes you indeed unmap too early. So can't even share the overall
>>>> condition, but we could definitely share the little framebuffer_changed
>>>> helper.
>>> That leaves me with the question: why do other atomic drivers work?
>>>
>>> If drm_atomic_helper_wait_for_vblanks() skipping vblanks results in the
>>> cursor bo being unmapped too early for rockchip, why is it not unmapped
>>> too early for all of the other drivers using that helper?
>> It's unmapped too early for everyone, it's just that normally that doesn't
>> result in a fireworks show. What we maybe could/should do is do the
>> unmapping asynchronously, but that runs into the overall "current atomic
>> helpers don't do async yet" problem. Might be a good point to start fixing
>> this up though.
> OK, thanks, I think I'm beginning to understand how this all fits
> together.
>
> It looks like there are two options for me to get reasonable cursor
> performance on rockchip in the short term:
>
> 1) Export the current framebuffer_changed() function as
>     drm_atomic_helper_framebuffer_changed() and use it in
>     rockchip_crtc_wait_for_update().
>
> 2) Add a mechanism to suppress the legacy_cursor_update check in
>     drm_atomic_helper_wait_for_vblanks() and switch the rockchip driver
>     over to it.
>
> In both of these cases we're only restoring the unsynced cursor ioctls
> behaviour when the cursor is moved but it will still be expensive when
> the cursor bo changes.  That gives sufficient performance in my testing.
>
>
>

Thanks for point that.

because rockchip not support hardware vblank counter, use 
drm_atomic_helper_wait_for_vblanks have under issues:

                                              | <-- HW vsync irq and reg 
take effect
             plane_commit  --->  |
      get_vblank and wait ->   |
                                              | <-- handle_vblank, 
vblank->count + 1
                  cleanup_fb   ---> |
               iommu crash  --->  |
                                              | <-- HW vsync irq and reg 
take effect
there is no hardware vblank counter on rockchip vop, we can't ensure the 
consistency of reg take effect and vblank->count,
if plane commit hit into the period of  reg take effect and 
vblank->count, cleanup_fb happen before old_fb swap out from vop,
then iommu crash.

That is why I special the wait_for_vblanks, we need check the reg really 
take effect before clean up old fb.
at vop_win_pending_is_complete function, check win enable and win 
address, to ensure that.

Not only rockchip drm do that thing:

exynos also check address before cleanup fb
         if (start == start_s)
             exynos_drm_crtc_finish_update(ctx->crtc, plane);

Thanks.

-- ?ark Yao



From jeffy.chen at rock-chips.com  Wed Jan 13 17:47:12 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Thu, 14 Jan 2016 09:47:12 +0800
Subject: [U-Boot] [PATCH v4 1/6] common/image-fdt.c: Make boot_get_fdt()
 perform a check for Android images
In-Reply-To: <20160113152157.GY3359@bill-the-cat>
References: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
 <1452675200-15941-2-git-send-email-jeffy.chen@rock-chips.com>
 <20160113152157.GY3359@bill-the-cat>
Message-ID: <5696FE20.6080003@rock-chips.com>

Hi Tom,

On 2016-1-13 23:21, Tom Rini wrote:
> On Wed, Jan 13, 2016 at 04:53:15PM +0800, Jeffy Chen wrote:
>
>> Android images don't have a fdt.
>>
>> Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
>> Acked-by: Simon Glass <sjg at chromium.org>
>> ---
>>
>> Changes in v4: None
>> Changes in v3: None
>> Changes in v2: None
>>
>>   common/image-fdt.c | 4 ++++
>>   1 file changed, 4 insertions(+)
>>
>> diff --git a/common/image-fdt.c b/common/image-fdt.c
>> index 5e4e5bd..41aaa0d 100644
>> --- a/common/image-fdt.c
>> +++ b/common/image-fdt.c
>> @@ -379,6 +379,10 @@ int boot_get_fdt(int flag, int argc, char * const argv[], uint8_t arch,
>>   				       (long)fdt_addr);
>>   			}
>>   			break;
>> +#ifdef CONFIG_ANDROID_BOOT_IMAGE
>> +		case IMAGE_FORMAT_ANDROID:
>> +			goto no_fdt;
>> +#endif
>>   		default:
>>   			puts("ERROR: Did not find a cmdline Flattened Device Tree\n");
>>   			goto no_fdt;
> Hang on, this doesn't seem right.  Rob or Paul can you comment more
> here?  I know we've gone through some issues in the past with respect to
> booting Android and FDT.  Thanks!
Oh! How careless i was...Seems we don't need this patch, the default 
case is enough :)



From jeffy.chen at rock-chips.com  Wed Jan 13 18:31:34 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Thu, 14 Jan 2016 10:31:34 +0800
Subject: [U-Boot] [PATCH v4 5/6] rockchip: kylin: Enable boot with android
 boot image
In-Reply-To: <20160113152811.GC3359@bill-the-cat>
References: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
 <1452675200-15941-6-git-send-email-jeffy.chen@rock-chips.com>
 <20160113152811.GC3359@bill-the-cat>
Message-ID: <56970886.2040009@rock-chips.com>

Hi Tom,

On 2016-1-13 23:28, Tom Rini wrote:
> On Wed, Jan 13, 2016 at 04:53:19PM +0800, Jeffy Chen wrote:
>
>> The android kernel is using appended dtb by default, and store
>> ramdisk right after kernel & dtb.
>> So we needs to relocate ramdisk, and use atags to pass params.
>>
>> Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
>> Acked-by: Simon Glass <sjg at chromium.org>
>> ---
>>
>> Changes in v4: None
>> Changes in v3: None
>> Changes in v2: None
>>
>>   include/configs/kylin_rk3036.h | 23 +++++++++++++++++++++++
>>   1 file changed, 23 insertions(+)
>>
>> diff --git a/include/configs/kylin_rk3036.h b/include/configs/kylin_rk3036.h
>> index b750b26..49997ec 100644
>> --- a/include/configs/kylin_rk3036.h
>> +++ b/include/configs/kylin_rk3036.h
>> @@ -35,6 +35,29 @@
>>   #undef CONFIG_EXTRA_ENV_SETTINGS
>>   #define CONFIG_EXTRA_ENV_SETTINGS \
>>   	"partitions=" PARTS_DEFAULT \
>> +	"mmcdev=0\0" \
>> +	"mmcpart=5\0" \
>> +	"loadaddr=" __stringify(CONFIG_SYS_LOAD_ADDR) "\0" \
>> +
>> +#define CONFIG_ANDROID_BOOT_IMAGE
>> +#define CONFIG_SYS_BOOT_RAMDISK_HIGH
> This should already be set.
Right, i'll remove it...
>> +#define CONFIG_SYS_HUSH_PARSER
>> +
>> +#undef CONFIG_BOOTCOMMAND
>> +#define CONFIG_BOOTCOMMAND \
>> +	"mmc dev ${mmcdev}; if mmc rescan; then " \
>> +		"part start mmc ${mmcdev} ${mmcpart} boot_start;" \
>> +		"part size mmc ${mmcdev} ${mmcpart} boot_size;" \
>> +		"mmc read ${loadaddr} ${boot_start} ${boot_size};" \
>> +		"bootm start ${loadaddr}; bootm ramdisk;" \
>> +		"bootm prep; bootm go;" \
>> +	"fi;" \
>> +
>> +/* Enable atags */
>> +#define CONFIG_SYS_BOOTPARAMS_LEN	(64*1024)
>> +#define CONFIG_INITRD_TAG
>> +#define CONFIG_SETUP_MEMORY_TAGS
>> +#define CONFIG_CMDLINE_TAG
> But I'm confused as to what exactly is going on here.  Appended dtb is
> not the same as ATAGS.  And you shouldn't need to split up bootm like
> that.  Can you please explain a bit more?  Thanks!
The u-boot will pass atags to kernel, and kernel will merge those atags 
into the appended dtb(fdt).

The default bootm flow would not pass ramdisk state, but we need it, so 
we should add this state into default flow, or just use split bootm cmds :)



From wxt at rock-chips.com  Wed Jan 13 18:55:24 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Thu, 14 Jan 2016 10:55:24 +0800
Subject: [PATCH v2] ARM: dts: rockchip: add reboot-mode node for rk3036 SoCs
In-Reply-To: <1452650930-22883-1-git-send-email-wxt@rock-chips.com>
References: <1452650930-22883-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1452740124-32558-1-git-send-email-wxt@rock-chips.com>

Add reboot mode driver DT node for rk3036 platform.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

 arch/arm/boot/dts/rk3036.dtsi | 28 +++++++++++++++++++++++++++-
 1 file changed, 27 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
index ccc69ab..74e3bc1 100644
--- a/arch/arm/boot/dts/rk3036.dtsi
+++ b/arch/arm/boot/dts/rk3036.dtsi
@@ -43,6 +43,7 @@
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 #include <dt-bindings/pinctrl/rockchip.h>
 #include <dt-bindings/clock/rk3036-cru.h>
+#include <dt-bindings/soc/rockchip_boot-mode.h>
 #include "skeleton.dtsi"
 
 / {
@@ -307,8 +308,33 @@
 	};
 
 	grf: syscon at 20008000 {
-		compatible = "rockchip,rk3036-grf", "syscon";
+		compatible = "rockchip,rk3036-grf", "syscon", "simple-mfd";
 		reg = <0x20008000 0x1000>;
+
+		reboot-mode {
+			compatible = "syscon-reboot-mode";
+			offset = <0x1d8>;
+
+			normal {
+				linux,mode = "normal";
+				loader,magic = <BOOT_NORMAL>;
+			};
+
+			loader {
+				linux,mode = "loader";
+				loader,magic = <BOOT_LOADER>;
+			};
+
+			recovery {
+				linux,mode = "recovery";
+				loader,magic = <BOOT_RECOVERY>;
+			};
+
+			fastboot {
+				linux,mode = "fastboot";
+				loader,magic = <BOOT_FASTBOOT>;
+			};
+		};
 	};
 
 	acodec: acodec-ana at 20030000 {
-- 
1.9.1



From daniel at ffwll.ch  Thu Jan 14 00:32:25 2016
From: daniel at ffwll.ch (Daniel Vetter)
Date: Thu, 14 Jan 2016 09:32:25 +0100
Subject: [RFC/PATCH] drm/rockchip: don't wait for vblank if fb hasn't
 changed
In-Reply-To: <5696F6F4.5060908@rock-chips.com>
References: <1452689614-6570-1-git-send-email-john@metanate.com>
 <20160113142320.GQ19130@phenom.ffwll.local>
 <20160113143425.363ce07a.john@metanate.com>
 <20160113154005.GT19130@phenom.ffwll.local>
 <20160113155529.6048b2c1.john@metanate.com>
 <20160113162156.GA19130@phenom.ffwll.local>
 <20160113164038.5fcf670f.john@metanate.com>
 <20160113171917.GC19130@phenom.ffwll.local>
 <20160113173954.52e688fc.john@metanate.com>
 <5696F6F4.5060908@rock-chips.com>
Message-ID: <CAKMK7uGQ47eLS2HxUP2+tQcf5cZm+eoGryCRrBzA2b8=77ox-Q@mail.gmail.com>

On Thu, Jan 14, 2016 at 2:16 AM, Mark yao <mark.yao at rock-chips.com> wrote:
> On 2016?01?14? 01:39, John Keeping wrote:
>>
>> On Wed, 13 Jan 2016 18:19:17 +0100, Daniel Vetter wrote:
>>
>>> On Wed, Jan 13, 2016 at 04:40:38PM +0000, John Keeping wrote:
>>>>
>>>> On Wed, 13 Jan 2016 17:21:56 +0100, Daniel Vetter wrote:
>>>>
>>>>>
>>>>> On Wed, Jan 13, 2016 at 03:55:29PM +0000, John Keeping wrote:
>>>>>>
>>>>>> On Wed, 13 Jan 2016 16:40:05 +0100, Daniel Vetter wrote:
>>>>>>
>>>>>>>
>>>>>>> On Wed, Jan 13, 2016 at 02:34:25PM +0000, John Keeping wrote:
>>>>>>>>
>>>>>>>> On Wed, 13 Jan 2016 15:23:20 +0100, Daniel Vetter wrote:
>>>>>>>>
>>>>>>>>>
>>>>>>>>> On Wed, Jan 13, 2016 at 12:53:34PM +0000, John Keeping wrote:
>>>>>>>>>>
>>>>>>>>>> As commented in drm_atomic_helper_wait_for_vblanks(), userspace
>>>>>>>>>> relies on cursor ioctls being unsynced.  Converting the rockchip
>>>>>>>>>> driver to atomic has significantly impacted cursor performance by
>>>>>>>>>> making every cursor update wait for vblank.
>>>>>>>>>>
>>>>>>>>>> By skipping the vblank sync when the framebuffer has not changed
>>>>>>>>>> (as is done in drm_atomic_helper_wait_for_vblanks()) we can avoid
>>>>>>>>>> this for the common case of moving the cursor and only need to
>>>>>>>>>> delay the cursor ioctl when the cursor icon changes.
>>>>>>>>>>
>>>>>>>>>> I originally inserted a check on legacy_cursor_update as well, but
>>>>>>>>>> that caused a storm of iommu page faults.  I didn't investigate
>>>>>>>>>> the
>>>>>>>>>> cause of those since this change gives enough of a performance
>>>>>>>>>> improvement for my use case.
>>>>>>>>>>
>>>>>>>>>> This is RFC because of that and because the framebuffer_changed()
>>>>>>>>>> function is copied from drm_atomic_helper.c as a quick way to test
>>>>>>>>>> the result.
>>>>>>>>>>
>>>>>>>>>> Signed-off-by: John Keeping <john at metanate.com>
>>>>>>>>>> ---
>>>>>>>>>>   drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 27
>>>>>>>>>> +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2
>>>>>>>>>> deletions(-)
>>>>>>>>>>
>>>>>>>>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>>>>>>>>>> b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index
>>>>>>>>>> f784488..8fd9821
>>>>>>>>>> 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>>>>>>>>>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>>>>>>>>>> @@ -177,8 +177,28 @@ static void
>>>>>>>>>> rockchip_crtc_wait_for_update(struct drm_crtc *crtc)
>>>>>>>>>> crtc_funcs->wait_for_update(crtc); }
>>>>>>>>>>   +static bool framebuffer_changed(struct drm_device *dev,
>>>>>>>>>> +                               struct drm_atomic_state
>>>>>>>>>> *old_state,
>>>>>>>>>> +                               struct drm_crtc *crtc)
>>>>>>>>>> +{
>>>>>>>>>> +       struct drm_plane *plane;
>>>>>>>>>> +       struct drm_plane_state *old_plane_state;
>>>>>>>>>> +       int i;
>>>>>>>>>> +
>>>>>>>>>> +       for_each_plane_in_state(old_state, plane, old_plane_state,
>>>>>>>>>> i) {
>>>>>>>>>> +               if (plane->state->crtc != crtc &&
>>>>>>>>>> +                   old_plane_state->crtc != crtc)
>>>>>>>>>> +                       continue;
>>>>>>>>>> +
>>>>>>>>>> +               if (plane->state->fb != old_plane_state->fb)
>>>>>>>>>> +                       return true;
>>>>>>>>>> +       }
>>>>>>>>>> +
>>>>>>>>>> +       return false;
>>>>>>>>>> +}
>>>>>>>>>
>>>>>>>>> Please don't hand-roll logic that affects semantics like this.
>>>>>>>>> Instead
>>>>>>>>> please use drm_atomic_helper_wait_for_vblanks(), which should do
>>>>>>>>> this
>>>>>>>>> correctly for you.
>>>>>>>>>
>>>>>>>>> If that's not the case then we need to improve the generic helper,
>>>>>>>>> or
>>>>>>>>> figure out what's different with rockhip.
>>>>>>>>
>>>>>>>> According to commit 63ebb9f (drm/rockchip: Convert to support atomic
>>>>>>>> API) it's because rockchip doesn't have a hardware vblank counter.
>>>>>>>>
>>>>>>>> I'm not entirely clear on why this prevents the use of
>>>>>>>> drm_atomic_helper_wait_for_vblanks().
>>>>>>>
>>>>>>> Hm, that commit isn't terribly helpful. If that's really needed then
>>>>>>> imo I
>>>>>>> think we should extract a
>>>>>>> "drm_atomic_helper_plane_needs_vblank_wait()"
>>>>>>> helper that's used by both. But since rockchip does vblank_get/put
>>>>>>> calls
>>>>>>> I'd hope vblanks actually work correctly. And then the helper should
>>>>>>> work
>>>>>>> too.
>>>>>>
>>>>>> I tried switching the call to rockchip_crtc_wait_for_update() to
>>>>>> drm_atomic_helper_wait_for_vblanks() and it works fine until I switch
>>>>>> the buffer associated with a cursor, at which point I get iommu page
>>>>>> faults, presumably because the GEM buffer is unreferenced too early.
>>>>>>
>>>>>> AFAICT the buffer will be released via drm_atomic_state_free()
>>>>>> unconditionally, but I suspect I'm missing something since that would
>>>>>> mean every driver would hit a similar problem.
>>>>>
>>>>> Yeah, with the helper we always skip, which means when the cursor bo
>>>>> changes you indeed unmap too early. So can't even share the overall
>>>>> condition, but we could definitely share the little framebuffer_changed
>>>>> helper.
>>>>
>>>> That leaves me with the question: why do other atomic drivers work?
>>>>
>>>> If drm_atomic_helper_wait_for_vblanks() skipping vblanks results in the
>>>> cursor bo being unmapped too early for rockchip, why is it not unmapped
>>>> too early for all of the other drivers using that helper?
>>>
>>> It's unmapped too early for everyone, it's just that normally that
>>> doesn't
>>> result in a fireworks show. What we maybe could/should do is do the
>>> unmapping asynchronously, but that runs into the overall "current atomic
>>> helpers don't do async yet" problem. Might be a good point to start
>>> fixing
>>> this up though.
>>
>> OK, thanks, I think I'm beginning to understand how this all fits
>> together.
>>
>> It looks like there are two options for me to get reasonable cursor
>> performance on rockchip in the short term:
>>
>> 1) Export the current framebuffer_changed() function as
>>     drm_atomic_helper_framebuffer_changed() and use it in
>>     rockchip_crtc_wait_for_update().
>>
>> 2) Add a mechanism to suppress the legacy_cursor_update check in
>>     drm_atomic_helper_wait_for_vblanks() and switch the rockchip driver
>>     over to it.
>>
>> In both of these cases we're only restoring the unsynced cursor ioctls
>> behaviour when the cursor is moved but it will still be expensive when
>> the cursor bo changes.  That gives sufficient performance in my testing.
>>
>>
>>
>
> Thanks for point that.
>
> because rockchip not support hardware vblank counter, use
> drm_atomic_helper_wait_for_vblanks have under issues:
>
>                                              | <-- HW vsync irq and reg take
> effect
>             plane_commit  --->  |
>      get_vblank and wait ->   |
>                                              | <-- handle_vblank,
> vblank->count + 1
>                  cleanup_fb   ---> |
>               iommu crash  --->  |
>                                              | <-- HW vsync irq and reg take
> effect
> there is no hardware vblank counter on rockchip vop, we can't ensure the
> consistency of reg take effect and vblank->count,
> if plane commit hit into the period of  reg take effect and vblank->count,
> cleanup_fb happen before old_fb swap out from vop,
> then iommu crash.
>
> That is why I special the wait_for_vblanks, we need check the reg really
> take effect before clean up old fb.
> at vop_win_pending_is_complete function, check win enable and win address,
> to ensure that.
>
> Not only rockchip drm do that thing:
>
> exynos also check address before cleanup fb
>         if (start == start_s)
>             exynos_drm_crtc_finish_update(ctx->crtc, plane);
>
> Thanks.

Do you have a scanline counter or something similar at least? Any
other indication about how far along the chip is with scanning out? We
use that in i915 to avoid races with the interrupt handler and detect
this w/a scenario.

I think if you have a scanline counter then it should magically work,
since the vblank code will realize that you're already past the last
vblank interrupt and /should/ have incremented already. Or something
like that.

Otherwise if this is common we might want to figure out how to solve
this in a generic way. It's one of these problems that will make
generic async support almost impossible.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch


From mark.yao at rock-chips.com  Thu Jan 14 00:46:37 2016
From: mark.yao at rock-chips.com (Mark yao)
Date: Thu, 14 Jan 2016 16:46:37 +0800
Subject: [RFC/PATCH] drm/rockchip: don't wait for vblank if fb hasn't
 changed
In-Reply-To: <CAKMK7uGQ47eLS2HxUP2+tQcf5cZm+eoGryCRrBzA2b8=77ox-Q@mail.gmail.com>
References: <1452689614-6570-1-git-send-email-john@metanate.com>	<20160113142320.GQ19130@phenom.ffwll.local>	<20160113143425.363ce07a.john@metanate.com>	<20160113154005.GT19130@phenom.ffwll.local>	<20160113155529.6048b2c1.john@metanate.com>	<20160113162156.GA19130@phenom.ffwll.local>	<20160113164038.5fcf670f.john@metanate.com>	<20160113171917.GC19130@phenom.ffwll.local>	<20160113173954.52e688fc.john@metanate.com>	<5696F6F4.5060908@rock-chips.com>
 <CAKMK7uGQ47eLS2HxUP2+tQcf5cZm+eoGryCRrBzA2b8=77ox-Q@mail.gmail.com>
Message-ID: <5697606D.50603@rock-chips.com>

On 2016?01?14? 16:32, Daniel Vetter wrote:
> On Thu, Jan 14, 2016 at 2:16 AM, Mark yao <mark.yao at rock-chips.com> wrote:
>> On 2016?01?14? 01:39, John Keeping wrote:
>>> On Wed, 13 Jan 2016 18:19:17 +0100, Daniel Vetter wrote:
>>>
>>>> On Wed, Jan 13, 2016 at 04:40:38PM +0000, John Keeping wrote:
>>>>> On Wed, 13 Jan 2016 17:21:56 +0100, Daniel Vetter wrote:
>>>>>
>>>>>> On Wed, Jan 13, 2016 at 03:55:29PM +0000, John Keeping wrote:
>>>>>>> On Wed, 13 Jan 2016 16:40:05 +0100, Daniel Vetter wrote:
>>>>>>>
>>>>>>>> On Wed, Jan 13, 2016 at 02:34:25PM +0000, John Keeping wrote:
>>>>>>>>> On Wed, 13 Jan 2016 15:23:20 +0100, Daniel Vetter wrote:
>>>>>>>>>
>>>>>>>>>> On Wed, Jan 13, 2016 at 12:53:34PM +0000, John Keeping wrote:
>>>>>>>>>>> As commented in drm_atomic_helper_wait_for_vblanks(), userspace
>>>>>>>>>>> relies on cursor ioctls being unsynced.  Converting the rockchip
>>>>>>>>>>> driver to atomic has significantly impacted cursor performance by
>>>>>>>>>>> making every cursor update wait for vblank.
>>>>>>>>>>>
>>>>>>>>>>> By skipping the vblank sync when the framebuffer has not changed
>>>>>>>>>>> (as is done in drm_atomic_helper_wait_for_vblanks()) we can avoid
>>>>>>>>>>> this for the common case of moving the cursor and only need to
>>>>>>>>>>> delay the cursor ioctl when the cursor icon changes.
>>>>>>>>>>>
>>>>>>>>>>> I originally inserted a check on legacy_cursor_update as well, but
>>>>>>>>>>> that caused a storm of iommu page faults.  I didn't investigate
>>>>>>>>>>> the
>>>>>>>>>>> cause of those since this change gives enough of a performance
>>>>>>>>>>> improvement for my use case.
>>>>>>>>>>>
>>>>>>>>>>> This is RFC because of that and because the framebuffer_changed()
>>>>>>>>>>> function is copied from drm_atomic_helper.c as a quick way to test
>>>>>>>>>>> the result.
>>>>>>>>>>>
>>>>>>>>>>> Signed-off-by: John Keeping <john at metanate.com>
>>>>>>>>>>> ---
>>>>>>>>>>>    drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 27
>>>>>>>>>>> +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2
>>>>>>>>>>> deletions(-)
>>>>>>>>>>>
>>>>>>>>>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>>>>>>>>>>> b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index
>>>>>>>>>>> f784488..8fd9821
>>>>>>>>>>> 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>>>>>>>>>>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>>>>>>>>>>> @@ -177,8 +177,28 @@ static void
>>>>>>>>>>> rockchip_crtc_wait_for_update(struct drm_crtc *crtc)
>>>>>>>>>>> crtc_funcs->wait_for_update(crtc); }
>>>>>>>>>>>    +static bool framebuffer_changed(struct drm_device *dev,
>>>>>>>>>>> +                               struct drm_atomic_state
>>>>>>>>>>> *old_state,
>>>>>>>>>>> +                               struct drm_crtc *crtc)
>>>>>>>>>>> +{
>>>>>>>>>>> +       struct drm_plane *plane;
>>>>>>>>>>> +       struct drm_plane_state *old_plane_state;
>>>>>>>>>>> +       int i;
>>>>>>>>>>> +
>>>>>>>>>>> +       for_each_plane_in_state(old_state, plane, old_plane_state,
>>>>>>>>>>> i) {
>>>>>>>>>>> +               if (plane->state->crtc != crtc &&
>>>>>>>>>>> +                   old_plane_state->crtc != crtc)
>>>>>>>>>>> +                       continue;
>>>>>>>>>>> +
>>>>>>>>>>> +               if (plane->state->fb != old_plane_state->fb)
>>>>>>>>>>> +                       return true;
>>>>>>>>>>> +       }
>>>>>>>>>>> +
>>>>>>>>>>> +       return false;
>>>>>>>>>>> +}
>>>>>>>>>> Please don't hand-roll logic that affects semantics like this.
>>>>>>>>>> Instead
>>>>>>>>>> please use drm_atomic_helper_wait_for_vblanks(), which should do
>>>>>>>>>> this
>>>>>>>>>> correctly for you.
>>>>>>>>>>
>>>>>>>>>> If that's not the case then we need to improve the generic helper,
>>>>>>>>>> or
>>>>>>>>>> figure out what's different with rockhip.
>>>>>>>>> According to commit 63ebb9f (drm/rockchip: Convert to support atomic
>>>>>>>>> API) it's because rockchip doesn't have a hardware vblank counter.
>>>>>>>>>
>>>>>>>>> I'm not entirely clear on why this prevents the use of
>>>>>>>>> drm_atomic_helper_wait_for_vblanks().
>>>>>>>> Hm, that commit isn't terribly helpful. If that's really needed then
>>>>>>>> imo I
>>>>>>>> think we should extract a
>>>>>>>> "drm_atomic_helper_plane_needs_vblank_wait()"
>>>>>>>> helper that's used by both. But since rockchip does vblank_get/put
>>>>>>>> calls
>>>>>>>> I'd hope vblanks actually work correctly. And then the helper should
>>>>>>>> work
>>>>>>>> too.
>>>>>>> I tried switching the call to rockchip_crtc_wait_for_update() to
>>>>>>> drm_atomic_helper_wait_for_vblanks() and it works fine until I switch
>>>>>>> the buffer associated with a cursor, at which point I get iommu page
>>>>>>> faults, presumably because the GEM buffer is unreferenced too early.
>>>>>>>
>>>>>>> AFAICT the buffer will be released via drm_atomic_state_free()
>>>>>>> unconditionally, but I suspect I'm missing something since that would
>>>>>>> mean every driver would hit a similar problem.
>>>>>> Yeah, with the helper we always skip, which means when the cursor bo
>>>>>> changes you indeed unmap too early. So can't even share the overall
>>>>>> condition, but we could definitely share the little framebuffer_changed
>>>>>> helper.
>>>>> That leaves me with the question: why do other atomic drivers work?
>>>>>
>>>>> If drm_atomic_helper_wait_for_vblanks() skipping vblanks results in the
>>>>> cursor bo being unmapped too early for rockchip, why is it not unmapped
>>>>> too early for all of the other drivers using that helper?
>>>> It's unmapped too early for everyone, it's just that normally that
>>>> doesn't
>>>> result in a fireworks show. What we maybe could/should do is do the
>>>> unmapping asynchronously, but that runs into the overall "current atomic
>>>> helpers don't do async yet" problem. Might be a good point to start
>>>> fixing
>>>> this up though.
>>> OK, thanks, I think I'm beginning to understand how this all fits
>>> together.
>>>
>>> It looks like there are two options for me to get reasonable cursor
>>> performance on rockchip in the short term:
>>>
>>> 1) Export the current framebuffer_changed() function as
>>>      drm_atomic_helper_framebuffer_changed() and use it in
>>>      rockchip_crtc_wait_for_update().
>>>
>>> 2) Add a mechanism to suppress the legacy_cursor_update check in
>>>      drm_atomic_helper_wait_for_vblanks() and switch the rockchip driver
>>>      over to it.
>>>
>>> In both of these cases we're only restoring the unsynced cursor ioctls
>>> behaviour when the cursor is moved but it will still be expensive when
>>> the cursor bo changes.  That gives sufficient performance in my testing.
>>>
>>>
>>>
>> Thanks for point that.
>>
>> because rockchip not support hardware vblank counter, use
>> drm_atomic_helper_wait_for_vblanks have under issues:
>>
>>                                               | <-- HW vsync irq and reg take
>> effect
>>              plane_commit  --->  |
>>       get_vblank and wait ->   |
>>                                               | <-- handle_vblank,
>> vblank->count + 1
>>                   cleanup_fb   ---> |
>>                iommu crash  --->  |
>>                                               | <-- HW vsync irq and reg take
>> effect
>> there is no hardware vblank counter on rockchip vop, we can't ensure the
>> consistency of reg take effect and vblank->count,
>> if plane commit hit into the period of  reg take effect and vblank->count,
>> cleanup_fb happen before old_fb swap out from vop,
>> then iommu crash.
>>
>> That is why I special the wait_for_vblanks, we need check the reg really
>> take effect before clean up old fb.
>> at vop_win_pending_is_complete function, check win enable and win address,
>> to ensure that.
>>
>> Not only rockchip drm do that thing:
>>
>> exynos also check address before cleanup fb
>>          if (start == start_s)
>>              exynos_drm_crtc_finish_update(ctx->crtc, plane);
>>
>> Thanks.
> Do you have a scanline counter or something similar at least? Any
> other indication about how far along the chip is with scanning out? We
> use that in i915 to avoid races with the interrupt handler and detect
> this w/a scenario.
>
> I think if you have a scanline counter then it should magically work,
> since the vblank code will realize that you're already past the last
> vblank interrupt and /should/ have incremented already. Or something
> like that.
>
> Otherwise if this is common we might want to figure out how to solve
> this in a generic way. It's one of these problems that will make
> generic async support almost impossible.
> -Daniel

No, both rk3288 or rk3036 not support hardware vblank counter and 
scanline counter.

At android side, we use same way, check address and enable bit to ensure 
register take effect.

On future chips,  scanline counter and hardware counter would be 
support, but not now.

-- 
?ark Yao




From a.hajda at samsung.com  Thu Jan 14 00:59:02 2016
From: a.hajda at samsung.com (Andrzej Hajda)
Date: Thu, 14 Jan 2016 09:59:02 +0100
Subject: [PATCH] drm/rockchip/dsi: fix handling mipi_dsi_pixel_format_to_bpp
 result
Message-ID: <1452761942-23864-1-git-send-email-a.hajda@samsung.com>

The function can return negative value so it should be assigned to signed
variable.

The problem has been detected using patch
scripts/coccinelle/tests/unsigned_lesser_than_zero.cocci.

Signed-off-by: Andrzej Hajda <a.hajda at samsung.com>
---
 drivers/gpu/drm/rockchip/dw-mipi-dsi.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
index 7bfe243..f8f8f29 100644
--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
@@ -461,10 +461,11 @@ static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi)
 
 static int dw_mipi_dsi_get_lane_bps(struct dw_mipi_dsi *dsi)
 {
-	unsigned int bpp, i, pre;
+	unsigned int i, pre;
 	unsigned long mpclk, pllref, tmp;
 	unsigned int m = 1, n = 1, target_mbps = 1000;
 	unsigned int max_mbps = dptdin_map[ARRAY_SIZE(dptdin_map) - 1].max_mbps;
+	int bpp;
 
 	bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
 	if (bpp < 0) {
-- 
1.9.1



From david.wu at rock-chips.com  Thu Jan 14 04:31:35 2016
From: david.wu at rock-chips.com (David Wu)
Date: Thu, 14 Jan 2016 20:31:35 +0800
Subject: [PATCH v3 0/4] Add rk3399 i2c clocks calculated method
Message-ID: <1452774699-57455-1-git-send-email-david.wu@rock-chips.com>

RK3399 have updated a new way to calculate i2c timing information to slove
"repeat start" timing issue. So it needs to integrate existing method, use
different ops to seperate them.  

After picking this series, pmic-rk818 and touchscreen-ts could work well
on the rk3368 sdk board. 100k, 400k and 1.7M i2c clk rates were tested on
the rk3399 fpga board, where i2c0 connected to pmic-ti65910. But 3.4M clk
rate was not tested, because of the scl rise time is 60ns, it could not
meet the i2c spec, the scl rise time is hard to reduce on fpga board.

David Wu (4):
  i2c: rk3x: switch to i2c generic dt parsing
  i2c: rk3x: add ops to caculate i2c clocks
  i2c: rk3x: new method to caculate i2c clocks
  i2c: rk3x: support I2C Highspeed Mode

 drivers/i2c/busses/i2c-rk3x.c | 405 +++++++++++++++++++++++++++++++++---------
 1 file changed, 319 insertions(+), 86 deletions(-)

-- 
1.9.1




From david.wu at rock-chips.com  Thu Jan 14 04:31:36 2016
From: david.wu at rock-chips.com (David Wu)
Date: Thu, 14 Jan 2016 20:31:36 +0800
Subject: [PATCH v3 1/4] i2c: rk3x: switch to i2c generic dt parsing
In-Reply-To: <1452774699-57455-1-git-send-email-david.wu@rock-chips.com>
References: <1452774699-57455-1-git-send-email-david.wu@rock-chips.com>
Message-ID: <1452774699-57455-2-git-send-email-david.wu@rock-chips.com>

Switch to the new generic functions: i2c_parse_fw_timings().

Signed-off-by: David Wu <david.wu at rock-chips.com>
---
changes in v3:
- parse timing information with common interface(Wolfram, Andy)

 drivers/i2c/busses/i2c-rk3x.c | 87 ++++++++++++-------------------------------
 1 file changed, 24 insertions(+), 63 deletions(-)

diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c
index 9096d17..1d86e8c 100644
--- a/drivers/i2c/busses/i2c-rk3x.c
+++ b/drivers/i2c/busses/i2c-rk3x.c
@@ -101,10 +101,7 @@ struct rk3x_i2c {
 	struct notifier_block clk_rate_nb;
 
 	/* Settings */
-	unsigned int scl_frequency;
-	unsigned int scl_rise_ns;
-	unsigned int scl_fall_ns;
-	unsigned int sda_fall_ns;
+	struct i2c_timings t;
 
 	/* Synchronization & notification */
 	spinlock_t lock;
@@ -437,10 +434,7 @@ out:
  * Calculate divider values for desired SCL frequency
  *
  * @clk_rate: I2C input clock rate
- * @scl_rate: Desired SCL rate
- * @scl_rise_ns: How many ns it takes for SCL to rise.
- * @scl_fall_ns: How many ns it takes for SCL to fall.
- * @sda_fall_ns: How many ns it takes for SDA to fall.
+ * @t_input: Known I2C timing information.
  * @div_low: Divider output for low
  * @div_high: Divider output for high
  *
@@ -448,11 +442,10 @@ out:
  * a best-effort divider value is returned in divs. If the target rate is
  * too high, we silently use the highest possible rate.
  */
-static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long scl_rate,
-			      unsigned long scl_rise_ns,
-			      unsigned long scl_fall_ns,
-			      unsigned long sda_fall_ns,
-			      unsigned long *div_low, unsigned long *div_high)
+static int rk3x_i2c_calc_divs(unsigned long clk_rate,
+			      struct i2c_timings *t_input,
+			      unsigned long *div_low,
+			      unsigned long *div_high)
 {
 	unsigned long spec_min_low_ns, spec_min_high_ns;
 	unsigned long spec_setup_start, spec_max_data_hold_ns;
@@ -472,12 +465,12 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long scl_rate,
 	int ret = 0;
 
 	/* Only support standard-mode and fast-mode */
-	if (WARN_ON(scl_rate > 400000))
-		scl_rate = 400000;
+	if (WARN_ON(t_input->bus_freq_hz > 400000))
+		t_input->bus_freq_hz = 400000;
 
 	/* prevent scl_rate_khz from becoming 0 */
-	if (WARN_ON(scl_rate < 1000))
-		scl_rate = 1000;
+	if (WARN_ON(t_input->bus_freq_hz < 1000))
+		t_input->bus_freq_hz = 1000;
 
 	/*
 	 * min_low_ns:  The minimum number of ns we need to hold low to
@@ -491,7 +484,7 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long scl_rate,
 	 *	 This is because the i2c host on Rockchip holds the data line
 	 *	 for half the low time.
 	 */
-	if (scl_rate <= 100000) {
+	if (t_input->bus_freq_hz <= 100000) {
 		/* Standard-mode */
 		spec_min_low_ns = 4700;
 		spec_setup_start = 4700;
@@ -506,7 +499,7 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long scl_rate,
 		spec_max_data_hold_ns = 900;
 		data_hold_buffer_ns = 50;
 	}
-	min_high_ns = scl_rise_ns + spec_min_high_ns;
+	min_high_ns = t_input->scl_rise_ns + spec_min_high_ns;
 
 	/*
 	 * Timings for repeated start:
@@ -517,18 +510,19 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long scl_rate,
 	 * we meet tSU;STA and tHD;STA times.
 	 */
 	min_high_ns = max(min_high_ns,
-		DIV_ROUND_UP((scl_rise_ns + spec_setup_start) * 1000, 875));
+		DIV_ROUND_UP((t_input->scl_rise_ns + spec_setup_start) * 1000,
+			     875));
 	min_high_ns = max(min_high_ns,
-		DIV_ROUND_UP((scl_rise_ns + spec_setup_start +
-			      sda_fall_ns + spec_min_high_ns), 2));
+		DIV_ROUND_UP((t_input->scl_rise_ns + spec_setup_start +
+			      t_input->sda_fall_ns + spec_min_high_ns), 2));
 
-	min_low_ns = scl_fall_ns + spec_min_low_ns;
+	min_low_ns = t_input->scl_fall_ns + spec_min_low_ns;
 	max_low_ns = spec_max_data_hold_ns * 2 - data_hold_buffer_ns;
 	min_total_ns = min_low_ns + min_high_ns;
 
 	/* Adjust to avoid overflow */
 	clk_rate_khz = DIV_ROUND_UP(clk_rate, 1000);
-	scl_rate_khz = scl_rate / 1000;
+	scl_rate_khz = t_input->bus_freq_hz / 1000;
 
 	/*
 	 * We need the total div to be >= this number
@@ -620,10 +614,8 @@ static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate)
 	u64 t_low_ns, t_high_ns;
 	int ret;
 
-	ret = rk3x_i2c_calc_divs(clk_rate, i2c->scl_frequency, i2c->scl_rise_ns,
-				 i2c->scl_fall_ns, i2c->sda_fall_ns,
-				 &div_low, &div_high);
-	WARN_ONCE(ret != 0, "Could not reach SCL freq %u", i2c->scl_frequency);
+	ret = rk3x_i2c_calc_divs(clk_rate, &i2c->t, &div_low, &div_high);
+	WARN_ONCE(ret != 0, "Could not reach SCL freq %u", i2c->t.bus_freq_hz);
 
 	clk_enable(i2c->clk);
 	i2c_writel(i2c, (div_high << 16) | (div_low & 0xffff), REG_CLKDIV);
@@ -634,7 +626,7 @@ static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate)
 	dev_dbg(i2c->dev,
 		"CLK %lukhz, Req %uns, Act low %lluns high %lluns\n",
 		clk_rate / 1000,
-		1000000000 / i2c->scl_frequency,
+		1000000000 / i2c->t.bus_freq_hz,
 		t_low_ns, t_high_ns);
 }
 
@@ -664,9 +656,7 @@ static int rk3x_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long
 
 	switch (event) {
 	case PRE_RATE_CHANGE:
-		if (rk3x_i2c_calc_divs(ndata->new_rate, i2c->scl_frequency,
-				       i2c->scl_rise_ns, i2c->scl_fall_ns,
-				       i2c->sda_fall_ns,
+		if (rk3x_i2c_calc_divs(ndata->new_rate, &i2c->t,
 				       &div_low, &div_high) != 0)
 			return NOTIFY_STOP;
 
@@ -879,37 +869,8 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
 	match = of_match_node(rk3x_i2c_match, np);
 	i2c->soc_data = (struct rk3x_i2c_soc_data *)match->data;
 
-	if (of_property_read_u32(pdev->dev.of_node, "clock-frequency",
-				 &i2c->scl_frequency)) {
-		dev_info(&pdev->dev, "using default SCL frequency: %d\n",
-			 DEFAULT_SCL_RATE);
-		i2c->scl_frequency = DEFAULT_SCL_RATE;
-	}
-
-	if (i2c->scl_frequency == 0 || i2c->scl_frequency > 400 * 1000) {
-		dev_warn(&pdev->dev, "invalid SCL frequency specified.\n");
-		dev_warn(&pdev->dev, "using default SCL frequency: %d\n",
-			 DEFAULT_SCL_RATE);
-		i2c->scl_frequency = DEFAULT_SCL_RATE;
-	}
-
-	/*
-	 * Read rise and fall time from device tree. If not available use
-	 * the default maximum timing from the specification.
-	 */
-	if (of_property_read_u32(pdev->dev.of_node, "i2c-scl-rising-time-ns",
-				 &i2c->scl_rise_ns)) {
-		if (i2c->scl_frequency <= 100000)
-			i2c->scl_rise_ns = 1000;
-		else
-			i2c->scl_rise_ns = 300;
-	}
-	if (of_property_read_u32(pdev->dev.of_node, "i2c-scl-falling-time-ns",
-				 &i2c->scl_fall_ns))
-		i2c->scl_fall_ns = 300;
-	if (of_property_read_u32(pdev->dev.of_node, "i2c-sda-falling-time-ns",
-				 &i2c->sda_fall_ns))
-		i2c->sda_fall_ns = i2c->scl_fall_ns;
+	/* use common interface to get I2C timing properties */
+	i2c_parse_fw_timings(&pdev->dev, &i2c->t, true);
 
 	strlcpy(i2c->adap.name, "rk3x-i2c", sizeof(i2c->adap.name));
 	i2c->adap.owner = THIS_MODULE;
-- 
1.9.1




From david.wu at rock-chips.com  Thu Jan 14 04:31:37 2016
From: david.wu at rock-chips.com (David Wu)
Date: Thu, 14 Jan 2016 20:31:37 +0800
Subject: [PATCH v3 2/4] i2c: rk3x: add ops to caculate i2c clocks
In-Reply-To: <1452774699-57455-1-git-send-email-david.wu@rock-chips.com>
References: <1452774699-57455-1-git-send-email-david.wu@rock-chips.com>
Message-ID: <1452774699-57455-3-git-send-email-david.wu@rock-chips.com>

From: David Wu <wdc at rock-chips.com>

I2c Controller of rk3x is updated for the rules to caculate clocks.
So it need a new method to caculate i2c clock timing information
for new version. The current method is defined as v0, and new is
v1, next maybe v2......

Signed-off-by: David Wu <david.wu at rock-chips.com>
---
Changes in v3:
- use GENMASK(Andy)
- Too many arguments for ops func, use struct for them(Andy)

changes in v2:
- split patch to three patches(Heiko)

 drivers/i2c/busses/i2c-rk3x.c | 80 +++++++++++++++++++++++++++++--------------
 1 file changed, 54 insertions(+), 26 deletions(-)

diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c
index 1d86e8c..185e0f9 100644
--- a/drivers/i2c/busses/i2c-rk3x.c
+++ b/drivers/i2c/busses/i2c-rk3x.c
@@ -58,6 +58,11 @@ enum {
 #define REG_CON_LASTACK   BIT(5) /* 1: send NACK after last received byte */
 #define REG_CON_ACTACK    BIT(6) /* 1: stop if NACK is received */
 
+#define VERSION_MASK	  GENMASK(31, 16)
+#define VERSION_SHIFT	  16
+
+#define RK3X_I2C_V0	  0x0
+
 /* REG_MRXADDR bits */
 #define REG_MRXADDR_VALID(x) BIT(24 + (x)) /* [x*8+7:x*8] of MRX[R]ADDR valid */
 
@@ -90,10 +95,27 @@ struct rk3x_i2c_soc_data {
 	int grf_offset;
 };
 
+/**
+ * struct rk3x_priv_i2c_timings - rk3x I2C timing information
+ * @div_low: Divider output for low
+ * @div_high: Divider output for high
+ */
+struct rk3x_priv_i2c_timings {
+	unsigned long div_low;
+	unsigned long div_high;
+};
+
+struct rk3x_i2c_ops {
+	int (*calc_clock)(unsigned long,
+			  struct i2c_timings *,
+			  struct rk3x_priv_i2c_timings *);
+};
+
 struct rk3x_i2c {
 	struct i2c_adapter adap;
 	struct device *dev;
 	struct rk3x_i2c_soc_data *soc_data;
+	struct rk3x_i2c_ops ops;
 
 	/* Hardware resources */
 	void __iomem *regs;
@@ -102,6 +124,7 @@ struct rk3x_i2c {
 
 	/* Settings */
 	struct i2c_timings t;
+	struct rk3x_priv_i2c_timings t_priv;
 
 	/* Synchronization & notification */
 	spinlock_t lock;
@@ -431,21 +454,20 @@ out:
 }
 
 /**
- * Calculate divider values for desired SCL frequency
+ * Calculate timing clock info values for desired SCL frequency
  *
  * @clk_rate: I2C input clock rate
- * @t_input: Known I2C timing information.
- * @div_low: Divider output for low
- * @div_high: Divider output for high
+ * @t_input: Known I2C timing information
+ * @t_output: Caculated rk3x private timing information that would
+ * be written into regs
  *
  * Returns: 0 on success, -EINVAL if the goal SCL rate is too slow. In that case
  * a best-effort divider value is returned in divs. If the target rate is
  * too high, we silently use the highest possible rate.
  */
-static int rk3x_i2c_calc_divs(unsigned long clk_rate,
-			      struct i2c_timings *t_input,
-			      unsigned long *div_low,
-			      unsigned long *div_high)
+static int rk3x_i2c_v0_calc_clock(unsigned long clk_rate,
+				  struct i2c_timings *t_input,
+				  struct rk3x_priv_i2c_timings *t_output)
 {
 	unsigned long spec_min_low_ns, spec_min_high_ns;
 	unsigned long spec_setup_start, spec_max_data_hold_ns;
@@ -553,8 +575,8 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate,
 		 * Time needed to meet hold requirements is important.
 		 * Just use that.
 		 */
-		*div_low = min_low_div;
-		*div_high = min_high_div;
+		t_output->div_low = min_low_div;
+		t_output->div_high = min_high_div;
 	} else {
 		/*
 		 * We've got to distribute some time among the low and high
@@ -583,25 +605,25 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate,
 
 		/* Give low the "ideal" and give high whatever extra is left */
 		extra_low_div = ideal_low_div - min_low_div;
-		*div_low = ideal_low_div;
-		*div_high = min_high_div + (extra_div - extra_low_div);
+		t_output->div_low = ideal_low_div;
+		t_output->div_high = min_high_div + (extra_div - extra_low_div);
 	}
 
 	/*
 	 * Adjust to the fact that the hardware has an implicit "+1".
 	 * NOTE: Above calculations always produce div_low > 0 and div_high > 0.
 	 */
-	*div_low = *div_low - 1;
-	*div_high = *div_high - 1;
+	t_output->div_low = t_output->div_low - 1;
+	t_output->div_high = t_output->div_high - 1;
 
 	/* Maximum divider supported by hw is 0xffff */
-	if (*div_low > 0xffff) {
-		*div_low = 0xffff;
+	if (t_output->div_low > 0xffff) {
+		t_output->div_low = 0xffff;
 		ret = -EINVAL;
 	}
 
-	if (*div_high > 0xffff) {
-		*div_high = 0xffff;
+	if (t_output->div_high > 0xffff) {
+		t_output->div_high = 0xffff;
 		ret = -EINVAL;
 	}
 
@@ -610,19 +632,21 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate,
 
 static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate)
 {
-	unsigned long div_low, div_high;
 	u64 t_low_ns, t_high_ns;
 	int ret;
 
-	ret = rk3x_i2c_calc_divs(clk_rate, &i2c->t, &div_low, &div_high);
+	ret = i2c->ops.calc_clock(clk_rate, &i2c->t, &i2c->t_priv);
 	WARN_ONCE(ret != 0, "Could not reach SCL freq %u", i2c->t.bus_freq_hz);
 
 	clk_enable(i2c->clk);
-	i2c_writel(i2c, (div_high << 16) | (div_low & 0xffff), REG_CLKDIV);
+	i2c_writel(i2c, (i2c->t_priv.div_high << 16) |
+		  (i2c->t_priv.div_low & 0xffff), REG_CLKDIV);
 	clk_disable(i2c->clk);
 
-	t_low_ns = div_u64(((u64)div_low + 1) * 8 * 1000000000, clk_rate);
-	t_high_ns = div_u64(((u64)div_high + 1) * 8 * 1000000000, clk_rate);
+	t_low_ns = div_u64(((u64)i2c->t_priv.div_low + 1) * 8 * 1000000000,
+			   clk_rate);
+	t_high_ns = div_u64(((u64)i2c->t_priv.div_high + 1) * 8 * 1000000000,
+			    clk_rate);
 	dev_dbg(i2c->dev,
 		"CLK %lukhz, Req %uns, Act low %lluns high %lluns\n",
 		clk_rate / 1000,
@@ -652,12 +676,11 @@ static int rk3x_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long
 {
 	struct clk_notifier_data *ndata = data;
 	struct rk3x_i2c *i2c = container_of(nb, struct rk3x_i2c, clk_rate_nb);
-	unsigned long div_low, div_high;
 
 	switch (event) {
 	case PRE_RATE_CHANGE:
-		if (rk3x_i2c_calc_divs(ndata->new_rate, &i2c->t,
-				       &div_low, &div_high) != 0)
+		if (i2c->ops.calc_clock(ndata->new_rate, &i2c->t,
+					&i2c->t_priv) != 0)
 			return NOTIFY_STOP;
 
 		/* scale up */
@@ -861,6 +884,7 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
 	u32 value;
 	int irq;
 	unsigned long clk_rate;
+	unsigned int version;
 
 	i2c = devm_kzalloc(&pdev->dev, sizeof(struct rk3x_i2c), GFP_KERNEL);
 	if (!i2c)
@@ -944,6 +968,10 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, i2c);
 
+	version = (readl(i2c->regs + REG_CON) & VERSION_MASK) >> VERSION_SHIFT;
+	if (version == RK3X_I2C_V0)
+		i2c->ops.calc_clock = rk3x_i2c_v0_calc_clock;
+
 	ret = clk_prepare(i2c->clk);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "Could not prepare clock\n");
-- 
1.9.1




From david.wu at rock-chips.com  Thu Jan 14 04:31:39 2016
From: david.wu at rock-chips.com (David Wu)
Date: Thu, 14 Jan 2016 20:31:39 +0800
Subject: [PATCH v3 4/4] i2c: rk3x: support I2C Highspeed Mode
In-Reply-To: <1452774699-57455-1-git-send-email-david.wu@rock-chips.com>
References: <1452774699-57455-1-git-send-email-david.wu@rock-chips.com>
Message-ID: <1452774699-57455-5-git-send-email-david.wu@rock-chips.com>

The i2c controller of new version1 supports highspeed mode,
1.7M and 3.4M rate. It also could be calculated clocks by the rules.

Note: The final divs would be effected a lot by hardware elements like
scl_rise_ns, scl_fall_ns and sda_rise_ns.

There are two examples of div calculated by the rules, not contain
hardware elements like scl_rise time, scl_fall time and sda_rise time:

    1. scl: 1.7M:
       Source Pclk: 200M, Target scl: 1700K, Final scl: 1.667K;
       Tpclk = 5ns;

       divl = 9, divh = 4;
       l = 10, h = 5;
       tLow = 400ns, tHigh = 200ns;

       start_setup_cnt = stop_setup_cnt = 0;
       u = p = 1;
       tSU;sta = (8h * u + 1) * T = 205ns;
       tHD;sta = [8h * (u + 1) - 1] * T = 395ns;
       tSU;sto = (8h * p + 1) * T = 205ns;

       data_upd_st = 1;
       s = data_upd_st + 1 = 2;
       tHD;sda = (l * s + 1) * T = 105;
       tSU;sda = [(8 - s) * l + 1] * T = 295;

    2. scl: 3.4M
       Source Pclk: 200M, Target scl: 3400K, Final scl: 3125K;
       Tpclk = 5ns;

       divl = 4, divh = 2;
       l = 5, h = 3;
       tLow = 200ns, tHigh = 120ns;

       start_setup_cnt = stop_setup_cnt = 1;
       u = p = 2;
       tSU;sta = (8h * u + 1) * T = 245ns;
       tHD;sta = [8h * (u + 1) - 1] * T = 355ns;
       tSU;sto = (8h * p + 1) * T = 245ns;

       data_upd_st = 1;
       s = data_upd_st + 1 = 2;
       tHD;sda = (l * s + 1) * T = 55ns;
       tSU;sda = [(8 - l) * s + 1] * T = 145ns;

Signed-off-by: David Wu <david.wu at rock-chips.com>
---
Changes in v3: None

changes in v2:     
- split patch to three patches(Heiko)

 drivers/i2c/busses/i2c-rk3x.c | 24 +++++++++++++++++++++---
 1 file changed, 21 insertions(+), 3 deletions(-)

diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c
index b7229a7..bb041aa 100644
--- a/drivers/i2c/busses/i2c-rk3x.c
+++ b/drivers/i2c/busses/i2c-rk3x.c
@@ -700,9 +700,9 @@ static int rk3x_i2c_v1_calc_clock(unsigned long clk_rate,
 
 	int ret = 0;
 
-	/* Support standard-mode and fast-mode */
-	if (WARN_ON(t_input->bus_freq_hz > 400000))
-		t_input->bus_freq_hz = 400000;
+	/* Support standard-mode, fast-mode and highspeed-mode */
+	if (WARN_ON(t_input->bus_freq_hz > 3400000))
+		t_input->bus_freq_hz = 3400000;
 
 	/* prevent scl_rate_khz from becoming 0 */
 	if (WARN_ON(t_input->bus_freq_hz < 1000))
@@ -732,6 +732,24 @@ static int rk3x_i2c_v1_calc_clock(unsigned long clk_rate,
 
 		spec_min_data_setup_ns = 100;
 		spec_max_data_hold_ns = 900;
+	} else if (t_input->bus_freq_hz <= 1700000) {
+		spec_min_low_ns = 320;
+		spec_min_high_ns = 120;
+
+		spec_min_setup_start_ns = 160;
+		spec_min_stop_setup_ns = 160;
+
+		spec_min_data_setup_ns = 10;
+		spec_max_data_hold_ns = 150;
+	} else {
+		spec_min_low_ns = 160;
+		spec_min_high_ns = 60;
+
+		spec_min_setup_start_ns = 160;
+		spec_min_stop_setup_ns = 160;
+
+		spec_min_data_setup_ns = 10;
+		spec_max_data_hold_ns = 70;
 	}
 
 	/* caculate min-divh and min-divl */
-- 
1.9.1




From david.wu at rock-chips.com  Thu Jan 14 04:31:38 2016
From: david.wu at rock-chips.com (David Wu)
Date: Thu, 14 Jan 2016 20:31:38 +0800
Subject: [PATCH v3 3/4] i2c: rk3x: new method to caculate i2c clocks
In-Reply-To: <1452774699-57455-1-git-send-email-david.wu@rock-chips.com>
References: <1452774699-57455-1-git-send-email-david.wu@rock-chips.com>
Message-ID: <1452774699-57455-4-git-send-email-david.wu@rock-chips.com>

There was an timing issue about "repeated start" time at the I2C
controller of version0, controller appears to drop SDA at .875x (7/8)
programmed clk high. The rule(.875x) isn't enough to meet tSU;STA
requirements on 100k's Standard-mode. To resolve this issue,
data_upd_st, start_setup_cnt and stop_setup_cnt configs for I2C
timing information are added, new rules are designed to calculate
the timing information at new v1.

There are two examples for clocks calculated by the rules, not contain
hardware elements like scl_rise time, scl_fall time and sda_rise time:

    1. Standard-mode:
       Source Pclk: 80M, Target scl: 100K, Final scl: 100K;
       Tpclk = 12.5ns;

       divl = 53, divh = 45;
       l = 54, h = 46;
       tLow = 5.400us, tHigh = 4.600us;

       start_setup_cnt = 1, stop_setup_cnt = 0;
       u = 2, p = 1;
       tSU;sta = (8h * u + 1) * T = 9.2125us;
       tHD;sta = [8h * (u + 1) - 1] * T = 13.7875us;
       tSU;sto = (8h * p + 1) * T = 4.6125;

       data_upd_st = 2;
       s = data_upd_st + 1 = 3;
       tHD;sda = (l * s + 1) * T = 2.038us;
       tSU;sda = [(8 - s) * l + 1] * T = 3.388us;

    2. Fast-mode:
       Source Pclk: 80M, Target scl: 400K, Final scl: 400K;
       Tpclk = 12.5ns;

       divl = 16, divh = 7;
       l = 17, h = 8;
       tLow = 1.7us, tHigh = 0.8us;

       start_setup_cnt = stop_setup_cnt = 0;
       u = p = 1;
       tSU;sta = (8h * u + 1) * T = 0.8125us;
       tHD;sta = [8h * (u + 1) - 1] * T = 1.5875us;
       tSU;sto = (8h * p + 1) * T = 0.8125us;

       data_upd_st = 2;
       s = data_upd_st + 1 = 3;
       tHD;sda = (l * s + 1) * T = 650ns;
       tSU;sda = [(8 - l) * s + 1] * T = 1075ns;

It seemed the rules make the timing meet the I2C spec requirements
both Standard-mode and Fast-mode.

Signed-off-by: David Wu <david.wu at rock-chips.com>
---
changes in v3:
- Add more comments
- Calculate counts behind divh and divl

changes in v2:     
- split patch to three patches(Heiko)

 drivers/i2c/busses/i2c-rk3x.c | 236 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 231 insertions(+), 5 deletions(-)

diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c
index 185e0f9..b7229a7 100644
--- a/drivers/i2c/busses/i2c-rk3x.c
+++ b/drivers/i2c/busses/i2c-rk3x.c
@@ -58,10 +58,15 @@ enum {
 #define REG_CON_LASTACK   BIT(5) /* 1: send NACK after last received byte */
 #define REG_CON_ACTACK    BIT(6) /* 1: stop if NACK is received */
 
+#define REG_CON_SDA_CNT(cnt)  ((cnt) << 8)
+#define REG_CON_STA_CNT(cnt)  ((cnt) << 12)
+#define REG_CON_STO_CNT(cnt)  ((cnt) << 14)
+
 #define VERSION_MASK	  GENMASK(31, 16)
 #define VERSION_SHIFT	  16
 
 #define RK3X_I2C_V0	  0x0
+#define RK3X_I2C_V1	  0x1
 
 /* REG_MRXADDR bits */
 #define REG_MRXADDR_VALID(x) BIT(24 + (x)) /* [x*8+7:x*8] of MRX[R]ADDR valid */
@@ -99,10 +104,16 @@ struct rk3x_i2c_soc_data {
  * struct rk3x_priv_i2c_timings - rk3x I2C timing information
  * @div_low: Divider output for low
  * @div_high: Divider output for high
+ * @thd_sda_count: SDA update point config used to adjust sda setup/hold time
+ * @tsu_sta_count: Start setup config for setup start time and hold start time
+ * @tsu_sto_count: Stop setup config for setup stop time
  */
 struct rk3x_priv_i2c_timings {
 	unsigned long div_low;
 	unsigned long div_high;
+	unsigned int thd_sda_count;
+	unsigned int tsu_sta_count;
+	unsigned int tsu_sto_count;
 };
 
 struct rk3x_i2c_ops {
@@ -154,6 +165,13 @@ static inline u32 i2c_readl(struct rk3x_i2c *i2c, unsigned int offset)
 	return readl(i2c->regs + offset);
 }
 
+static inline u32 rk3x_i2c_get_con_count(struct rk3x_i2c *i2c)
+{
+	return REG_CON_SDA_CNT(i2c->t_priv.thd_sda_count) |
+	       REG_CON_STA_CNT(i2c->t_priv.tsu_sta_count) |
+	       REG_CON_STO_CNT(i2c->t_priv.tsu_sto_count);
+}
+
 /* Reset all interrupt pending bits */
 static inline void rk3x_i2c_clean_ipd(struct rk3x_i2c *i2c)
 {
@@ -165,13 +183,13 @@ static inline void rk3x_i2c_clean_ipd(struct rk3x_i2c *i2c)
  */
 static void rk3x_i2c_start(struct rk3x_i2c *i2c)
 {
-	u32 val;
+	u32 val = rk3x_i2c_get_con_count(i2c);
 
 	rk3x_i2c_clean_ipd(i2c);
 	i2c_writel(i2c, REG_INT_START, REG_IEN);
 
 	/* enable adapter with correct mode, send START condition */
-	val = REG_CON_EN | REG_CON_MOD(i2c->mode) | REG_CON_START;
+	val = val | REG_CON_EN | REG_CON_MOD(i2c->mode) | REG_CON_START;
 
 	/* if we want to react to NACK, set ACTACK bit */
 	if (!(i2c->msg->flags & I2C_M_IGNORE_NAK))
@@ -212,7 +230,7 @@ static void rk3x_i2c_stop(struct rk3x_i2c *i2c, int error)
 		 * get the intended effect by resetting its internal state
 		 * and issuing an ordinary START.
 		 */
-		i2c_writel(i2c, 0, REG_CON);
+		i2c_writel(i2c, rk3x_i2c_get_con_count(i2c), REG_CON);
 
 		/* signal that we are finished with the current msg */
 		wake_up(&i2c->wait);
@@ -630,6 +648,211 @@ static int rk3x_i2c_v0_calc_clock(unsigned long clk_rate,
 	return ret;
 }
 
+/**
+ * Calculate timing clock info values for desired SCL frequency
+ *
+ * @clk_rate: I2C input clock rate
+ * @t_input: Known I2C timing information
+ * @t_output: Caculated rk3x private timing information that would
+ * be written into regs
+ * Returns: 0 on success, -EINVAL if the goal SCL rate is too slow. In that case
+ * a best-effort divider value is returned in divs. If the target rate is
+ * too high, we silently use the highest possible rate.
+ * The following formulas are v1's method to calculate clock.
+ *
+ * l = divl + 1;
+ * h = divh + 1;
+ * s = data_upd_st + 1;
+ * u = start_setup_cnt + 1;
+ * p = stop_setup_cnt + 1;
+ * T = Tclk_i2c;
+
+ * tHigh = 8 * h * T;
+ * tLow = 8 * l * T;
+
+ * tHD;sda = (l * s + 1) * T;
+ * tSU;sda = [(8 - s) * l + 1] * T;
+ * tI2C = 8 * (l + h) * T;
+
+ * tSU;sta = (8h * u + 1) * T;
+ * tHD;sta = [8h * (u + 1) - 1] * T;
+ * tSU;sto = (8h * p + 1) * T;
+ */
+static int rk3x_i2c_v1_calc_clock(unsigned long clk_rate,
+				  struct i2c_timings *t_input,
+				  struct rk3x_priv_i2c_timings *t_output)
+{
+	unsigned long spec_min_low_ns, spec_min_high_ns;
+	unsigned long spec_min_setup_start_ns, spec_min_stop_setup_ns;
+	unsigned long spec_min_data_setup_ns, spec_max_data_hold_ns;
+
+	unsigned long min_low_ns, min_high_ns, min_total_ns;
+	unsigned long min_setup_start_ns, min_setup_data_ns;
+	unsigned long min_stop_setup_ns, max_hold_data_ns;
+
+	unsigned long clk_rate_khz, scl_rate_khz;
+
+	unsigned long min_low_div, min_high_div;
+
+	unsigned long min_div_for_hold, min_total_div;
+	unsigned long extra_div, extra_low_div;
+	unsigned long data_hd_cnt;
+
+	int ret = 0;
+
+	/* Support standard-mode and fast-mode */
+	if (WARN_ON(t_input->bus_freq_hz > 400000))
+		t_input->bus_freq_hz = 400000;
+
+	/* prevent scl_rate_khz from becoming 0 */
+	if (WARN_ON(t_input->bus_freq_hz < 1000))
+		t_input->bus_freq_hz = 1000;
+
+	/*
+	 * min_low_ns: The minimum number of ns we need to hold low to
+	 *	       meet I2C specification, should include fall time.
+	 * min_high_ns: The minimum number of ns we need to hold high to
+	 *	        meet I2C specification, should include rise time.
+	 */
+	if (t_input->bus_freq_hz <= 100000) {
+		spec_min_low_ns = 4700;
+		spec_min_high_ns = 4000;
+
+		spec_min_setup_start_ns = 4700;
+		spec_min_stop_setup_ns = 4000;
+
+		spec_min_data_setup_ns = 250;
+                spec_max_data_hold_ns = 3450;
+	} else if (t_input->bus_freq_hz <= 400000) {
+		spec_min_low_ns = 1300;
+		spec_min_high_ns = 600;
+
+		spec_min_setup_start_ns = 600;
+		spec_min_stop_setup_ns = 600;
+
+		spec_min_data_setup_ns = 100;
+		spec_max_data_hold_ns = 900;
+	}
+
+	/* caculate min-divh and min-divl */
+	clk_rate_khz = DIV_ROUND_UP(clk_rate, 1000);
+	scl_rate_khz = t_input->bus_freq_hz / 1000;
+	min_total_div = DIV_ROUND_UP(clk_rate_khz, scl_rate_khz * 8);
+
+	min_high_ns = t_input->scl_rise_ns + spec_min_high_ns;
+	min_high_div = DIV_ROUND_UP(clk_rate_khz * min_high_ns, 8 * 1000000);
+
+	min_low_ns = t_input->scl_fall_ns + spec_min_low_ns;
+	min_low_div = DIV_ROUND_UP(clk_rate_khz * min_low_ns, 8 * 1000000);
+
+	/* Final divh and divl must be greater than 0, otherwise the
+	 * hardware would not output the i2c clk.
+	 */
+	if (min_high_div <= 1)
+		min_high_div = 2;
+	if (min_low_div <= 1)
+		min_low_div = 2;
+
+	/* These are the min dividers needed for min hold times. */
+	min_div_for_hold = (min_low_div + min_high_div);
+	min_total_ns = min_low_ns + min_high_ns;
+
+	/*
+	 * This is the maximum divider so we don't go over the maximum.
+	 * We don't round up here (we round down) since this is a maximum.
+	 */
+	 if (min_div_for_hold >= min_total_div) {
+		/*
+		 * Time needed to meet hold requirements is important.
+		 * Just use that.
+		 */
+		t_output->div_low = min_low_div;
+		t_output->div_high = min_high_div;
+	} else {
+		/*
+		 * We've got to distribute some time among the low and high
+		 * so we don't run too fast.
+		 * We'll try to split things up by the scale of min_low_div and
+		 * min_high_div, biasing slightly towards having a higher div
+		 * for low (spend more time low).
+		 */
+		extra_div = min_total_div - min_div_for_hold;
+		extra_low_div = DIV_ROUND_UP(min_low_div * extra_div,
+					     min_div_for_hold);
+
+		t_output->div_low = min_low_div + extra_low_div;
+		t_output->div_high = min_high_div + (extra_div - extra_low_div);
+	}
+
+	/*
+	 * calculate sda data hold count by the rules, thd_sda_count:3
+	 * is a appropriate value to reduce calculated times.
+	 * tHD;sda  = (l * s + 1) * T
+	 * tSU;sda = ((8 - s) * l + 1) * T
+	 */
+	for (data_hd_cnt = 3; data_hd_cnt >= 0; data_hd_cnt--) {
+		max_hold_data_ns =  DIV_ROUND_UP((data_hd_cnt
+						 * (t_output->div_low) + 1)
+						 * 1000000, clk_rate_khz);
+		min_setup_data_ns =  DIV_ROUND_UP(((8 - data_hd_cnt)
+						 * (t_output->div_low) + 1)
+						 * 1000000, clk_rate_khz);
+		if ((max_hold_data_ns < spec_max_data_hold_ns) &&
+		    (min_setup_data_ns > spec_min_data_setup_ns)) {
+			t_output->thd_sda_count = data_hd_cnt;
+			break;
+		}
+	}
+
+	/*
+	 * calculate start setup count, and we aren't care tHD;STA.
+	 * If the start setup count meets the rule of tSU;sta, it also
+	 * meets the rule of tHD;STA.
+	 * tSU;sta = (8h * u + 1) * T
+	 * tHD;sta = [8h * (u + 1) - 1] * T
+	 */
+	min_setup_start_ns = t_input->scl_rise_ns + spec_min_setup_start_ns;
+	t_output->tsu_sta_count = DIV_ROUND_UP(clk_rate_khz * min_setup_start_ns
+			   - 1000000, 8 * 1000000 * (t_output->div_high));
+
+	/*
+	 * calculate start setup count by the rule:
+	 * tSU;sto =(8h * p + 1) * T
+	 */
+	min_stop_setup_ns = t_input->scl_rise_ns + spec_min_stop_setup_ns;
+	t_output->tsu_sto_count = DIV_ROUND_UP(clk_rate_khz * min_stop_setup_ns
+			   - 1000000, 8 * 1000000 * (t_output->div_high));
+
+	/*
+	 * Adjust to the fact that the hardware has an implicit "+1".
+	 * NOTE: Above calculations always produce div_low > 0 and div_high > 0.
+	 */
+	t_output->div_low -= 1;
+	t_output->div_high -= 1;
+
+	/* Maximum divider supported by hw is 0xffff */
+	if (t_output->div_low > 0xffff) {
+		t_output->div_low = 0xffff;
+		ret = -EINVAL;
+	}
+
+	if (t_output->div_high > 0xffff) {
+		t_output->div_high = 0xffff;
+		ret = -EINVAL;
+	}
+
+	/*
+	 * Adjust to the fact that the hardware has an implicit "+1".
+	 * NOTE: Above calculations always produce thd_sda_count > 0,
+	 * tsu_sta_count > 0 and tsu_sta_count > 0.
+	 */
+	t_output->thd_sda_count -= 1;
+	t_output->tsu_sta_count -= 1;
+	t_output->tsu_sto_count -= 1;
+
+	return ret;
+}
+
 static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate)
 {
 	u64 t_low_ns, t_high_ns;
@@ -829,7 +1052,8 @@ static int rk3x_i2c_xfer(struct i2c_adapter *adap,
 
 			/* Force a STOP condition without interrupt */
 			i2c_writel(i2c, 0, REG_IEN);
-			i2c_writel(i2c, REG_CON_EN | REG_CON_STOP, REG_CON);
+			i2c_writel(i2c, rk3x_i2c_get_con_count(i2c) |
+					REG_CON_EN | REG_CON_STOP, REG_CON);
 
 			i2c->state = STATE_IDLE;
 
@@ -969,7 +1193,9 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
 	platform_set_drvdata(pdev, i2c);
 
 	version = (readl(i2c->regs + REG_CON) & VERSION_MASK) >> VERSION_SHIFT;
-	if (version == RK3X_I2C_V0)
+	if (version == RK3X_I2C_V1)
+		i2c->ops.calc_clock = rk3x_i2c_v1_calc_clock;
+	else
 		i2c->ops.calc_clock = rk3x_i2c_v0_calc_clock;
 
 	ret = clk_prepare(i2c->clk);
-- 
1.9.1




From andy.yan at rock-chips.com  Thu Jan 14 04:33:00 2016
From: andy.yan at rock-chips.com (Andy Yan)
Date: Thu, 14 Jan 2016 20:33:00 +0800
Subject: [PATCH 0/2] fix rk3066a-rayeager board hang when cpufreq changes
Message-ID: <1452774780-16493-1-git-send-email-andy.yan@rock-chips.com>


I found the rayeager board always hang in the system boot
process or when cpufreq change. When check the BSP code[0]
from ChipSpark, I found the current vdd_arm voltage is too
low and so is the vdd_logic voltage, which should be modulated
by PWM3.

So I increase the vdd_arm voltage and enabled pwm3 as a pwm
regulator here.

[0] https://github.com/RockchipOpensourceCommunity/px2-android-kernel-3.0/blob/master/arch/arm/mach-rkpx2/board-rkpx2-sdk.c


Andy Yan (2):
  ARM: dts: rockchip: increase vdd_arm voltage for rk3066a-rayeager
    board
  ARM: dts: rockchip: enable pwm3 as pwm regulator for rk3066a-rayeager
    board

 arch/arm/boot/dts/rk3066a-rayeager.dts | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

-- 
1.9.1




From andy.yan at rock-chips.com  Thu Jan 14 04:34:20 2016
From: andy.yan at rock-chips.com (Andy Yan)
Date: Thu, 14 Jan 2016 20:34:20 +0800
Subject: [PATCH 1/2] ARM: dts: rockchip: increase vdd_arm voltage for
 rk3066a-rayeager board
In-Reply-To: <1452774780-16493-1-git-send-email-andy.yan@rock-chips.com>
References: <1452774780-16493-1-git-send-email-andy.yan@rock-chips.com>
Message-ID: <1452774860-16572-1-git-send-email-andy.yan@rock-chips.com>

The current vdd_arm voltage is too low, increase it will make
the system more stable.

Signed-off-by: Andy Yan <andy.yan at rock-chips.com>
---

 arch/arm/boot/dts/rk3066a-rayeager.dts | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/arch/arm/boot/dts/rk3066a-rayeager.dts b/arch/arm/boot/dts/rk3066a-rayeager.dts
index 0bf6876..3d4c104 100644
--- a/arch/arm/boot/dts/rk3066a-rayeager.dts
+++ b/arch/arm/boot/dts/rk3066a-rayeager.dts
@@ -159,6 +159,14 @@
 
 &cpu0 {
 	cpu0-supply = <&vdd_arm>;
+	operating-points = <
+		/* kHz    uV */
+		1008000 1125000
+		816000  1125000
+		600000  1100000
+		504000  1100000
+		312000  1075000
+	>;
 };
 
 &emac {
-- 
1.9.1





From andy.yan at rock-chips.com  Thu Jan 14 04:35:56 2016
From: andy.yan at rock-chips.com (Andy Yan)
Date: Thu, 14 Jan 2016 20:35:56 +0800
Subject: [PATCH 2/2] ARM: dts: rockchip: enable pwm3 as pwm regulator for
 rk3066a-rayeager board
In-Reply-To: <1452774780-16493-1-git-send-email-andy.yan@rock-chips.com>
References: <1452774780-16493-1-git-send-email-andy.yan@rock-chips.com>
Message-ID: <1452774956-16621-1-git-send-email-andy.yan@rock-chips.com>

Rayeager board use pwm3 modulate the vdd_logic voltage, so enable it.

Signed-off-by: Andy Yan <andy.yan at rock-chips.com>
---

 arch/arm/boot/dts/rk3066a-rayeager.dts | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/arch/arm/boot/dts/rk3066a-rayeager.dts b/arch/arm/boot/dts/rk3066a-rayeager.dts
index 3d4c104..d17a5b8 100644
--- a/arch/arm/boot/dts/rk3066a-rayeager.dts
+++ b/arch/arm/boot/dts/rk3066a-rayeager.dts
@@ -78,6 +78,18 @@
 		};
 	};
 
+	pwm_regulator: pwm-regulator {
+		compatible = "pwm-regulator";
+		pwms = <&pwm3 0 1000>;
+		regulator-name = "vdd_logic";
+		regulator-min-microvolt = <1200000>;
+		regulator-max-microvolt = <1200000>;
+		regulator-always-on;
+		voltage-table = <1000000 100>,
+				<1200000 42>;
+		status = "okay";
+	};
+
 	vsys: vsys-regulator {
 		compatible = "regulator-fixed";
 		regulator-name = "vsys";
@@ -443,6 +455,10 @@
 	status = "okay";
 };
 
+&pwm3 {
+	status = "okay";
+};
+
 &saradc {
 	vref-supply = <&vcc_25>;
 	status = "okay";
-- 
1.9.1




From heiko at sntech.de  Thu Jan 14 04:41:05 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Thu, 14 Jan 2016 13:41:05 +0100
Subject: [PATCH 2/2] ARM: dts: rockchip: enable pwm3 as pwm regulator for
 rk3066a-rayeager board
In-Reply-To: <1452774956-16621-1-git-send-email-andy.yan@rock-chips.com>
References: <1452774780-16493-1-git-send-email-andy.yan@rock-chips.com>
 <1452774956-16621-1-git-send-email-andy.yan@rock-chips.com>
Message-ID: <2237362.fjXobafQhV@phil>

Hi Andy,

Am Donnerstag, 14. Januar 2016, 20:35:56 schrieb Andy Yan:
> Rayeager board use pwm3 modulate the vdd_logic voltage, so enable it.
> 
> Signed-off-by: Andy Yan <andy.yan at rock-chips.com>
> ---
> 
>  arch/arm/boot/dts/rk3066a-rayeager.dts | 16 ++++++++++++++++
>  1 file changed, 16 insertions(+)
> 
> diff --git a/arch/arm/boot/dts/rk3066a-rayeager.dts
> b/arch/arm/boot/dts/rk3066a-rayeager.dts index 3d4c104..d17a5b8 100644
> --- a/arch/arm/boot/dts/rk3066a-rayeager.dts
> +++ b/arch/arm/boot/dts/rk3066a-rayeager.dts
> @@ -78,6 +78,18 @@
>  		};
>  	};
> 
> +	pwm_regulator: pwm-regulator {
> +		compatible = "pwm-regulator";
> +		pwms = <&pwm3 0 1000>;
> +		regulator-name = "vdd_logic";
> +		regulator-min-microvolt = <1200000>;
> +		regulator-max-microvolt = <1200000>;

I guess this is to make sure it stays at 1.2V at all times, or is the min-
microvolt settings supposed to be lower?


Thanks
Heiko


From heiko at sntech.de  Thu Jan 14 04:45:02 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Thu, 14 Jan 2016 13:45:02 +0100
Subject: [PATCH 1/2] ARM: dts: rockchip: increase vdd_arm voltage for
 rk3066a-rayeager board
In-Reply-To: <1452774860-16572-1-git-send-email-andy.yan@rock-chips.com>
References: <1452774780-16493-1-git-send-email-andy.yan@rock-chips.com>
 <1452774860-16572-1-git-send-email-andy.yan@rock-chips.com>
Message-ID: <1709463.R522kSjgXB@phil>

Hi Andy,

Am Donnerstag, 14. Januar 2016, 20:34:20 schrieb Andy Yan:
> The current vdd_arm voltage is too low, increase it will make
> the system more stable.
> 
> Signed-off-by: Andy Yan <andy.yan at rock-chips.com>
> ---
> 
>  arch/arm/boot/dts/rk3066a-rayeager.dts | 8 ++++++++
>  1 file changed, 8 insertions(+)
> 
> diff --git a/arch/arm/boot/dts/rk3066a-rayeager.dts
> b/arch/arm/boot/dts/rk3066a-rayeager.dts index 0bf6876..3d4c104 100644
> --- a/arch/arm/boot/dts/rk3066a-rayeager.dts
> +++ b/arch/arm/boot/dts/rk3066a-rayeager.dts
> @@ -159,6 +159,14 @@
> 
>  &cpu0 {
>  	cpu0-supply = <&vdd_arm>;
> +	operating-points = <
> +		/* kHz    uV */
> +		1008000 1125000
> +		816000  1125000
> +		600000  1100000
> +		504000  1100000
> +		312000  1075000
> +	>;
>  };
> 
>  &emac {

Does this only apply to the rayeager-board / px2 soc or for all rk3066a/px2 
boards? I.e. the original values come from some sdk kernel and I might've 
transcribed that wrong at the time and I remember having problems trying to 
reach higher frequencies as well.

So could you check what the full operating points looks like for both and if 
this can move to the rk3066a.dtsi, as both should be able to reach at least 
1.4GHz / 1.6GHz.


Thanks
Heiko


From andy.shevchenko at gmail.com  Thu Jan 14 05:05:15 2016
From: andy.shevchenko at gmail.com (Andy Shevchenko)
Date: Thu, 14 Jan 2016 15:05:15 +0200
Subject: [PATCH v3 1/4] i2c: rk3x: switch to i2c generic dt parsing
In-Reply-To: <1452774699-57455-2-git-send-email-david.wu@rock-chips.com>
References: <1452774699-57455-1-git-send-email-david.wu@rock-chips.com>
 <1452774699-57455-2-git-send-email-david.wu@rock-chips.com>
Message-ID: <CAHp75VdtDjNMLH2szW=u-+iogoTc3t5wnOpoGndoadMCetLtDw@mail.gmail.com>

On Thu, Jan 14, 2016 at 2:31 PM, David Wu <david.wu at rock-chips.com> wrote:
> Switch to the new generic functions: i2c_parse_fw_timings().

Nice one!
Reviwed-by: Andy Shevchenko <andy.shevchenko at gmail.com>

Minor comments below.

> --- a/drivers/i2c/busses/i2c-rk3x.c
> +++ b/drivers/i2c/busses/i2c-rk3x.c

> @@ -437,10 +434,7 @@ out:
>   * Calculate divider values for desired SCL frequency
>   *
>   * @clk_rate: I2C input clock rate
> - * @scl_rate: Desired SCL rate
> - * @scl_rise_ns: How many ns it takes for SCL to rise.
> - * @scl_fall_ns: How many ns it takes for SCL to fall.
> - * @sda_fall_ns: How many ns it takes for SDA to fall.
> + * @t_input: Known I2C timing information.

Perhaps t_input -> t.

>   * @div_low: Divider output for low
>   * @div_high: Divider output for high
>   *
> @@ -448,11 +442,10 @@ out:
>   * a best-effort divider value is returned in divs. If the target rate is
>   * too high, we silently use the highest possible rate.
>   */
> -static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long scl_rate,
> -                             unsigned long scl_rise_ns,
> -                             unsigned long scl_fall_ns,
> -                             unsigned long sda_fall_ns,
> -                             unsigned long *div_low, unsigned long *div_high)
> +static int rk3x_i2c_calc_divs(unsigned long clk_rate,
> +                             struct i2c_timings *t_input,

Ditto.

> +                             unsigned long *div_low,
> +                             unsigned long *div_high)
>  {
>         unsigned long spec_min_low_ns, spec_min_high_ns;
>         unsigned long spec_setup_start, spec_max_data_hold_ns;

> @@ -517,18 +510,19 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long scl_rate,
>          * we meet tSU;STA and tHD;STA times.
>          */
>         min_high_ns = max(min_high_ns,
> -               DIV_ROUND_UP((scl_rise_ns + spec_setup_start) * 1000, 875));
> +               DIV_ROUND_UP((t_input->scl_rise_ns + spec_setup_start) * 1000,
> +                            875));

To one line (after above change).

>         min_high_ns = max(min_high_ns,
> -               DIV_ROUND_UP((scl_rise_ns + spec_setup_start +
> -                             sda_fall_ns + spec_min_high_ns), 2));
> +               DIV_ROUND_UP((t_input->scl_rise_ns + spec_setup_start +
> +                             t_input->sda_fall_ns + spec_min_high_ns), 2));

Ditto.

> @@ -620,10 +614,8 @@ static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate)
>         u64 t_low_ns, t_high_ns;
>         int ret;
>
> -       ret = rk3x_i2c_calc_divs(clk_rate, i2c->scl_frequency, i2c->scl_rise_ns,
> -                                i2c->scl_fall_ns, i2c->sda_fall_ns,
> -                                &div_low, &div_high);
> -       WARN_ONCE(ret != 0, "Could not reach SCL freq %u", i2c->scl_frequency);
> +       ret = rk3x_i2c_calc_divs(clk_rate, &i2c->t, &div_low, &div_high);
> +       WARN_ONCE(ret != 0, "Could not reach SCL freq %u", i2c->t.bus_freq_hz);

I would recommend to

struct i2c_timings *t = &i2c->t;

+       ret = rk3x_i2c_calc_divs(clk_rate, t, &div_low, &div_high);
+       WARN_ONCE(ret != 0, "Could not reach SCL freq %u", t->bus_freq_hz);

> @@ -634,7 +626,7 @@ static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate)
>         dev_dbg(i2c->dev,
>                 "CLK %lukhz, Req %uns, Act low %lluns high %lluns\n",
>                 clk_rate / 1000,
> -               1000000000 / i2c->scl_frequency,
> +               1000000000 / i2c->t.bus_freq_hz,

Ditto.

-- 
With Best Regards,
Andy Shevchenko


From andy.shevchenko at gmail.com  Thu Jan 14 05:19:56 2016
From: andy.shevchenko at gmail.com (Andy Shevchenko)
Date: Thu, 14 Jan 2016 15:19:56 +0200
Subject: [PATCH v3 2/4] i2c: rk3x: add ops to caculate i2c clocks
In-Reply-To: <1452774699-57455-3-git-send-email-david.wu@rock-chips.com>
References: <1452774699-57455-1-git-send-email-david.wu@rock-chips.com>
 <1452774699-57455-3-git-send-email-david.wu@rock-chips.com>
Message-ID: <CAHp75VdR01_suo3nTWuuT2OEusy6z6KGSg29LKD8T3kY9eGMPA@mail.gmail.com>

On Thu, Jan 14, 2016 at 2:31 PM, David Wu <david.wu at rock-chips.com> wrote:
> From: David Wu <wdc at rock-chips.com>
>
> I2c Controller of rk3x is updated for the rules to caculate clocks.
> So it need a new method to caculate i2c clock timing information
> for new version. The current method is defined as v0, and new is
> v1, next maybe v2......

Thanks for an update. My comments below.

> --- a/drivers/i2c/busses/i2c-rk3x.c
> +++ b/drivers/i2c/busses/i2c-rk3x.c

> @@ -90,10 +95,27 @@ struct rk3x_i2c_soc_data {
>         int grf_offset;
>  };
>
> +/**
> + * struct rk3x_priv_i2c_timings - rk3x I2C timing information
> + * @div_low: Divider output for low
> + * @div_high: Divider output for high
> + */
> +struct rk3x_priv_i2c_timings {
> +       unsigned long div_low;
> +       unsigned long div_high;
> +};
> +
> +struct rk3x_i2c_ops {
> +       int (*calc_clock)(unsigned long,
> +                         struct i2c_timings *,
> +                         struct rk3x_priv_i2c_timings *);
> +};
> +
>  struct rk3x_i2c {
>         struct i2c_adapter adap;
>         struct device *dev;
>         struct rk3x_i2c_soc_data *soc_data;
> +       struct rk3x_i2c_ops ops;

Not much sense for now to have a struct for one member.

>
>         /* Hardware resources */
>         void __iomem *regs;
> @@ -102,6 +124,7 @@ struct rk3x_i2c {
>

What about
      /* I2C timing settings */
      struct i2c_timings t;

      /* Divider settings */
      int (*calc_divs)(unsigned long,
                        struct i2c_timings *,
                        unsigned long *div_low,
                        unsigned long *div_high);
      unsigned long div_low;
      unsigned long div_high;
?

As far as I understand you still calculate divider values.

>
>         /* Synchronization & notification */
>         spinlock_t lock;
> @@ -431,21 +454,20 @@ out:
>  }
>
>  /**
> - * Calculate divider values for desired SCL frequency
> + * Calculate timing clock info values for desired SCL frequency
>   *
>   * @clk_rate: I2C input clock rate
> - * @t_input: Known I2C timing information.
> - * @div_low: Divider output for low
> - * @div_high: Divider output for high

For now seems better to leave this prototype.

> + * @t_input: Known I2C timing information
> + * @t_output: Caculated rk3x private timing information that would
> + * be written into regs
>   *
>   * Returns: 0 on success, -EINVAL if the goal SCL rate is too slow. In that case
>   * a best-effort divider value is returned in divs. If the target rate is
>   * too high, we silently use the highest possible rate.
>   */
> -static int rk3x_i2c_calc_divs(unsigned long clk_rate,
> -                             struct i2c_timings *t_input,
> -                             unsigned long *div_low,
> -                             unsigned long *div_high)
> +static int rk3x_i2c_v0_calc_clock(unsigned long clk_rate,
> +                                 struct i2c_timings *t_input,
> +                                 struct rk3x_priv_i2c_timings *t_output)
>  {
>         unsigned long spec_min_low_ns, spec_min_high_ns;
>         unsigned long spec_setup_start, spec_max_data_hold_ns;


> @@ -583,25 +605,25 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate,
>
>                 /* Give low the "ideal" and give high whatever extra is left */
>                 extra_low_div = ideal_low_div - min_low_div;
> -               *div_low = ideal_low_div;
> -               *div_high = min_high_div + (extra_div - extra_low_div);
> +               t_output->div_low = ideal_low_div;
> +               t_output->div_high = min_high_div + (extra_div - extra_low_div);
>         }
>
>         /*
>          * Adjust to the fact that the hardware has an implicit "+1".
>          * NOTE: Above calculations always produce div_low > 0 and div_high > 0.
>          */
> -       *div_low = *div_low - 1;
> -       *div_high = *div_high - 1;
> +       t_output->div_low = t_output->div_low - 1;

-= 1

> +       t_output->div_high = t_output->div_high - 1;

Ditto.

But without change of prototype those no needed.

>
>         /* Maximum divider supported by hw is 0xffff */
> -       if (*div_low > 0xffff) {
> -               *div_low = 0xffff;
> +       if (t_output->div_low > 0xffff) {
> +               t_output->div_low = 0xffff;
>                 ret = -EINVAL;
>         }
>
> -       if (*div_high > 0xffff) {
> -               *div_high = 0xffff;
> +       if (t_output->div_high > 0xffff) {
> +               t_output->div_high = 0xffff;
>                 ret = -EINVAL;
>         }
>
> @@ -610,19 +632,21 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate,
>
>  static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate)
>  {
> -       unsigned long div_low, div_high;
>         u64 t_low_ns, t_high_ns;
>         int ret;
>
> -       ret = rk3x_i2c_calc_divs(clk_rate, &i2c->t, &div_low, &div_high);
> +       ret = i2c->ops.calc_clock(clk_rate, &i2c->t, &i2c->t_priv);
>         WARN_ONCE(ret != 0, "Could not reach SCL freq %u", i2c->t.bus_freq_hz);
>
>         clk_enable(i2c->clk);
> -       i2c_writel(i2c, (div_high << 16) | (div_low & 0xffff), REG_CLKDIV);
> +       i2c_writel(i2c, (i2c->t_priv.div_high << 16) |
> +                 (i2c->t_priv.div_low & 0xffff), REG_CLKDIV);
>         clk_disable(i2c->clk);
>
> -       t_low_ns = div_u64(((u64)div_low + 1) * 8 * 1000000000, clk_rate);
> -       t_high_ns = div_u64(((u64)div_high + 1) * 8 * 1000000000, clk_rate);
> +       t_low_ns = div_u64(((u64)i2c->t_priv.div_low + 1) * 8 * 1000000000,
> +                          clk_rate);
> +       t_high_ns = div_u64(((u64)i2c->t_priv.div_high + 1) * 8 * 1000000000,
> +                           clk_rate);
>         dev_dbg(i2c->dev,
>                 "CLK %lukhz, Req %uns, Act low %lluns high %lluns\n",
>                 clk_rate / 1000,
> @@ -652,12 +676,11 @@ static int rk3x_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long
>  {
>         struct clk_notifier_data *ndata = data;
>         struct rk3x_i2c *i2c = container_of(nb, struct rk3x_i2c, clk_rate_nb);
> -       unsigned long div_low, div_high;
>
>         switch (event) {
>         case PRE_RATE_CHANGE:
> -               if (rk3x_i2c_calc_divs(ndata->new_rate, &i2c->t,
> -                                      &div_low, &div_high) != 0)
> +               if (i2c->ops.calc_clock(ndata->new_rate, &i2c->t,
> +                                       &i2c->t_priv) != 0)
>                         return NOTIFY_STOP;
>
>                 /* scale up */
> @@ -861,6 +884,7 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
>         u32 value;
>         int irq;
>         unsigned long clk_rate;
> +       unsigned int version;

No need to have a separate variable?

> +       version = (readl(i2c->regs + REG_CON) & VERSION_MASK) >> VERSION_SHIFT;
> +       if (version == RK3X_I2C_V0)
> +               i2c->ops.calc_clock = rk3x_i2c_v0_calc_clock;

value = readl(i2c->regs + REG_CON);
if ((value & VERSION_MASK) >> VERSION_SHIFT) == RK3X_I2C_V0)
  i2c->ops.calc_clock = rk3x_i2c_v0_calc_clock;

-- 
With Best Regards,
Andy Shevchenko


From andy.yan at rock-chips.com  Thu Jan 14 05:20:01 2016
From: andy.yan at rock-chips.com (Andy Yan)
Date: Thu, 14 Jan 2016 21:20:01 +0800
Subject: [PATCH 1/2] ARM: dts: rockchip: increase vdd_arm voltage for
 rk3066a-rayeager board
In-Reply-To: <1709463.R522kSjgXB@phil>
References: <1452774780-16493-1-git-send-email-andy.yan@rock-chips.com>
 <1452774860-16572-1-git-send-email-andy.yan@rock-chips.com>
 <1709463.R522kSjgXB@phil>
Message-ID: <5697A081.4060903@rock-chips.com>

Hi Heiko:

On 2016?01?14? 20:45, Heiko Stuebner wrote:
> Hi Andy,
>
> Am Donnerstag, 14. Januar 2016, 20:34:20 schrieb Andy Yan:
>> The current vdd_arm voltage is too low, increase it will make
>> the system more stable.
>>
>> Signed-off-by: Andy Yan <andy.yan at rock-chips.com>
>> ---
>>
>>   arch/arm/boot/dts/rk3066a-rayeager.dts | 8 ++++++++
>>   1 file changed, 8 insertions(+)
>>
>> diff --git a/arch/arm/boot/dts/rk3066a-rayeager.dts
>> b/arch/arm/boot/dts/rk3066a-rayeager.dts index 0bf6876..3d4c104 100644
>> --- a/arch/arm/boot/dts/rk3066a-rayeager.dts
>> +++ b/arch/arm/boot/dts/rk3066a-rayeager.dts
>> @@ -159,6 +159,14 @@
>>
>>   &cpu0 {
>>   	cpu0-supply = <&vdd_arm>;
>> +	operating-points = <
>> +		/* kHz    uV */
>> +		1008000 1125000
>> +		816000  1125000
>> +		600000  1100000
>> +		504000  1100000
>> +		312000  1075000
>> +	>;
>>   };
>>
>>   &emac {
> Does this only apply to the rayeager-board / px2 soc or for all rk3066a/px2
> boards? I.e. the original values come from some sdk kernel and I might've
> transcribed that wrong at the time and I remember having problems trying to
> reach higher frequencies as well.
>
> So could you check what the full operating points looks like for both and if
> this can move to the rk3066a.dtsi, as both should be able to reach at least
> 1.4GHz / 1.6GHz.
>
>
> Thanks
> Heiko
>
> ______

     I have checked the sdk kernel, the opp for the sdk board is like 
bellow:

  /**
  * dvfs_cpu_logic_table: table for arm and logic dvfs
  * @frequency   : arm frequency
  * @cpu_volt    : arm voltage depend on frequency
  * @logic_volt  : logic voltage arm requests depend on frequency
  * comments     : min arm/logic voltage
  */
static struct dvfs_arm_table dvfs_cpu_logic_table[] = {
         {.frequency = 252 * 1000,       .cpu_volt = 1075 * 1000,        
.logic_volt = 1125 * 1000},//0.975V/1.000V
         {.frequency = 504 * 1000,       .cpu_volt = 1100 * 1000,        
.logic_volt = 1125 * 1000},//0.975V/1.000V
         {.frequency = 816 * 1000,       .cpu_volt = 1125 * 1000,        
.logic_volt = 1150 * 1000},//1.000V/1.025V
         {.frequency = 1008 * 1000,      .cpu_volt = 1125 * 1000,        
.logic_volt = 1150 * 1000},//1.025V/1.050V
         {.frequency = 1200 * 1000,      .cpu_volt = 1175 * 1000,        
.logic_volt = 1200 * 1000},//1.100V/1.050V
         {.frequency = 1272 * 1000,      .cpu_volt = 1225 * 1000,        
.logic_volt = 1200 * 1000},//1.150V/1.100V
         {.frequency = 1416 * 1000,      .cpu_volt = 1300 * 1000,        
.logic_volt = 1200 * 1000},//1.225V/1.100V
         {.frequency = 1512 * 1000,      .cpu_volt = 1350 * 1000,        
.logic_volt = 1250 * 1000},//1.300V/1.150V
         {.frequency = 1608 * 1000,      .cpu_volt = 1425 * 1000,        
.logic_volt = 1300 * 1000},//1.325V/1.175V
         {.frequency = CPUFREQ_TABLE_END},
};

  This table is made base on mass stress test. So is a safe opp table 
for most rk3066a base boards. But it some
  board is not so well in power supply circuit design, the may also need 
to adjust it.

  From the table we see that the logic_voltage need to increase  as the 
cpufreq increase. But it seems that the current mainline kernel does not 
support adjust the logic voltage when cpufreq change. So we can only 
scale the cpufreq to 1.4GHZ by fixed the logic_volt at 1.2V.

  If you like, I can move it to rk3066a.dtsi with the max freq at 
1008MHZ, as I haven't  test to 1.4GHZ  with the mainline kernel. Or give 
me some time to test to see if the rayeager board can run to 1.4G stable 
with the mainline .
> _________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip
>
>
>
>





From andy.shevchenko at gmail.com  Thu Jan 14 05:29:30 2016
From: andy.shevchenko at gmail.com (Andy Shevchenko)
Date: Thu, 14 Jan 2016 15:29:30 +0200
Subject: [PATCH v3 3/4] i2c: rk3x: new method to caculate i2c clocks
In-Reply-To: <1452774699-57455-4-git-send-email-david.wu@rock-chips.com>
References: <1452774699-57455-1-git-send-email-david.wu@rock-chips.com>
 <1452774699-57455-4-git-send-email-david.wu@rock-chips.com>
Message-ID: <CAHp75VcweSAdUbe8V1UxsNXeQ-7gpeMS8DAdVe1MiCXVPHNjLA@mail.gmail.com>

On Thu, Jan 14, 2016 at 2:31 PM, David Wu <david.wu at rock-chips.com> wrote:
> There was an timing issue about "repeated start" time at the I2C
> controller of version0, controller appears to drop SDA at .875x (7/8)
> programmed clk high. The rule(.875x) isn't enough to meet tSU;STA
> requirements on 100k's Standard-mode. To resolve this issue,
> data_upd_st, start_setup_cnt and stop_setup_cnt configs for I2C
> timing information are added, new rules are designed to calculate
> the timing information at new v1.

> --- a/drivers/i2c/busses/i2c-rk3x.c
> +++ b/drivers/i2c/busses/i2c-rk3x.c
> @@ -58,10 +58,15 @@ enum {
>  #define REG_CON_LASTACK   BIT(5) /* 1: send NACK after last received byte */
>  #define REG_CON_ACTACK    BIT(6) /* 1: stop if NACK is received */
>
> +#define REG_CON_SDA_CNT(cnt)  ((cnt) << 8)
> +#define REG_CON_STA_CNT(cnt)  ((cnt) << 12)
> +#define REG_CON_STO_CNT(cnt)  ((cnt) << 14)
> +
>  #define VERSION_MASK     GENMASK(31, 16)
>  #define VERSION_SHIFT    16
>
>  #define RK3X_I2C_V0      0x0
> +#define RK3X_I2C_V1      0x1
>
>  /* REG_MRXADDR bits */
>  #define REG_MRXADDR_VALID(x) BIT(24 + (x)) /* [x*8+7:x*8] of MRX[R]ADDR valid */
> @@ -99,10 +104,16 @@ struct rk3x_i2c_soc_data {
>   * struct rk3x_priv_i2c_timings - rk3x I2C timing information
>   * @div_low: Divider output for low
>   * @div_high: Divider output for high
> + * @thd_sda_count: SDA update point config used to adjust sda setup/hold time
> + * @tsu_sta_count: Start setup config for setup start time and hold start time
> + * @tsu_sto_count: Stop setup config for setup stop time
>   */
>  struct rk3x_priv_i2c_timings {
>         unsigned long div_low;
>         unsigned long div_high;
> +       unsigned int thd_sda_count;
> +       unsigned int tsu_sta_count;
> +       unsigned int tsu_sto_count;
>  };

And in this (or even separate) patch makes sense to introduce
extension structure, which is struct rk3x_priv_i2c_timings.

>  struct rk3x_i2c_ops {
> @@ -154,6 +165,13 @@ static inline u32 i2c_readl(struct rk3x_i2c *i2c, unsigned int offset)
>         return readl(i2c->regs + offset);
>  }
>
> +static inline u32 rk3x_i2c_get_con_count(struct rk3x_i2c *i2c)
> +{
> +       return REG_CON_SDA_CNT(i2c->t_priv.thd_sda_count) |
> +              REG_CON_STA_CNT(i2c->t_priv.tsu_sta_count) |
> +              REG_CON_STO_CNT(i2c->t_priv.tsu_sto_count);
> +}
> +
>  /* Reset all interrupt pending bits */
>  static inline void rk3x_i2c_clean_ipd(struct rk3x_i2c *i2c)
>  {
> @@ -165,13 +183,13 @@ static inline void rk3x_i2c_clean_ipd(struct rk3x_i2c *i2c)
>   */
>  static void rk3x_i2c_start(struct rk3x_i2c *i2c)
>  {
> -       u32 val;
> +       u32 val = rk3x_i2c_get_con_count(i2c);
>
>         rk3x_i2c_clean_ipd(i2c);
>         i2c_writel(i2c, REG_INT_START, REG_IEN);
>
>         /* enable adapter with correct mode, send START condition */
> -       val = REG_CON_EN | REG_CON_MOD(i2c->mode) | REG_CON_START;
> +       val = val | REG_CON_EN | REG_CON_MOD(i2c->mode) | REG_CON_START;
>
>         /* if we want to react to NACK, set ACTACK bit */
>         if (!(i2c->msg->flags & I2C_M_IGNORE_NAK))
> @@ -212,7 +230,7 @@ static void rk3x_i2c_stop(struct rk3x_i2c *i2c, int error)
>                  * get the intended effect by resetting its internal state
>                  * and issuing an ordinary START.
>                  */
> -               i2c_writel(i2c, 0, REG_CON);
> +               i2c_writel(i2c, rk3x_i2c_get_con_count(i2c), REG_CON);
>
>                 /* signal that we are finished with the current msg */
>                 wake_up(&i2c->wait);
> @@ -630,6 +648,211 @@ static int rk3x_i2c_v0_calc_clock(unsigned long clk_rate,
>         return ret;
>  }
>
> +/**
> + * Calculate timing clock info values for desired SCL frequency
> + *
> + * @clk_rate: I2C input clock rate
> + * @t_input: Known I2C timing information
> + * @t_output: Caculated rk3x private timing information that would
> + * be written into regs
> + * Returns: 0 on success, -EINVAL if the goal SCL rate is too slow. In that case
> + * a best-effort divider value is returned in divs. If the target rate is
> + * too high, we silently use the highest possible rate.
> + * The following formulas are v1's method to calculate clock.
> + *
> + * l = divl + 1;
> + * h = divh + 1;
> + * s = data_upd_st + 1;
> + * u = start_setup_cnt + 1;
> + * p = stop_setup_cnt + 1;
> + * T = Tclk_i2c;
> +
> + * tHigh = 8 * h * T;
> + * tLow = 8 * l * T;
> +
> + * tHD;sda = (l * s + 1) * T;
> + * tSU;sda = [(8 - s) * l + 1] * T;
> + * tI2C = 8 * (l + h) * T;
> +
> + * tSU;sta = (8h * u + 1) * T;
> + * tHD;sta = [8h * (u + 1) - 1] * T;
> + * tSU;sto = (8h * p + 1) * T;
> + */
> +static int rk3x_i2c_v1_calc_clock(unsigned long clk_rate,
> +                                 struct i2c_timings *t_input,
> +                                 struct rk3x_priv_i2c_timings *t_output)
> +{

I see some similarities with existing code for v0. Can be refactored?

> +       unsigned long spec_min_low_ns, spec_min_high_ns;
> +       unsigned long spec_min_setup_start_ns, spec_min_stop_setup_ns;
> +       unsigned long spec_min_data_setup_ns, spec_max_data_hold_ns;
> +
> +       unsigned long min_low_ns, min_high_ns, min_total_ns;
> +       unsigned long min_setup_start_ns, min_setup_data_ns;
> +       unsigned long min_stop_setup_ns, max_hold_data_ns;
> +
> +       unsigned long clk_rate_khz, scl_rate_khz;
> +
> +       unsigned long min_low_div, min_high_div;
> +
> +       unsigned long min_div_for_hold, min_total_div;
> +       unsigned long extra_div, extra_low_div;
> +       unsigned long data_hd_cnt;
> +
> +       int ret = 0;
> +
> +       /* Support standard-mode and fast-mode */
> +       if (WARN_ON(t_input->bus_freq_hz > 400000))
> +               t_input->bus_freq_hz = 400000;
> +
> +       /* prevent scl_rate_khz from becoming 0 */
> +       if (WARN_ON(t_input->bus_freq_hz < 1000))
> +               t_input->bus_freq_hz = 1000;
> +
> +       /*
> +        * min_low_ns: The minimum number of ns we need to hold low to
> +        *             meet I2C specification, should include fall time.
> +        * min_high_ns: The minimum number of ns we need to hold high to
> +        *              meet I2C specification, should include rise time.
> +        */
> +       if (t_input->bus_freq_hz <= 100000) {
> +               spec_min_low_ns = 4700;
> +               spec_min_high_ns = 4000;
> +
> +               spec_min_setup_start_ns = 4700;
> +               spec_min_stop_setup_ns = 4000;
> +
> +               spec_min_data_setup_ns = 250;
> +                spec_max_data_hold_ns = 3450;
> +       } else if (t_input->bus_freq_hz <= 400000) {
> +               spec_min_low_ns = 1300;
> +               spec_min_high_ns = 600;
> +
> +               spec_min_setup_start_ns = 600;
> +               spec_min_stop_setup_ns = 600;
> +
> +               spec_min_data_setup_ns = 100;
> +               spec_max_data_hold_ns = 900;
> +       }
> +
> +       /* caculate min-divh and min-divl */
> +       clk_rate_khz = DIV_ROUND_UP(clk_rate, 1000);
> +       scl_rate_khz = t_input->bus_freq_hz / 1000;
> +       min_total_div = DIV_ROUND_UP(clk_rate_khz, scl_rate_khz * 8);
> +
> +       min_high_ns = t_input->scl_rise_ns + spec_min_high_ns;
> +       min_high_div = DIV_ROUND_UP(clk_rate_khz * min_high_ns, 8 * 1000000);
> +
> +       min_low_ns = t_input->scl_fall_ns + spec_min_low_ns;
> +       min_low_div = DIV_ROUND_UP(clk_rate_khz * min_low_ns, 8 * 1000000);
> +
> +       /* Final divh and divl must be greater than 0, otherwise the
> +        * hardware would not output the i2c clk.
> +        */
> +       if (min_high_div <= 1)
> +               min_high_div = 2;
> +       if (min_low_div <= 1)
> +               min_low_div = 2;
> +
> +       /* These are the min dividers needed for min hold times. */
> +       min_div_for_hold = (min_low_div + min_high_div);
> +       min_total_ns = min_low_ns + min_high_ns;
> +
> +       /*
> +        * This is the maximum divider so we don't go over the maximum.
> +        * We don't round up here (we round down) since this is a maximum.
> +        */
> +        if (min_div_for_hold >= min_total_div) {
> +               /*
> +                * Time needed to meet hold requirements is important.
> +                * Just use that.
> +                */
> +               t_output->div_low = min_low_div;
> +               t_output->div_high = min_high_div;
> +       } else {
> +               /*
> +                * We've got to distribute some time among the low and high
> +                * so we don't run too fast.
> +                * We'll try to split things up by the scale of min_low_div and
> +                * min_high_div, biasing slightly towards having a higher div
> +                * for low (spend more time low).
> +                */
> +               extra_div = min_total_div - min_div_for_hold;
> +               extra_low_div = DIV_ROUND_UP(min_low_div * extra_div,
> +                                            min_div_for_hold);
> +
> +               t_output->div_low = min_low_div + extra_low_div;
> +               t_output->div_high = min_high_div + (extra_div - extra_low_div);
> +       }
> +
> +       /*
> +        * calculate sda data hold count by the rules, thd_sda_count:3
> +        * is a appropriate value to reduce calculated times.
> +        * tHD;sda  = (l * s + 1) * T
> +        * tSU;sda = ((8 - s) * l + 1) * T
> +        */
> +       for (data_hd_cnt = 3; data_hd_cnt >= 0; data_hd_cnt--) {
> +               max_hold_data_ns =  DIV_ROUND_UP((data_hd_cnt
> +                                                * (t_output->div_low) + 1)
> +                                                * 1000000, clk_rate_khz);
> +               min_setup_data_ns =  DIV_ROUND_UP(((8 - data_hd_cnt)
> +                                                * (t_output->div_low) + 1)
> +                                                * 1000000, clk_rate_khz);
> +               if ((max_hold_data_ns < spec_max_data_hold_ns) &&
> +                   (min_setup_data_ns > spec_min_data_setup_ns)) {
> +                       t_output->thd_sda_count = data_hd_cnt;
> +                       break;
> +               }
> +       }
> +
> +       /*
> +        * calculate start setup count, and we aren't care tHD;STA.
> +        * If the start setup count meets the rule of tSU;sta, it also
> +        * meets the rule of tHD;STA.
> +        * tSU;sta = (8h * u + 1) * T
> +        * tHD;sta = [8h * (u + 1) - 1] * T
> +        */
> +       min_setup_start_ns = t_input->scl_rise_ns + spec_min_setup_start_ns;
> +       t_output->tsu_sta_count = DIV_ROUND_UP(clk_rate_khz * min_setup_start_ns
> +                          - 1000000, 8 * 1000000 * (t_output->div_high));
> +
> +       /*
> +        * calculate start setup count by the rule:
> +        * tSU;sto =(8h * p + 1) * T
> +        */
> +       min_stop_setup_ns = t_input->scl_rise_ns + spec_min_stop_setup_ns;
> +       t_output->tsu_sto_count = DIV_ROUND_UP(clk_rate_khz * min_stop_setup_ns
> +                          - 1000000, 8 * 1000000 * (t_output->div_high));
> +
> +       /*
> +        * Adjust to the fact that the hardware has an implicit "+1".
> +        * NOTE: Above calculations always produce div_low > 0 and div_high > 0.
> +        */
> +       t_output->div_low -= 1;
> +       t_output->div_high -= 1;
> +
> +       /* Maximum divider supported by hw is 0xffff */
> +       if (t_output->div_low > 0xffff) {
> +               t_output->div_low = 0xffff;
> +               ret = -EINVAL;
> +       }
> +
> +       if (t_output->div_high > 0xffff) {
> +               t_output->div_high = 0xffff;
> +               ret = -EINVAL;
> +       }
> +
> +       /*
> +        * Adjust to the fact that the hardware has an implicit "+1".
> +        * NOTE: Above calculations always produce thd_sda_count > 0,
> +        * tsu_sta_count > 0 and tsu_sta_count > 0.
> +        */
> +       t_output->thd_sda_count -= 1;
> +       t_output->tsu_sta_count -= 1;
> +       t_output->tsu_sto_count -= 1;
> +
> +       return ret;
> +}
> +
>  static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate)
>  {
>         u64 t_low_ns, t_high_ns;
> @@ -829,7 +1052,8 @@ static int rk3x_i2c_xfer(struct i2c_adapter *adap,
>
>                         /* Force a STOP condition without interrupt */
>                         i2c_writel(i2c, 0, REG_IEN);
> -                       i2c_writel(i2c, REG_CON_EN | REG_CON_STOP, REG_CON);
> +                       i2c_writel(i2c, rk3x_i2c_get_con_count(i2c) |
> +                                       REG_CON_EN | REG_CON_STOP, REG_CON);
>
>                         i2c->state = STATE_IDLE;
>
> @@ -969,7 +1193,9 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
>         platform_set_drvdata(pdev, i2c);
>
>         version = (readl(i2c->regs + REG_CON) & VERSION_MASK) >> VERSION_SHIFT;
> -       if (version == RK3X_I2C_V0)
> +       if (version == RK3X_I2C_V1)
> +               i2c->ops.calc_clock = rk3x_i2c_v1_calc_clock;
> +       else
>                 i2c->ops.calc_clock = rk3x_i2c_v0_calc_clock;

Perhaps time to use switch-case:

switch ((value & MASK) >> SHIFT) {
case V1:
v1();
break;
case V0:
default:
v0();
break;
}

-- 
With Best Regards,
Andy Shevchenko


From eddie.cai.kernel at gmail.com  Thu Jan 14 05:50:53 2016
From: eddie.cai.kernel at gmail.com (Eddie Cai)
Date: Thu, 14 Jan 2016 21:50:53 +0800
Subject: [PATCH 1/2] ARM: dts: rockchip: increase vdd_arm voltage for
 rk3066a-rayeager board
In-Reply-To: <5697A081.4060903@rock-chips.com>
References: <1452774780-16493-1-git-send-email-andy.yan@rock-chips.com>
 <1452774860-16572-1-git-send-email-andy.yan@rock-chips.com>
 <1709463.R522kSjgXB@phil> <5697A081.4060903@rock-chips.com>
Message-ID: <CANCyOWak0wMNRPxkJtE2DJ1UvCneWxuNXjwjxjM3+_Jjuaahvw@mail.gmail.com>

Hi Heiko

I don't want open source version kernel support too high frequency.
Many people use RK3066/PX2 on their board. We don't know if their
layout is good enough to support higher frequency. ie. bad layout will
cause more voltage drop on the VDD_CPU power rail. So it may need
higher voltage. I think stable is more important here. People want
higher frequency can add more opp depend on their board's layout.

2016-01-14 21:20 GMT+08:00 Andy Yan <andy.yan at rock-chips.com>:
> Hi Heiko:
>
>
> On 2016?01?14? 20:45, Heiko Stuebner wrote:
>>
>> Hi Andy,
>>
>> Am Donnerstag, 14. Januar 2016, 20:34:20 schrieb Andy Yan:
>>>
>>> The current vdd_arm voltage is too low, increase it will make
>>> the system more stable.
>>>
>>> Signed-off-by: Andy Yan <andy.yan at rock-chips.com>
>>> ---
>>>
>>>   arch/arm/boot/dts/rk3066a-rayeager.dts | 8 ++++++++
>>>   1 file changed, 8 insertions(+)
>>>
>>> diff --git a/arch/arm/boot/dts/rk3066a-rayeager.dts
>>> b/arch/arm/boot/dts/rk3066a-rayeager.dts index 0bf6876..3d4c104 100644
>>> --- a/arch/arm/boot/dts/rk3066a-rayeager.dts
>>> +++ b/arch/arm/boot/dts/rk3066a-rayeager.dts
>>> @@ -159,6 +159,14 @@
>>>
>>>   &cpu0 {
>>>         cpu0-supply = <&vdd_arm>;
>>> +       operating-points = <
>>> +               /* kHz    uV */
>>> +               1008000 1125000
>>> +               816000  1125000
>>> +               600000  1100000
>>> +               504000  1100000
>>> +               312000  1075000
>>> +       >;
>>>   };
>>>
>>>   &emac {
>>
>> Does this only apply to the rayeager-board / px2 soc or for all
>> rk3066a/px2
>> boards? I.e. the original values come from some sdk kernel and I might've
>> transcribed that wrong at the time and I remember having problems trying
>> to
>> reach higher frequencies as well.
>>
>> So could you check what the full operating points looks like for both and
>> if
>> this can move to the rk3066a.dtsi, as both should be able to reach at
>> least
>> 1.4GHz / 1.6GHz.
>>
>>
>> Thanks
>> Heiko
>>
>> ______
>
>
>     I have checked the sdk kernel, the opp for the sdk board is like bellow:
>
>  /**
>  * dvfs_cpu_logic_table: table for arm and logic dvfs
>  * @frequency   : arm frequency
>  * @cpu_volt    : arm voltage depend on frequency
>  * @logic_volt  : logic voltage arm requests depend on frequency
>  * comments     : min arm/logic voltage
>  */
> static struct dvfs_arm_table dvfs_cpu_logic_table[] = {
>         {.frequency = 252 * 1000,       .cpu_volt = 1075 * 1000,
> .logic_volt = 1125 * 1000},//0.975V/1.000V
>         {.frequency = 504 * 1000,       .cpu_volt = 1100 * 1000,
> .logic_volt = 1125 * 1000},//0.975V/1.000V
>         {.frequency = 816 * 1000,       .cpu_volt = 1125 * 1000,
> .logic_volt = 1150 * 1000},//1.000V/1.025V
>         {.frequency = 1008 * 1000,      .cpu_volt = 1125 * 1000,
> .logic_volt = 1150 * 1000},//1.025V/1.050V
>         {.frequency = 1200 * 1000,      .cpu_volt = 1175 * 1000,
> .logic_volt = 1200 * 1000},//1.100V/1.050V
>         {.frequency = 1272 * 1000,      .cpu_volt = 1225 * 1000,
> .logic_volt = 1200 * 1000},//1.150V/1.100V
>         {.frequency = 1416 * 1000,      .cpu_volt = 1300 * 1000,
> .logic_volt = 1200 * 1000},//1.225V/1.100V
>         {.frequency = 1512 * 1000,      .cpu_volt = 1350 * 1000,
> .logic_volt = 1250 * 1000},//1.300V/1.150V
>         {.frequency = 1608 * 1000,      .cpu_volt = 1425 * 1000,
> .logic_volt = 1300 * 1000},//1.325V/1.175V
>         {.frequency = CPUFREQ_TABLE_END},
> };
>
>  This table is made base on mass stress test. So is a safe opp table for
> most rk3066a base boards. But it some
>  board is not so well in power supply circuit design, the may also need to
> adjust it.
>
>  From the table we see that the logic_voltage need to increase  as the
> cpufreq increase. But it seems that the current mainline kernel does not
> support adjust the logic voltage when cpufreq change. So we can only scale
> the cpufreq to 1.4GHZ by fixed the logic_volt at 1.2V.
>
>  If you like, I can move it to rk3066a.dtsi with the max freq at 1008MHZ, as
> I haven't  test to 1.4GHZ  with the mainline kernel. Or give me some time to
> test to see if the rayeager board can run to 1.4G stable with the mainline .
>
>> _________________________________________
>> Linux-rockchip mailing list
>> Linux-rockchip at lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-rockchip
>>
>>
>>
>>
>
>
>
>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip


From daniel at ffwll.ch  Thu Jan 14 06:20:47 2016
From: daniel at ffwll.ch (Daniel Vetter)
Date: Thu, 14 Jan 2016 15:20:47 +0100
Subject: [RFC/PATCH] drm/rockchip: don't wait for vblank if fb hasn't
 changed
In-Reply-To: <5697606D.50603@rock-chips.com>
References: <20160113143425.363ce07a.john@metanate.com>
 <20160113154005.GT19130@phenom.ffwll.local>
 <20160113155529.6048b2c1.john@metanate.com>
 <20160113162156.GA19130@phenom.ffwll.local>
 <20160113164038.5fcf670f.john@metanate.com>
 <20160113171917.GC19130@phenom.ffwll.local>
 <20160113173954.52e688fc.john@metanate.com>
 <5696F6F4.5060908@rock-chips.com>
 <CAKMK7uGQ47eLS2HxUP2+tQcf5cZm+eoGryCRrBzA2b8=77ox-Q@mail.gmail.com>
 <5697606D.50603@rock-chips.com>
Message-ID: <20160114142047.GD19130@phenom.ffwll.local>

On Thu, Jan 14, 2016 at 04:46:37PM +0800, Mark yao wrote:
> On 2016?01?14? 16:32, Daniel Vetter wrote:
> >On Thu, Jan 14, 2016 at 2:16 AM, Mark yao <mark.yao at rock-chips.com> wrote:
> >>On 2016?01?14? 01:39, John Keeping wrote:
> >>>On Wed, 13 Jan 2016 18:19:17 +0100, Daniel Vetter wrote:
> >>>
> >>>>On Wed, Jan 13, 2016 at 04:40:38PM +0000, John Keeping wrote:
> >>>>>On Wed, 13 Jan 2016 17:21:56 +0100, Daniel Vetter wrote:
> >>>>>
> >>>>>>On Wed, Jan 13, 2016 at 03:55:29PM +0000, John Keeping wrote:
> >>>>>>>On Wed, 13 Jan 2016 16:40:05 +0100, Daniel Vetter wrote:
> >>>>>>>
> >>>>>>>>On Wed, Jan 13, 2016 at 02:34:25PM +0000, John Keeping wrote:
> >>>>>>>>>On Wed, 13 Jan 2016 15:23:20 +0100, Daniel Vetter wrote:
> >>>>>>>>>
> >>>>>>>>>>On Wed, Jan 13, 2016 at 12:53:34PM +0000, John Keeping wrote:
> >>>>>>>>>>>As commented in drm_atomic_helper_wait_for_vblanks(), userspace
> >>>>>>>>>>>relies on cursor ioctls being unsynced.  Converting the rockchip
> >>>>>>>>>>>driver to atomic has significantly impacted cursor performance by
> >>>>>>>>>>>making every cursor update wait for vblank.
> >>>>>>>>>>>
> >>>>>>>>>>>By skipping the vblank sync when the framebuffer has not changed
> >>>>>>>>>>>(as is done in drm_atomic_helper_wait_for_vblanks()) we can avoid
> >>>>>>>>>>>this for the common case of moving the cursor and only need to
> >>>>>>>>>>>delay the cursor ioctl when the cursor icon changes.
> >>>>>>>>>>>
> >>>>>>>>>>>I originally inserted a check on legacy_cursor_update as well, but
> >>>>>>>>>>>that caused a storm of iommu page faults.  I didn't investigate
> >>>>>>>>>>>the
> >>>>>>>>>>>cause of those since this change gives enough of a performance
> >>>>>>>>>>>improvement for my use case.
> >>>>>>>>>>>
> >>>>>>>>>>>This is RFC because of that and because the framebuffer_changed()
> >>>>>>>>>>>function is copied from drm_atomic_helper.c as a quick way to test
> >>>>>>>>>>>the result.
> >>>>>>>>>>>
> >>>>>>>>>>>Signed-off-by: John Keeping <john at metanate.com>
> >>>>>>>>>>>---
> >>>>>>>>>>>   drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 27
> >>>>>>>>>>>+++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2
> >>>>>>>>>>>deletions(-)
> >>>>>>>>>>>
> >>>>>>>>>>>diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> >>>>>>>>>>>b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index
> >>>>>>>>>>>f784488..8fd9821
> >>>>>>>>>>>100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> >>>>>>>>>>>+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> >>>>>>>>>>>@@ -177,8 +177,28 @@ static void
> >>>>>>>>>>>rockchip_crtc_wait_for_update(struct drm_crtc *crtc)
> >>>>>>>>>>>crtc_funcs->wait_for_update(crtc); }
> >>>>>>>>>>>   +static bool framebuffer_changed(struct drm_device *dev,
> >>>>>>>>>>>+                               struct drm_atomic_state
> >>>>>>>>>>>*old_state,
> >>>>>>>>>>>+                               struct drm_crtc *crtc)
> >>>>>>>>>>>+{
> >>>>>>>>>>>+       struct drm_plane *plane;
> >>>>>>>>>>>+       struct drm_plane_state *old_plane_state;
> >>>>>>>>>>>+       int i;
> >>>>>>>>>>>+
> >>>>>>>>>>>+       for_each_plane_in_state(old_state, plane, old_plane_state,
> >>>>>>>>>>>i) {
> >>>>>>>>>>>+               if (plane->state->crtc != crtc &&
> >>>>>>>>>>>+                   old_plane_state->crtc != crtc)
> >>>>>>>>>>>+                       continue;
> >>>>>>>>>>>+
> >>>>>>>>>>>+               if (plane->state->fb != old_plane_state->fb)
> >>>>>>>>>>>+                       return true;
> >>>>>>>>>>>+       }
> >>>>>>>>>>>+
> >>>>>>>>>>>+       return false;
> >>>>>>>>>>>+}
> >>>>>>>>>>Please don't hand-roll logic that affects semantics like this.
> >>>>>>>>>>Instead
> >>>>>>>>>>please use drm_atomic_helper_wait_for_vblanks(), which should do
> >>>>>>>>>>this
> >>>>>>>>>>correctly for you.
> >>>>>>>>>>
> >>>>>>>>>>If that's not the case then we need to improve the generic helper,
> >>>>>>>>>>or
> >>>>>>>>>>figure out what's different with rockhip.
> >>>>>>>>>According to commit 63ebb9f (drm/rockchip: Convert to support atomic
> >>>>>>>>>API) it's because rockchip doesn't have a hardware vblank counter.
> >>>>>>>>>
> >>>>>>>>>I'm not entirely clear on why this prevents the use of
> >>>>>>>>>drm_atomic_helper_wait_for_vblanks().
> >>>>>>>>Hm, that commit isn't terribly helpful. If that's really needed then
> >>>>>>>>imo I
> >>>>>>>>think we should extract a
> >>>>>>>>"drm_atomic_helper_plane_needs_vblank_wait()"
> >>>>>>>>helper that's used by both. But since rockchip does vblank_get/put
> >>>>>>>>calls
> >>>>>>>>I'd hope vblanks actually work correctly. And then the helper should
> >>>>>>>>work
> >>>>>>>>too.
> >>>>>>>I tried switching the call to rockchip_crtc_wait_for_update() to
> >>>>>>>drm_atomic_helper_wait_for_vblanks() and it works fine until I switch
> >>>>>>>the buffer associated with a cursor, at which point I get iommu page
> >>>>>>>faults, presumably because the GEM buffer is unreferenced too early.
> >>>>>>>
> >>>>>>>AFAICT the buffer will be released via drm_atomic_state_free()
> >>>>>>>unconditionally, but I suspect I'm missing something since that would
> >>>>>>>mean every driver would hit a similar problem.
> >>>>>>Yeah, with the helper we always skip, which means when the cursor bo
> >>>>>>changes you indeed unmap too early. So can't even share the overall
> >>>>>>condition, but we could definitely share the little framebuffer_changed
> >>>>>>helper.
> >>>>>That leaves me with the question: why do other atomic drivers work?
> >>>>>
> >>>>>If drm_atomic_helper_wait_for_vblanks() skipping vblanks results in the
> >>>>>cursor bo being unmapped too early for rockchip, why is it not unmapped
> >>>>>too early for all of the other drivers using that helper?
> >>>>It's unmapped too early for everyone, it's just that normally that
> >>>>doesn't
> >>>>result in a fireworks show. What we maybe could/should do is do the
> >>>>unmapping asynchronously, but that runs into the overall "current atomic
> >>>>helpers don't do async yet" problem. Might be a good point to start
> >>>>fixing
> >>>>this up though.
> >>>OK, thanks, I think I'm beginning to understand how this all fits
> >>>together.
> >>>
> >>>It looks like there are two options for me to get reasonable cursor
> >>>performance on rockchip in the short term:
> >>>
> >>>1) Export the current framebuffer_changed() function as
> >>>     drm_atomic_helper_framebuffer_changed() and use it in
> >>>     rockchip_crtc_wait_for_update().
> >>>
> >>>2) Add a mechanism to suppress the legacy_cursor_update check in
> >>>     drm_atomic_helper_wait_for_vblanks() and switch the rockchip driver
> >>>     over to it.
> >>>
> >>>In both of these cases we're only restoring the unsynced cursor ioctls
> >>>behaviour when the cursor is moved but it will still be expensive when
> >>>the cursor bo changes.  That gives sufficient performance in my testing.
> >>>
> >>>
> >>>
> >>Thanks for point that.
> >>
> >>because rockchip not support hardware vblank counter, use
> >>drm_atomic_helper_wait_for_vblanks have under issues:
> >>
> >>                                              | <-- HW vsync irq and reg take
> >>effect
> >>             plane_commit  --->  |
> >>      get_vblank and wait ->   |
> >>                                              | <-- handle_vblank,
> >>vblank->count + 1
> >>                  cleanup_fb   ---> |
> >>               iommu crash  --->  |
> >>                                              | <-- HW vsync irq and reg take
> >>effect
> >>there is no hardware vblank counter on rockchip vop, we can't ensure the
> >>consistency of reg take effect and vblank->count,
> >>if plane commit hit into the period of  reg take effect and vblank->count,
> >>cleanup_fb happen before old_fb swap out from vop,
> >>then iommu crash.
> >>
> >>That is why I special the wait_for_vblanks, we need check the reg really
> >>take effect before clean up old fb.
> >>at vop_win_pending_is_complete function, check win enable and win address,
> >>to ensure that.
> >>
> >>Not only rockchip drm do that thing:
> >>
> >>exynos also check address before cleanup fb
> >>         if (start == start_s)
> >>             exynos_drm_crtc_finish_update(ctx->crtc, plane);
> >>
> >>Thanks.
> >Do you have a scanline counter or something similar at least? Any
> >other indication about how far along the chip is with scanning out? We
> >use that in i915 to avoid races with the interrupt handler and detect
> >this w/a scenario.
> >
> >I think if you have a scanline counter then it should magically work,
> >since the vblank code will realize that you're already past the last
> >vblank interrupt and /should/ have incremented already. Or something
> >like that.
> >
> >Otherwise if this is common we might want to figure out how to solve
> >this in a generic way. It's one of these problems that will make
> >generic async support almost impossible.
> >-Daniel
> 
> No, both rk3288 or rk3036 not support hardware vblank counter and scanline
> counter.
> 
> At android side, we use same way, check address and enable bit to ensure
> register take effect.
> 
> On future chips,  scanline counter and hardware counter would be support,
> but not now.

Ugh. Oh well, there's not really anything we can do in core nore helpers
to make this easier for drivers. This really only can be fixed sensibly at
the hardware level.

So yeah I think exposing framebuffer_changed as a helper is the way to go
here.

Thanks, Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


From john at metanate.com  Thu Jan 14 06:39:39 2016
From: john at metanate.com (John Keeping)
Date: Thu, 14 Jan 2016 14:39:39 +0000
Subject: [PATCH 0/3] drm/rockchip: fix cursor performance with atomic
In-Reply-To: <20160114142047.GD19130@phenom.ffwll.local>
References: <20160114142047.GD19130@phenom.ffwll.local>
Message-ID: <cover.1452782114.git.john@metanate.com>

On Thu, 14 Jan 2016 15:20:47 +0100, Daniel Vetter wrote:

> Ugh. Oh well, there's not really anything we can do in core nore helpers
> to make this easier for drivers. This really only can be fixed sensibly at
> the hardware level.
> 
> So yeah I think exposing framebuffer_changed as a helper is the way to go
> here.

OK, here's a series to do that.  I also added a comment to
rockchip_atomic_wait_for_complete() explaining why we can't use
drm_atomic_helper_wait_for_vblanks().

John Keeping (3):
  drm/atomic-helper: Export framebuffer_changed()
  drm/rockchip: don't wait for vblank if fb hasn't changed
  drm/rockchip: explain why we can't wait_for_vblanks

 drivers/gpu/drm/drm_atomic_helper.c        | 24 ++++++++++++++++++++----
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 14 ++++++++++++--
 include/drm/drm_atomic_helper.h            |  4 ++++
 3 files changed, 36 insertions(+), 6 deletions(-)

-- 
2.7.0.226.gfe986fe



From john at metanate.com  Thu Jan 14 06:39:41 2016
From: john at metanate.com (John Keeping)
Date: Thu, 14 Jan 2016 14:39:41 +0000
Subject: [PATCH 2/3] drm/rockchip: don't wait for vblank if fb hasn't changed
In-Reply-To: <cover.1452782114.git.john@metanate.com>
References: <cover.1452782114.git.john@metanate.com>
Message-ID: <2c23f18b4704d4f7126a0300d8875222502deb99.1452782114.git.john@metanate.com>

As commented in drm_atomic_helper_wait_for_vblanks(), userspace relies
on cursor ioctls being unsynced.  Converting the rockchip driver to
atomic has significantly impacted cursor performance by making every
cursor update wait for vblank.

By skipping the vblank sync when the framebuffer has not changed (as is
done in drm_atomic_helper_wait_for_vblanks()) we can avoid this for the
common case of moving the cursor and only need to delay the cursor ioctl
when the cursor icon changes.

We cannot add the check on legacy_cursor_update since that results in
the cursor bo being unreferenced while the hardware may still be reading
it.  Fully supporting unsynced cursor updates is left for the future
when the atomic helper framework supports async updates.

Signed-off-by: John Keeping <john at metanate.com>
---
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
index f784488..679d23a 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -178,7 +178,7 @@ static void rockchip_crtc_wait_for_update(struct drm_crtc *crtc)
 }
 
 static void
-rockchip_atomic_wait_for_complete(struct drm_atomic_state *old_state)
+rockchip_atomic_wait_for_complete(struct drm_device *dev, struct drm_atomic_state *old_state)
 {
 	struct drm_crtc_state *old_crtc_state;
 	struct drm_crtc *crtc;
@@ -194,6 +194,10 @@ rockchip_atomic_wait_for_complete(struct drm_atomic_state *old_state)
 		if (!crtc->state->active)
 			continue;
 
+		if (!drm_atomic_helper_framebuffer_changed(dev,
+				old_state, crtc))
+			continue;
+
 		ret = drm_crtc_vblank_get(crtc);
 		if (ret != 0)
 			continue;
@@ -241,7 +245,7 @@ rockchip_atomic_commit_complete(struct rockchip_atomic_commit *commit)
 
 	drm_atomic_helper_commit_planes(dev, state, true);
 
-	rockchip_atomic_wait_for_complete(state);
+	rockchip_atomic_wait_for_complete(dev, state);
 
 	drm_atomic_helper_cleanup_planes(dev, state);
 
-- 
2.7.0.226.gfe986fe



From john at metanate.com  Thu Jan 14 06:39:40 2016
From: john at metanate.com (John Keeping)
Date: Thu, 14 Jan 2016 14:39:40 +0000
Subject: [PATCH 1/3] drm/atomic-helper: Export framebuffer_changed()
In-Reply-To: <cover.1452782114.git.john@metanate.com>
References: <cover.1452782114.git.john@metanate.com>
Message-ID: <55e84a6ee82c0fbd796bb67a630781daa5bdcae0.1452782114.git.john@metanate.com>

The Rockchip driver cannot use drm_atomic_helper_wait_for_vblanks()
because it has hardware counters for neither vblanks nor scanlines.

In order to simplify re-implementing the functionality for this driver,
export the framebuffer_changed() helper so it can be reused.

Signed-off-by: John Keeping <john at metanate.com>
---
 drivers/gpu/drm/drm_atomic_helper.c | 24 ++++++++++++++++++++----
 include/drm/drm_atomic_helper.h     |  4 ++++
 2 files changed, 24 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 268d37f..7449293 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -948,9 +948,23 @@ static void wait_for_fences(struct drm_device *dev,
 	}
 }
 
-static bool framebuffer_changed(struct drm_device *dev,
-				struct drm_atomic_state *old_state,
-				struct drm_crtc *crtc)
+/**
+ * drm_atomic_helper_framebuffer_changed - check if framebuffer has changed
+ * @dev: DRM device
+ * @old_state: atomic state object with old state structures
+ * @crtc: DRM crtc
+ *
+ * Checks whether the framebuffer used for this CRTC changes as a result of
+ * the atomic update.  This is useful for drivers which cannot use
+ * drm_atomic_helper_wait_for_vblanks() and need to reimplement its
+ * functionality.
+ *
+ * Returns:
+ * true if the framebuffer changed.
+ */
+bool drm_atomic_helper_framebuffer_changed(struct drm_device *dev,
+					   struct drm_atomic_state *old_state,
+					   struct drm_crtc *crtc)
 {
 	struct drm_plane *plane;
 	struct drm_plane_state *old_plane_state;
@@ -967,6 +981,7 @@ static bool framebuffer_changed(struct drm_device *dev,
 
 	return false;
 }
+EXPORT_SYMBOL(drm_atomic_helper_framebuffer_changed);
 
 /**
  * drm_atomic_helper_wait_for_vblanks - wait for vblank on crtcs
@@ -1001,7 +1016,8 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev,
 		if (old_state->legacy_cursor_update)
 			continue;
 
-		if (!framebuffer_changed(dev, old_state, crtc))
+		if (!drm_atomic_helper_framebuffer_changed(dev,
+				old_state, crtc))
 			continue;
 
 		ret = drm_crtc_vblank_get(crtc);
diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h
index a286cce..74fce78 100644
--- a/include/drm/drm_atomic_helper.h
+++ b/include/drm/drm_atomic_helper.h
@@ -42,6 +42,10 @@ int drm_atomic_helper_commit(struct drm_device *dev,
 			     struct drm_atomic_state *state,
 			     bool async);
 
+bool drm_atomic_helper_framebuffer_changed(struct drm_device *dev,
+					   struct drm_atomic_state *old_state,
+					   struct drm_crtc *crtc);
+
 void drm_atomic_helper_wait_for_vblanks(struct drm_device *dev,
 					struct drm_atomic_state *old_state);
 
-- 
2.7.0.226.gfe986fe



From john at metanate.com  Thu Jan 14 06:39:42 2016
From: john at metanate.com (John Keeping)
Date: Thu, 14 Jan 2016 14:39:42 +0000
Subject: [PATCH 3/3] drm/rockchip: explain why we can't wait_for_vblanks
In-Reply-To: <cover.1452782114.git.john@metanate.com>
References: <cover.1452782114.git.john@metanate.com>
Message-ID: <107bbc36a316ed0ddc7b5a8bcd9b6db6cbc71d4f.1452782114.git.john@metanate.com>

Signed-off-by: John Keeping <john at metanate.com>
---
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
index 679d23a..b267ce4 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -177,6 +177,12 @@ static void rockchip_crtc_wait_for_update(struct drm_crtc *crtc)
 		crtc_funcs->wait_for_update(crtc);
 }
 
+/*
+ * We can't use drm_atomic_helper_wait_for_vblanks() because rk3288 and rk3066
+ * have hardware counters for neither vblanks nor scanlines.  This function is
+ * equivalent but uses rockchip_crtc_wait_for_update() instead of waiting for
+ * vblank_count to change.
+ */
 static void
 rockchip_atomic_wait_for_complete(struct drm_device *dev, struct drm_atomic_state *old_state)
 {
-- 
2.7.0.226.gfe986fe



From daniel at ffwll.ch  Thu Jan 14 06:56:35 2016
From: daniel at ffwll.ch (Daniel Vetter)
Date: Thu, 14 Jan 2016 15:56:35 +0100
Subject: [PATCH 1/3] drm/atomic-helper: Export framebuffer_changed()
In-Reply-To: <55e84a6ee82c0fbd796bb67a630781daa5bdcae0.1452782114.git.john@metanate.com>
References: <20160114142047.GD19130@phenom.ffwll.local>
 <cover.1452782114.git.john@metanate.com>
 <55e84a6ee82c0fbd796bb67a630781daa5bdcae0.1452782114.git.john@metanate.com>
Message-ID: <20160114145635.GF19130@phenom.ffwll.local>

On Thu, Jan 14, 2016 at 02:39:40PM +0000, John Keeping wrote:
> The Rockchip driver cannot use drm_atomic_helper_wait_for_vblanks()
> because it has hardware counters for neither vblanks nor scanlines.
> 
> In order to simplify re-implementing the functionality for this driver,
> export the framebuffer_changed() helper so it can be reused.
> 
> Signed-off-by: John Keeping <john at metanate.com>

Reviewed-by: Daniel Vetter <daniel.vetter at ffwll.ch> And since I don't
expect anyone else to need this anytime soon probably best you just pull
this in through rockchip trees.

Cheers, Daniel

> ---
>  drivers/gpu/drm/drm_atomic_helper.c | 24 ++++++++++++++++++++----
>  include/drm/drm_atomic_helper.h     |  4 ++++
>  2 files changed, 24 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> index 268d37f..7449293 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -948,9 +948,23 @@ static void wait_for_fences(struct drm_device *dev,
>  	}
>  }
>  
> -static bool framebuffer_changed(struct drm_device *dev,
> -				struct drm_atomic_state *old_state,
> -				struct drm_crtc *crtc)
> +/**
> + * drm_atomic_helper_framebuffer_changed - check if framebuffer has changed
> + * @dev: DRM device
> + * @old_state: atomic state object with old state structures
> + * @crtc: DRM crtc
> + *
> + * Checks whether the framebuffer used for this CRTC changes as a result of
> + * the atomic update.  This is useful for drivers which cannot use
> + * drm_atomic_helper_wait_for_vblanks() and need to reimplement its
> + * functionality.
> + *
> + * Returns:
> + * true if the framebuffer changed.
> + */
> +bool drm_atomic_helper_framebuffer_changed(struct drm_device *dev,
> +					   struct drm_atomic_state *old_state,
> +					   struct drm_crtc *crtc)
>  {
>  	struct drm_plane *plane;
>  	struct drm_plane_state *old_plane_state;
> @@ -967,6 +981,7 @@ static bool framebuffer_changed(struct drm_device *dev,
>  
>  	return false;
>  }
> +EXPORT_SYMBOL(drm_atomic_helper_framebuffer_changed);
>  
>  /**
>   * drm_atomic_helper_wait_for_vblanks - wait for vblank on crtcs
> @@ -1001,7 +1016,8 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev,
>  		if (old_state->legacy_cursor_update)
>  			continue;
>  
> -		if (!framebuffer_changed(dev, old_state, crtc))
> +		if (!drm_atomic_helper_framebuffer_changed(dev,
> +				old_state, crtc))
>  			continue;
>  
>  		ret = drm_crtc_vblank_get(crtc);
> diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h
> index a286cce..74fce78 100644
> --- a/include/drm/drm_atomic_helper.h
> +++ b/include/drm/drm_atomic_helper.h
> @@ -42,6 +42,10 @@ int drm_atomic_helper_commit(struct drm_device *dev,
>  			     struct drm_atomic_state *state,
>  			     bool async);
>  
> +bool drm_atomic_helper_framebuffer_changed(struct drm_device *dev,
> +					   struct drm_atomic_state *old_state,
> +					   struct drm_crtc *crtc);
> +
>  void drm_atomic_helper_wait_for_vblanks(struct drm_device *dev,
>  					struct drm_atomic_state *old_state);
>  
> -- 
> 2.7.0.226.gfe986fe
> 

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


From thierry.reding at gmail.com  Thu Jan 14 06:57:05 2016
From: thierry.reding at gmail.com (Thierry Reding)
Date: Thu, 14 Jan 2016 15:57:05 +0100
Subject: [PATCH 3/3] drm/rockchip: explain why we can't wait_for_vblanks
In-Reply-To: <107bbc36a316ed0ddc7b5a8bcd9b6db6cbc71d4f.1452782114.git.john@metanate.com>
References: <20160114142047.GD19130@phenom.ffwll.local>
 <cover.1452782114.git.john@metanate.com>
 <107bbc36a316ed0ddc7b5a8bcd9b6db6cbc71d4f.1452782114.git.john@metanate.com>
Message-ID: <20160114145705.GA24005@ulmo>

On Thu, Jan 14, 2016 at 02:39:42PM +0000, John Keeping wrote:
> Signed-off-by: John Keeping <john at metanate.com>
> ---
>  drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 6 ++++++
>  1 file changed, 6 insertions(+)
> 
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> index 679d23a..b267ce4 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> @@ -177,6 +177,12 @@ static void rockchip_crtc_wait_for_update(struct drm_crtc *crtc)
>  		crtc_funcs->wait_for_update(crtc);
>  }
>  
> +/*
> + * We can't use drm_atomic_helper_wait_for_vblanks() because rk3288 and rk3066
> + * have hardware counters for neither vblanks nor scanlines.  This function is
> + * equivalent but uses rockchip_crtc_wait_for_update() instead of waiting for
> + * vblank_count to change.
> + */

This is kind of misleading. From reading earlier parts of the thread the
reason why drm_atomic_helper_wait_for_vblanks() won't work is because it
has a potential race condition that can't be detected unless you also
have a vblank counter. However, the above comment makes it work like
drm_atomic_helper_wait_for_vblanks() doesn't work in the absence of a
vblank counter, which isn't quite true.

Perhaps also the drm_atomic_helper_wait_for_vblanks() kerneldoc needs to
be updated with these restrictions on its use?

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160114/20fef853/attachment-0001.sig>

From dianders at chromium.org  Thu Jan 14 08:12:50 2016
From: dianders at chromium.org (Doug Anderson)
Date: Thu, 14 Jan 2016 08:12:50 -0800
Subject: [PATCH v3 3/4] i2c: rk3x: new method to caculate i2c clocks
In-Reply-To: <1452774699-57455-4-git-send-email-david.wu@rock-chips.com>
References: <1452774699-57455-1-git-send-email-david.wu@rock-chips.com>
 <1452774699-57455-4-git-send-email-david.wu@rock-chips.com>
Message-ID: <CAD=FV=UXMzEQiKcMa7VjDx+arcbRo1e-0HPHBnG8GP7k6uFt1g@mail.gmail.com>

Hi,

On Thu, Jan 14, 2016 at 4:31 AM, David Wu <david.wu at rock-chips.com> wrote:
> There was an timing issue about "repeated start" time at the I2C
> controller of version0, controller appears to drop SDA at .875x (7/8)
> programmed clk high. The rule(.875x) isn't enough to meet tSU;STA
> requirements on 100k's Standard-mode. To resolve this issue,
> data_upd_st, start_setup_cnt and stop_setup_cnt configs for I2C
> timing information are added, new rules are designed to calculate
> the timing information at new v1.

I'm curious: does new hardware behave differently and that's why you
need v1?  ...or does old and new hardware behave the same and you're
just introducing v1 so you don't mess with how old boards are working?

>From the description it sounds as if the old code had problems as 100k too...

If the new controller is different, I'd probably reword like the
following (you'd have to re-wordwrap):

There was an timing issue about "repeated start" time at the I2C
controller of version0, controller appears to drop SDA at .875x (7/8)
programmed clk high. On version 1 of the controller, the rule(.875x)
isn't enough to meet tSU;STA
requirements on 100k's Standard-mode. To resolve this issue,
data_upd_st, start_setup_cnt and stop_setup_cnt configs for I2C
timing information are added, new rules are designed to calculate
the timing information at new v1.

-Doug


From trini at konsulko.com  Thu Jan 14 08:22:27 2016
From: trini at konsulko.com (Tom Rini)
Date: Thu, 14 Jan 2016 11:22:27 -0500
Subject: [U-Boot] [PATCH v4 5/6] rockchip: kylin: Enable boot with
 android boot image
In-Reply-To: <56970886.2040009@rock-chips.com>
References: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
 <1452675200-15941-6-git-send-email-jeffy.chen@rock-chips.com>
 <20160113152811.GC3359@bill-the-cat>
 <56970886.2040009@rock-chips.com>
Message-ID: <20160114162227.GY3359@bill-the-cat>

On Thu, Jan 14, 2016 at 10:31:34AM +0800, Jeffy Chen wrote:
> Hi Tom,
> 
> On 2016-1-13 23:28, Tom Rini wrote:
> >On Wed, Jan 13, 2016 at 04:53:19PM +0800, Jeffy Chen wrote:
> >
> >>The android kernel is using appended dtb by default, and store
> >>ramdisk right after kernel & dtb.
> >>So we needs to relocate ramdisk, and use atags to pass params.
> >>
> >>Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
> >>Acked-by: Simon Glass <sjg at chromium.org>
> >>---
> >>
> >>Changes in v4: None
> >>Changes in v3: None
> >>Changes in v2: None
> >>
> >>  include/configs/kylin_rk3036.h | 23 +++++++++++++++++++++++
> >>  1 file changed, 23 insertions(+)
> >>
> >>diff --git a/include/configs/kylin_rk3036.h b/include/configs/kylin_rk3036.h
> >>index b750b26..49997ec 100644
> >>--- a/include/configs/kylin_rk3036.h
> >>+++ b/include/configs/kylin_rk3036.h
> >>@@ -35,6 +35,29 @@
> >>  #undef CONFIG_EXTRA_ENV_SETTINGS
> >>  #define CONFIG_EXTRA_ENV_SETTINGS \
> >>  	"partitions=" PARTS_DEFAULT \
> >>+	"mmcdev=0\0" \
> >>+	"mmcpart=5\0" \
> >>+	"loadaddr=" __stringify(CONFIG_SYS_LOAD_ADDR) "\0" \
> >>+
> >>+#define CONFIG_ANDROID_BOOT_IMAGE
> >>+#define CONFIG_SYS_BOOT_RAMDISK_HIGH
> >This should already be set.
> Right, i'll remove it...
> >>+#define CONFIG_SYS_HUSH_PARSER
> >>+
> >>+#undef CONFIG_BOOTCOMMAND
> >>+#define CONFIG_BOOTCOMMAND \
> >>+	"mmc dev ${mmcdev}; if mmc rescan; then " \
> >>+		"part start mmc ${mmcdev} ${mmcpart} boot_start;" \
> >>+		"part size mmc ${mmcdev} ${mmcpart} boot_size;" \
> >>+		"mmc read ${loadaddr} ${boot_start} ${boot_size};" \
> >>+		"bootm start ${loadaddr}; bootm ramdisk;" \
> >>+		"bootm prep; bootm go;" \
> >>+	"fi;" \
> >>+
> >>+/* Enable atags */
> >>+#define CONFIG_SYS_BOOTPARAMS_LEN	(64*1024)
> >>+#define CONFIG_INITRD_TAG
> >>+#define CONFIG_SETUP_MEMORY_TAGS
> >>+#define CONFIG_CMDLINE_TAG
> >But I'm confused as to what exactly is going on here.  Appended dtb is
> >not the same as ATAGS.  And you shouldn't need to split up bootm like
> >that.  Can you please explain a bit more?  Thanks!
> The u-boot will pass atags to kernel, and kernel will merge those
> atags into the appended dtb(fdt).
> 
> The default bootm flow would not pass ramdisk state, but we need it,
> so we should add this state into default flow, or just use split
> bootm cmds :)

That seems very strange.  Is the ramdisk concatenated with the kernel
and dtb as well (and that's why bootm ramdisk somehow finds it but
normal bootm doesn't as you aren't passing in a ramdisk address) ?

-- 
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160114/f5d09f05/attachment.sig>

From john at metanate.com  Thu Jan 14 08:26:19 2016
From: john at metanate.com (John Keeping)
Date: Thu, 14 Jan 2016 16:26:19 +0000
Subject: [PATCH 3/3] drm/rockchip: explain why we can't wait_for_vblanks
In-Reply-To: <20160114145705.GA24005@ulmo>
References: <20160114142047.GD19130@phenom.ffwll.local>
 <cover.1452782114.git.john@metanate.com>
 <107bbc36a316ed0ddc7b5a8bcd9b6db6cbc71d4f.1452782114.git.john@metanate.com>
 <20160114145705.GA24005@ulmo>
Message-ID: <20160114162619.233ab39c.john@metanate.com>

On Thu, 14 Jan 2016 15:57:05 +0100, Thierry Reding wrote:

> On Thu, Jan 14, 2016 at 02:39:42PM +0000, John Keeping wrote:
> > Signed-off-by: John Keeping <john at metanate.com>
> > ---
> >  drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 6 ++++++
> >  1 file changed, 6 insertions(+)
> > 
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > index 679d23a..b267ce4 100644
> > --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > @@ -177,6 +177,12 @@ static void rockchip_crtc_wait_for_update(struct drm_crtc *crtc)
> >  		crtc_funcs->wait_for_update(crtc);
> >  }
> >  
> > +/*
> > + * We can't use drm_atomic_helper_wait_for_vblanks() because rk3288 and rk3066
> > + * have hardware counters for neither vblanks nor scanlines.  This function is
> > + * equivalent but uses rockchip_crtc_wait_for_update() instead of waiting for
> > + * vblank_count to change.
> > + */  
> 
> This is kind of misleading. From reading earlier parts of the thread the
> reason why drm_atomic_helper_wait_for_vblanks() won't work is because it
> has a potential race condition that can't be detected unless you also
> have a vblank counter. However, the above comment makes it work like
> drm_atomic_helper_wait_for_vblanks() doesn't work in the absence of a
> vblank counter, which isn't quite true.

How about something like this (using the sequence from Mark's message):

/*
 * We can't use drm_atomic_helper_wait_for_vblanks() because rk3288 and rk3066
 * have hardware counters for neither vblanks nor scanlines, which results in
 * a race where:
 *				| <-- HW vsync irq and reg take effect
 *	       plane_commit --> |
 *	get_vblank and wait --> |
 *				| <-- handle_vblank, vblank->count + 1
 *		 cleanup_fb --> |
 *		iommu crash --> |
 *				| <-- HW vsync irq and reg take effect
 *
 * This function is equivalent but uses rockchip_crtc_wait_for_update() instead
 * of waiting for vblank_count to change.
 */


From trini at konsulko.com  Thu Jan 14 16:59:50 2016
From: trini at konsulko.com (Tom Rini)
Date: Thu, 14 Jan 2016 19:59:50 -0500
Subject: [U-Boot] [PATCH v4 5/6] rockchip: kylin: Enable boot with
 android boot image
In-Reply-To: <569842F2.4080808@rock-chips.com>
References: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
 <1452675200-15941-6-git-send-email-jeffy.chen@rock-chips.com>
 <20160113152811.GC3359@bill-the-cat>
 <56970886.2040009@rock-chips.com>
 <20160114162227.GY3359@bill-the-cat>
 <569842F2.4080808@rock-chips.com>
Message-ID: <20160115005950.GC3359@bill-the-cat>

On Fri, Jan 15, 2016 at 08:53:06AM +0800, Jeffy Chen wrote:
> Hi Tom,
> 
> On 2016-1-15 0:22, Tom Rini wrote:
> >On Thu, Jan 14, 2016 at 10:31:34AM +0800, Jeffy Chen wrote:
> >>Hi Tom,
> >>
> >>On 2016-1-13 23:28, Tom Rini wrote:
> >>>On Wed, Jan 13, 2016 at 04:53:19PM +0800, Jeffy Chen wrote:
> >>>
> >>>>The android kernel is using appended dtb by default, and store
> >>>>ramdisk right after kernel & dtb.
> >>>>So we needs to relocate ramdisk, and use atags to pass params.
> >>>>
> >>>>Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
> >>>>Acked-by: Simon Glass <sjg at chromium.org>
> >>>>---
> >>>>
> >>>>Changes in v4: None
> >>>>Changes in v3: None
> >>>>Changes in v2: None
> >>>>
> >>>>  include/configs/kylin_rk3036.h | 23 +++++++++++++++++++++++
> >>>>  1 file changed, 23 insertions(+)
> >>>>
> >>>>diff --git a/include/configs/kylin_rk3036.h b/include/configs/kylin_rk3036.h
> >>>>index b750b26..49997ec 100644
> >>>>--- a/include/configs/kylin_rk3036.h
> >>>>+++ b/include/configs/kylin_rk3036.h
> >>>>@@ -35,6 +35,29 @@
> >>>>  #undef CONFIG_EXTRA_ENV_SETTINGS
> >>>>  #define CONFIG_EXTRA_ENV_SETTINGS \
> >>>>  	"partitions=" PARTS_DEFAULT \
> >>>>+	"mmcdev=0\0" \
> >>>>+	"mmcpart=5\0" \
> >>>>+	"loadaddr=" __stringify(CONFIG_SYS_LOAD_ADDR) "\0" \
> >>>>+
> >>>>+#define CONFIG_ANDROID_BOOT_IMAGE
> >>>>+#define CONFIG_SYS_BOOT_RAMDISK_HIGH
> >>>This should already be set.
> >>Right, i'll remove it...
> >>>>+#define CONFIG_SYS_HUSH_PARSER
> >>>>+
> >>>>+#undef CONFIG_BOOTCOMMAND
> >>>>+#define CONFIG_BOOTCOMMAND \
> >>>>+	"mmc dev ${mmcdev}; if mmc rescan; then " \
> >>>>+		"part start mmc ${mmcdev} ${mmcpart} boot_start;" \
> >>>>+		"part size mmc ${mmcdev} ${mmcpart} boot_size;" \
> >>>>+		"mmc read ${loadaddr} ${boot_start} ${boot_size};" \
> >>>>+		"bootm start ${loadaddr}; bootm ramdisk;" \
> >>>>+		"bootm prep; bootm go;" \
> >>>>+	"fi;" \
> >>>>+
> >>>>+/* Enable atags */
> >>>>+#define CONFIG_SYS_BOOTPARAMS_LEN	(64*1024)
> >>>>+#define CONFIG_INITRD_TAG
> >>>>+#define CONFIG_SETUP_MEMORY_TAGS
> >>>>+#define CONFIG_CMDLINE_TAG
> >>>But I'm confused as to what exactly is going on here.  Appended dtb is
> >>>not the same as ATAGS.  And you shouldn't need to split up bootm like
> >>>that.  Can you please explain a bit more?  Thanks!
> >>The u-boot will pass atags to kernel, and kernel will merge those
> >>atags into the appended dtb(fdt).
> >>
> >>The default bootm flow would not pass ramdisk state, but we need it,
> >>so we should add this state into default flow, or just use split
> >>bootm cmds :)
> >That seems very strange.  Is the ramdisk concatenated with the kernel
> >and dtb as well (and that's why bootm ramdisk somehow finds it but
> >normal bootm doesn't as you aren't passing in a ramdisk address) ?
> Yes, the ramdisk concatenated with the kernel and dtb as
> well(u-boot/include/android_image.h: struct andr_img_hdr).
> 
> And the normal bootm cmd would find it by parsing andr_img_hdr struct.
> But we still need bootm ramdisk state, because it will call
> boot_ramdisk_high to relocate ramdisk area :)
> 
> I found if not relocate it to somewhere else, it would be corrupted
> after kernel's decompressing(during update fdt area).

So 'bootm $loadaddr' of an Android image sees, but does not relocate the
ramdisk that is included in the image, but bootm ramdisk does?  That
sounds like a bug in the regular bootm handling.

-- 
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160114/f48c07b8/attachment.sig>

From zyw at rock-chips.com  Thu Jan 14 17:04:12 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Fri, 15 Jan 2016 09:04:12 +0800
Subject: [PATCH] drm/rockchip/dsi: fix handling
 mipi_dsi_pixel_format_to_bpp result
In-Reply-To: <1452761942-23864-1-git-send-email-a.hajda@samsung.com>
References: <1452761942-23864-1-git-send-email-a.hajda@samsung.com>
Message-ID: <5698458C.3070901@rock-chips.com>

Hi Andrzej

On 01/14/2016 04:59 PM, Andrzej Hajda wrote:
> The function can return negative value so it should be assigned to signed
> variable.
>
> The problem has been detected using patch
> scripts/coccinelle/tests/unsigned_lesser_than_zero.cocci.
>
> Signed-off-by: Andrzej Hajda <a.hajda at samsung.com>
> ---
>   drivers/gpu/drm/rockchip/dw-mipi-dsi.c | 3 ++-
>   1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
> index 7bfe243..f8f8f29 100644
> --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
> +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
> @@ -461,10 +461,11 @@ static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi)
>   
>   static int dw_mipi_dsi_get_lane_bps(struct dw_mipi_dsi *dsi)
>   {
> -	unsigned int bpp, i, pre;
> +	unsigned int i, pre;
>   	unsigned long mpclk, pllref, tmp;
>   	unsigned int m = 1, n = 1, target_mbps = 1000;
>   	unsigned int max_mbps = dptdin_map[ARRAY_SIZE(dptdin_map) - 1].max_mbps;
> +	int bpp;
>   
>   	bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
>   	if (bpp < 0) {
Reviewed-by: Chris Zhong <zyw at rock-chips.com>

Thanks



From jeffy.chen at rock-chips.com  Thu Jan 14 16:53:06 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Fri, 15 Jan 2016 08:53:06 +0800
Subject: [U-Boot] [PATCH v4 5/6] rockchip: kylin: Enable boot with android
 boot image
In-Reply-To: <20160114162227.GY3359@bill-the-cat>
References: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
 <1452675200-15941-6-git-send-email-jeffy.chen@rock-chips.com>
 <20160113152811.GC3359@bill-the-cat> <56970886.2040009@rock-chips.com>
 <20160114162227.GY3359@bill-the-cat>
Message-ID: <569842F2.4080808@rock-chips.com>

Hi Tom,

On 2016-1-15 0:22, Tom Rini wrote:
> On Thu, Jan 14, 2016 at 10:31:34AM +0800, Jeffy Chen wrote:
>> Hi Tom,
>>
>> On 2016-1-13 23:28, Tom Rini wrote:
>>> On Wed, Jan 13, 2016 at 04:53:19PM +0800, Jeffy Chen wrote:
>>>
>>>> The android kernel is using appended dtb by default, and store
>>>> ramdisk right after kernel & dtb.
>>>> So we needs to relocate ramdisk, and use atags to pass params.
>>>>
>>>> Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
>>>> Acked-by: Simon Glass <sjg at chromium.org>
>>>> ---
>>>>
>>>> Changes in v4: None
>>>> Changes in v3: None
>>>> Changes in v2: None
>>>>
>>>>   include/configs/kylin_rk3036.h | 23 +++++++++++++++++++++++
>>>>   1 file changed, 23 insertions(+)
>>>>
>>>> diff --git a/include/configs/kylin_rk3036.h b/include/configs/kylin_rk3036.h
>>>> index b750b26..49997ec 100644
>>>> --- a/include/configs/kylin_rk3036.h
>>>> +++ b/include/configs/kylin_rk3036.h
>>>> @@ -35,6 +35,29 @@
>>>>   #undef CONFIG_EXTRA_ENV_SETTINGS
>>>>   #define CONFIG_EXTRA_ENV_SETTINGS \
>>>>   	"partitions=" PARTS_DEFAULT \
>>>> +	"mmcdev=0\0" \
>>>> +	"mmcpart=5\0" \
>>>> +	"loadaddr=" __stringify(CONFIG_SYS_LOAD_ADDR) "\0" \
>>>> +
>>>> +#define CONFIG_ANDROID_BOOT_IMAGE
>>>> +#define CONFIG_SYS_BOOT_RAMDISK_HIGH
>>> This should already be set.
>> Right, i'll remove it...
>>>> +#define CONFIG_SYS_HUSH_PARSER
>>>> +
>>>> +#undef CONFIG_BOOTCOMMAND
>>>> +#define CONFIG_BOOTCOMMAND \
>>>> +	"mmc dev ${mmcdev}; if mmc rescan; then " \
>>>> +		"part start mmc ${mmcdev} ${mmcpart} boot_start;" \
>>>> +		"part size mmc ${mmcdev} ${mmcpart} boot_size;" \
>>>> +		"mmc read ${loadaddr} ${boot_start} ${boot_size};" \
>>>> +		"bootm start ${loadaddr}; bootm ramdisk;" \
>>>> +		"bootm prep; bootm go;" \
>>>> +	"fi;" \
>>>> +
>>>> +/* Enable atags */
>>>> +#define CONFIG_SYS_BOOTPARAMS_LEN	(64*1024)
>>>> +#define CONFIG_INITRD_TAG
>>>> +#define CONFIG_SETUP_MEMORY_TAGS
>>>> +#define CONFIG_CMDLINE_TAG
>>> But I'm confused as to what exactly is going on here.  Appended dtb is
>>> not the same as ATAGS.  And you shouldn't need to split up bootm like
>>> that.  Can you please explain a bit more?  Thanks!
>> The u-boot will pass atags to kernel, and kernel will merge those
>> atags into the appended dtb(fdt).
>>
>> The default bootm flow would not pass ramdisk state, but we need it,
>> so we should add this state into default flow, or just use split
>> bootm cmds :)
> That seems very strange.  Is the ramdisk concatenated with the kernel
> and dtb as well (and that's why bootm ramdisk somehow finds it but
> normal bootm doesn't as you aren't passing in a ramdisk address) ?
Yes, the ramdisk concatenated with the kernel and dtb as 
well(u-boot/include/android_image.h: struct andr_img_hdr).

And the normal bootm cmd would find it by parsing andr_img_hdr struct.
But we still need bootm ramdisk state, because it will call 
boot_ramdisk_high to relocate ramdisk area :)

I found if not relocate it to somewhere else, it would be corrupted 
after kernel's decompressing(during update fdt area).



From andy.yan at rock-chips.com  Thu Jan 14 17:11:16 2016
From: andy.yan at rock-chips.com (Andy Yan)
Date: Fri, 15 Jan 2016 09:11:16 +0800
Subject: [PATCH 1/2] ARM: dts: rockchip: increase vdd_arm voltage for
 rk3066a-rayeager board
In-Reply-To: <CANCyOWak0wMNRPxkJtE2DJ1UvCneWxuNXjwjxjM3+_Jjuaahvw@mail.gmail.com>
References: <1452774780-16493-1-git-send-email-andy.yan@rock-chips.com>
 <1452774860-16572-1-git-send-email-andy.yan@rock-chips.com>
 <1709463.R522kSjgXB@phil> <5697A081.4060903@rock-chips.com>
 <CANCyOWak0wMNRPxkJtE2DJ1UvCneWxuNXjwjxjM3+_Jjuaahvw@mail.gmail.com>
Message-ID: <56984734.6000806@rock-chips.com>

Hi Heiko:

On 2016?01?14? 21:50, Eddie Cai wrote:
> Hi Heiko
>
> I don't want open source version kernel support too high frequency.
> Many people use RK3066/PX2 on their board. We don't know if their
> layout is good enough to support higher frequency. ie. bad layout will
> cause more voltage drop on the VDD_CPU power rail. So it may need
> higher voltage. I think stable is more important here. People want
> higher frequency can add more opp depend on their board's layout.
>
> 2016-01-14 21:20 GMT+08:00 Andy Yan <andy.yan at rock-chips.com>:
>> Hi Heiko:
>>
>>
>> On 2016?01?14? 20:45, Heiko Stuebner wrote:
>>> Hi Andy,
>>>
>>> Am Donnerstag, 14. Januar 2016, 20:34:20 schrieb Andy Yan:
>>>> The current vdd_arm voltage is too low, increase it will make
>>>> the system more stable.
>>>>
>>>> Signed-off-by: Andy Yan <andy.yan at rock-chips.com>
>>>> ---
>>>>
>>>>    arch/arm/boot/dts/rk3066a-rayeager.dts | 8 ++++++++
>>>>    1 file changed, 8 insertions(+)
>>>>
>>>> diff --git a/arch/arm/boot/dts/rk3066a-rayeager.dts
>>>> b/arch/arm/boot/dts/rk3066a-rayeager.dts index 0bf6876..3d4c104 100644
>>>> --- a/arch/arm/boot/dts/rk3066a-rayeager.dts
>>>> +++ b/arch/arm/boot/dts/rk3066a-rayeager.dts
>>>> @@ -159,6 +159,14 @@
>>>>
>>>>    &cpu0 {
>>>>          cpu0-supply = <&vdd_arm>;
>>>> +       operating-points = <
>>>> +               /* kHz    uV */
>>>> +               1008000 1125000
>>>> +               816000  1125000
>>>> +               600000  1100000
>>>> +               504000  1100000
>>>> +               312000  1075000
>>>> +       >;
>>>>    };
>>>>
>>>>    &emac {
>>> Does this only apply to the rayeager-board / px2 soc or for all
>>> rk3066a/px2
>>> boards? I.e. the original values come from some sdk kernel and I might've
>>> transcribed that wrong at the time and I remember having problems trying
>>> to
>>> reach higher frequencies as well.
>>>
>>> So could you check what the full operating points looks like for both and
>>> if
>>> this can move to the rk3066a.dtsi, as both should be able to reach at
>>> least
>>> 1.4GHz / 1.6GHz.
>>>
>>>
>>> Thanks
>>> Heiko
>>>
>>> ______
>>
>>      I have checked the sdk kernel, the opp for the sdk board is like bellow:
>>
>>   /**
>>   * dvfs_cpu_logic_table: table for arm and logic dvfs
>>   * @frequency   : arm frequency
>>   * @cpu_volt    : arm voltage depend on frequency
>>   * @logic_volt  : logic voltage arm requests depend on frequency
>>   * comments     : min arm/logic voltage
>>   */
>> static struct dvfs_arm_table dvfs_cpu_logic_table[] = {
>>          {.frequency = 252 * 1000,       .cpu_volt = 1075 * 1000,
>> .logic_volt = 1125 * 1000},//0.975V/1.000V
>>          {.frequency = 504 * 1000,       .cpu_volt = 1100 * 1000,
>> .logic_volt = 1125 * 1000},//0.975V/1.000V
>>          {.frequency = 816 * 1000,       .cpu_volt = 1125 * 1000,
>> .logic_volt = 1150 * 1000},//1.000V/1.025V
>>          {.frequency = 1008 * 1000,      .cpu_volt = 1125 * 1000,
>> .logic_volt = 1150 * 1000},//1.025V/1.050V
>>          {.frequency = 1200 * 1000,      .cpu_volt = 1175 * 1000,
>> .logic_volt = 1200 * 1000},//1.100V/1.050V
>>          {.frequency = 1272 * 1000,      .cpu_volt = 1225 * 1000,
>> .logic_volt = 1200 * 1000},//1.150V/1.100V
>>          {.frequency = 1416 * 1000,      .cpu_volt = 1300 * 1000,
>> .logic_volt = 1200 * 1000},//1.225V/1.100V
>>          {.frequency = 1512 * 1000,      .cpu_volt = 1350 * 1000,
>> .logic_volt = 1250 * 1000},//1.300V/1.150V
>>          {.frequency = 1608 * 1000,      .cpu_volt = 1425 * 1000,
>> .logic_volt = 1300 * 1000},//1.325V/1.175V
>>          {.frequency = CPUFREQ_TABLE_END},
>> };
>>
>>   This table is made base on mass stress test. So is a safe opp table for
>> most rk3066a base boards. But it some
>>   board is not so well in power supply circuit design, the may also need to
>> adjust it.
>>
>>   From the table we see that the logic_voltage need to increase  as the
>> cpufreq increase. But it seems that the current mainline kernel does not
>> support adjust the logic voltage when cpufreq change. So we can only scale
>> the cpufreq to 1.4GHZ by fixed the logic_volt at 1.2V.
>>
>>   If you like, I can move it to rk3066a.dtsi with the max freq at 1008MHZ, as
>> I haven't  test to 1.4GHZ  with the mainline kernel. Or give me some time to
>> test to see if the rayeager board can run to 1.4G stable with the mainline .
>>
>>

   I have run Rayeager  board with cpufreq freely change from 
312MHZ~1416MHZ for 11 hours now, it's stable. Maybe we can set the 
default opp table for rk3066a/PX2 from 312MHZ~1416HZ in current mainline.




From jeffy.chen at rock-chips.com  Thu Jan 14 18:20:43 2016
From: jeffy.chen at rock-chips.com (Jeffy Chen)
Date: Fri, 15 Jan 2016 10:20:43 +0800
Subject: [U-Boot] [PATCH v4 5/6] rockchip: kylin: Enable boot with android
 boot image
In-Reply-To: <20160115005950.GC3359@bill-the-cat>
References: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
 <1452675200-15941-6-git-send-email-jeffy.chen@rock-chips.com>
 <20160113152811.GC3359@bill-the-cat> <56970886.2040009@rock-chips.com>
 <20160114162227.GY3359@bill-the-cat> <569842F2.4080808@rock-chips.com>
 <20160115005950.GC3359@bill-the-cat>
Message-ID: <5698577B.8040209@rock-chips.com>

Hi Tom,

On 2016-1-15 8:59, Tom Rini wrote:
> On Fri, Jan 15, 2016 at 08:53:06AM +0800, Jeffy Chen wrote:
>> Hi Tom,
>>
>> On 2016-1-15 0:22, Tom Rini wrote:
>>> On Thu, Jan 14, 2016 at 10:31:34AM +0800, Jeffy Chen wrote:
>>>> Hi Tom,
>>>>
>>>> On 2016-1-13 23:28, Tom Rini wrote:
>>>>> On Wed, Jan 13, 2016 at 04:53:19PM +0800, Jeffy Chen wrote:
>>>>>
>>>>>> The android kernel is using appended dtb by default, and store
>>>>>> ramdisk right after kernel & dtb.
>>>>>> So we needs to relocate ramdisk, and use atags to pass params.
>>>>>>
>>>>>> Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
>>>>>> Acked-by: Simon Glass <sjg at chromium.org>
>>>>>> ---
>>>>>>
>>>>>> Changes in v4: None
>>>>>> Changes in v3: None
>>>>>> Changes in v2: None
>>>>>>
>>>>>>   include/configs/kylin_rk3036.h | 23 +++++++++++++++++++++++
>>>>>>   1 file changed, 23 insertions(+)
>>>>>>
>>>>>> diff --git a/include/configs/kylin_rk3036.h b/include/configs/kylin_rk3036.h
>>>>>> index b750b26..49997ec 100644
>>>>>> --- a/include/configs/kylin_rk3036.h
>>>>>> +++ b/include/configs/kylin_rk3036.h
>>>>>> @@ -35,6 +35,29 @@
>>>>>>   #undef CONFIG_EXTRA_ENV_SETTINGS
>>>>>>   #define CONFIG_EXTRA_ENV_SETTINGS \
>>>>>>   	"partitions=" PARTS_DEFAULT \
>>>>>> +	"mmcdev=0\0" \
>>>>>> +	"mmcpart=5\0" \
>>>>>> +	"loadaddr=" __stringify(CONFIG_SYS_LOAD_ADDR) "\0" \
>>>>>> +
>>>>>> +#define CONFIG_ANDROID_BOOT_IMAGE
>>>>>> +#define CONFIG_SYS_BOOT_RAMDISK_HIGH
>>>>> This should already be set.
>>>> Right, i'll remove it...
>>>>>> +#define CONFIG_SYS_HUSH_PARSER
>>>>>> +
>>>>>> +#undef CONFIG_BOOTCOMMAND
>>>>>> +#define CONFIG_BOOTCOMMAND \
>>>>>> +	"mmc dev ${mmcdev}; if mmc rescan; then " \
>>>>>> +		"part start mmc ${mmcdev} ${mmcpart} boot_start;" \
>>>>>> +		"part size mmc ${mmcdev} ${mmcpart} boot_size;" \
>>>>>> +		"mmc read ${loadaddr} ${boot_start} ${boot_size};" \
>>>>>> +		"bootm start ${loadaddr}; bootm ramdisk;" \
>>>>>> +		"bootm prep; bootm go;" \
>>>>>> +	"fi;" \
>>>>>> +
>>>>>> +/* Enable atags */
>>>>>> +#define CONFIG_SYS_BOOTPARAMS_LEN	(64*1024)
>>>>>> +#define CONFIG_INITRD_TAG
>>>>>> +#define CONFIG_SETUP_MEMORY_TAGS
>>>>>> +#define CONFIG_CMDLINE_TAG
>>>>> But I'm confused as to what exactly is going on here.  Appended dtb is
>>>>> not the same as ATAGS.  And you shouldn't need to split up bootm like
>>>>> that.  Can you please explain a bit more?  Thanks!
>>>> The u-boot will pass atags to kernel, and kernel will merge those
>>>> atags into the appended dtb(fdt).
>>>>
>>>> The default bootm flow would not pass ramdisk state, but we need it,
>>>> so we should add this state into default flow, or just use split
>>>> bootm cmds :)
>>> That seems very strange.  Is the ramdisk concatenated with the kernel
>>> and dtb as well (and that's why bootm ramdisk somehow finds it but
>>> normal bootm doesn't as you aren't passing in a ramdisk address) ?
>> Yes, the ramdisk concatenated with the kernel and dtb as
>> well(u-boot/include/android_image.h: struct andr_img_hdr).
>>
>> And the normal bootm cmd would find it by parsing andr_img_hdr struct.
>> But we still need bootm ramdisk state, because it will call
>> boot_ramdisk_high to relocate ramdisk area :)
>>
>> I found if not relocate it to somewhere else, it would be corrupted
>> after kernel's decompressing(during update fdt area).
> So 'bootm $loadaddr' of an Android image sees, but does not relocate the
> ramdisk that is included in the image, but bootm ramdisk does?  That
> sounds like a bug in the regular bootm handling.
Yep, the default bootm flow would not contain ramdisk relocate state:

vi common/cmd_bootm.c
         return do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START |
                 BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER |
                 BOOTM_STATE_LOADOS |
#if defined(CONFIG_PPC) || defined(CONFIG_MIPS)
                 BOOTM_STATE_OS_CMDLINE |
#endif
                 BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |
                 BOOTM_STATE_OS_GO, &images, 1);

But i'm not sure if it's ok to add it here...



From wdc at rock-chips.com  Fri Jan 15 00:34:44 2016
From: wdc at rock-chips.com (David.Wu)
Date: Fri, 15 Jan 2016 16:34:44 +0800
Subject: [PATCH v3 2/4] i2c: rk3x: add ops to caculate i2c clocks
In-Reply-To: <CAHp75VdR01_suo3nTWuuT2OEusy6z6KGSg29LKD8T3kY9eGMPA@mail.gmail.com>
References: <1452774699-57455-1-git-send-email-david.wu@rock-chips.com>
 <1452774699-57455-3-git-send-email-david.wu@rock-chips.com>
 <CAHp75VdR01_suo3nTWuuT2OEusy6z6KGSg29LKD8T3kY9eGMPA@mail.gmail.com>
Message-ID: <5698AF24.2030202@rock-chips.com>


Hi Andy,

? 2016/1/14 21:19, Andy Shevchenko ??:
> On Thu, Jan 14, 2016 at 2:31 PM, David Wu <david.wu at rock-chips.com> wrote:
>> From: David Wu <wdc at rock-chips.com>
>>
>> I2c Controller of rk3x is updated for the rules to caculate clocks.
>> So it need a new method to caculate i2c clock timing information
>> for new version. The current method is defined as v0, and new is
>> v1, next maybe v2......
> Thanks for an update. My comments below.
>
>> --- a/drivers/i2c/busses/i2c-rk3x.c
>> +++ b/drivers/i2c/busses/i2c-rk3x.c
>> @@ -90,10 +95,27 @@ struct rk3x_i2c_soc_data {
>>          int grf_offset;
>>   };
>>
>> +/**
>> + * struct rk3x_priv_i2c_timings - rk3x I2C timing information
>> + * @div_low: Divider output for low
>> + * @div_high: Divider output for high
>> + */
>> +struct rk3x_priv_i2c_timings {
>> +       unsigned long div_low;
>> +       unsigned long div_high;
>> +};
>> +
>> +struct rk3x_i2c_ops {
>> +       int (*calc_clock)(unsigned long,
>> +                         struct i2c_timings *,
>> +                         struct rk3x_priv_i2c_timings *);
>> +};
>> +
>>   struct rk3x_i2c {
>>          struct i2c_adapter adap;
>>          struct device *dev;
>>          struct rk3x_i2c_soc_data *soc_data;
>> +       struct rk3x_i2c_ops ops;
> Not much sense for now to have a struct for one member.

Yes, it looks ok not to do change here.

>>          /* Hardware resources */
>>          void __iomem *regs;
>> @@ -102,6 +124,7 @@ struct rk3x_i2c {
>>
> What about
>        /* I2C timing settings */
>        struct i2c_timings t;
>
>        /* Divider settings */
>        int (*calc_divs)(unsigned long,
>                          struct i2c_timings *,
>                          unsigned long *div_low,
>                          unsigned long *div_high);
>        unsigned long div_low;
>        unsigned long div_high;
> ?
>
> As far as I understand you still calculate divider values.
>
>>          /* Synchronization & notification */
>>          spinlock_t lock;
>> @@ -431,21 +454,20 @@ out:
>>   }
>>
>>   /**
>> - * Calculate divider values for desired SCL frequency
>> + * Calculate timing clock info values for desired SCL frequency
>>    *
>>    * @clk_rate: I2C input clock rate
>> - * @t_input: Known I2C timing information.
>> - * @div_low: Divider output for low
>> - * @div_high: Divider output for high
> For now seems better to leave this prototype.
>
>> + * @t_input: Known I2C timing information
>> + * @t_output: Caculated rk3x private timing information that would
>> + * be written into regs
>>    *
>>    * Returns: 0 on success, -EINVAL if the goal SCL rate is too slow. In that case
>>    * a best-effort divider value is returned in divs. If the target rate is
>>    * too high, we silently use the highest possible rate.
>>    */
>> -static int rk3x_i2c_calc_divs(unsigned long clk_rate,
>> -                             struct i2c_timings *t_input,
>> -                             unsigned long *div_low,
>> -                             unsigned long *div_high)
>> +static int rk3x_i2c_v0_calc_clock(unsigned long clk_rate,
>> +                                 struct i2c_timings *t_input,
>> +                                 struct rk3x_priv_i2c_timings *t_output)
>>   {
>>          unsigned long spec_min_low_ns, spec_min_high_ns;
>>          unsigned long spec_setup_start, spec_max_data_hold_ns;
>
>> @@ -583,25 +605,25 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate,
>>
>>                  /* Give low the "ideal" and give high whatever extra is left */
>>                  extra_low_div = ideal_low_div - min_low_div;
>> -               *div_low = ideal_low_div;
>> -               *div_high = min_high_div + (extra_div - extra_low_div);
>> +               t_output->div_low = ideal_low_div;
>> +               t_output->div_high = min_high_div + (extra_div - extra_low_div);
>>          }
>>
>>          /*
>>           * Adjust to the fact that the hardware has an implicit "+1".
>>           * NOTE: Above calculations always produce div_low > 0 and div_high > 0.
>>           */
>> -       *div_low = *div_low - 1;
>> -       *div_high = *div_high - 1;
>> +       t_output->div_low = t_output->div_low - 1;
> -= 1
>
>> +       t_output->div_high = t_output->div_high - 1;
> Ditto.
>
> But without change of prototype those no needed.
>
>>          /* Maximum divider supported by hw is 0xffff */
>> -       if (*div_low > 0xffff) {
>> -               *div_low = 0xffff;
>> +       if (t_output->div_low > 0xffff) {
>> +               t_output->div_low = 0xffff;
>>                  ret = -EINVAL;
>>          }
>>
>> -       if (*div_high > 0xffff) {
>> -               *div_high = 0xffff;
>> +       if (t_output->div_high > 0xffff) {
>> +               t_output->div_high = 0xffff;
>>                  ret = -EINVAL;
>>          }
>>
>> @@ -610,19 +632,21 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate,
>>
>>   static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate)
>>   {
>> -       unsigned long div_low, div_high;
>>          u64 t_low_ns, t_high_ns;
>>          int ret;
>>
>> -       ret = rk3x_i2c_calc_divs(clk_rate, &i2c->t, &div_low, &div_high);
>> +       ret = i2c->ops.calc_clock(clk_rate, &i2c->t, &i2c->t_priv);
>>          WARN_ONCE(ret != 0, "Could not reach SCL freq %u", i2c->t.bus_freq_hz);
>>
>>          clk_enable(i2c->clk);
>> -       i2c_writel(i2c, (div_high << 16) | (div_low & 0xffff), REG_CLKDIV);
>> +       i2c_writel(i2c, (i2c->t_priv.div_high << 16) |
>> +                 (i2c->t_priv.div_low & 0xffff), REG_CLKDIV);
>>          clk_disable(i2c->clk);
>>
>> -       t_low_ns = div_u64(((u64)div_low + 1) * 8 * 1000000000, clk_rate);
>> -       t_high_ns = div_u64(((u64)div_high + 1) * 8 * 1000000000, clk_rate);
>> +       t_low_ns = div_u64(((u64)i2c->t_priv.div_low + 1) * 8 * 1000000000,
>> +                          clk_rate);
>> +       t_high_ns = div_u64(((u64)i2c->t_priv.div_high + 1) * 8 * 1000000000,
>> +                           clk_rate);
>>          dev_dbg(i2c->dev,
>>                  "CLK %lukhz, Req %uns, Act low %lluns high %lluns\n",
>>                  clk_rate / 1000,
>> @@ -652,12 +676,11 @@ static int rk3x_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long
>>   {
>>          struct clk_notifier_data *ndata = data;
>>          struct rk3x_i2c *i2c = container_of(nb, struct rk3x_i2c, clk_rate_nb);
>> -       unsigned long div_low, div_high;
>>
>>          switch (event) {
>>          case PRE_RATE_CHANGE:
>> -               if (rk3x_i2c_calc_divs(ndata->new_rate, &i2c->t,
>> -                                      &div_low, &div_high) != 0)
>> +               if (i2c->ops.calc_clock(ndata->new_rate, &i2c->t,
>> +                                       &i2c->t_priv) != 0)
>>                          return NOTIFY_STOP;
>>
>>                  /* scale up */
>> @@ -861,6 +884,7 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
>>          u32 value;
>>          int irq;
>>          unsigned long clk_rate;
>> +       unsigned int version;
> No need to have a separate variable?
>
>> +       version = (readl(i2c->regs + REG_CON) & VERSION_MASK) >> VERSION_SHIFT;
>> +       if (version == RK3X_I2C_V0)
>> +               i2c->ops.calc_clock = rk3x_i2c_v0_calc_clock;
> value = readl(i2c->regs + REG_CON);
> if ((value & VERSION_MASK) >> VERSION_SHIFT) == RK3X_I2C_V0)
>    i2c->ops.calc_clock = rk3x_i2c_v0_calc_clock;
>




From caesar.upstream at gmail.com  Fri Jan 15 01:15:21 2016
From: caesar.upstream at gmail.com (Caesar Wang)
Date: Fri, 15 Jan 2016 17:15:21 +0800
Subject: [PATCH v2 06/12] mmc: pwrseq: add support for power-on sequencing
 through DT
In-Reply-To: <5693D226.2000307@osg.samsung.com>
References: <1452155155-16232-1-git-send-email-wxt@rock-chips.com>
 <1452155155-16232-7-git-send-email-wxt@rock-chips.com>
 <568FAA07.1010509@osg.samsung.com> <6960259.dpUH8KWsNS@phil>
 <5693D226.2000307@osg.samsung.com>
Message-ID: <5698B8A9.9040002@gmail.com>

Hi Javier,

? 2016?01?12? 00:02, Javier Martinez Canillas ??:
> Hello Heiko,
>
> On 01/08/2016 11:42 PM, Heiko Stuebner wrote:
>> Am Freitag, 8. Januar 2016, 09:22:31 schrieb Javier Martinez Canillas:
>>>> For example:
>>>> we need enable wifi module power to via the WL_REG_ON
>>>> pin, we need enable it as the regulator if this pin is connected to
>>>> the gpio of cpu.
>>> This part confuses me, so does your chip have an actual regulator that
>>> needs to be enabled or is just a fake regulator whose gpio property is
>>> used not to enable the regulator but to toggle the WL_REG_ON pin of
>>> the WiFi chip?
>> another option would be to use the reset-gpio-handles. rk3288-veyron and I
>> think some Exynos as well use it that way.
>>
> Yes I know, my point was that the reset-gpios property should be used
> instead of a fake regulator if what's needed is to toggle a chip pin.
>
>>>> Maybe, someone will say that can pull up/down from dts.
>>>> Unfortunately some SoCs can't support pinctrl pull up/down in
>>>> internal.
>>> Can you please elaborate on this? AFAIU this limitation is the reason
>>> why you went with the regulator approach so I think it deserve a more
>>> deep explanation.
>> On the rk3036 each pin has an individual unchangable pull direction. So it's
>> either no bias or pulling in the predefined direction (the pin_default bias
>> option).
>>
> I think each change has to be justified on its own so I would say that
> having a regulator enabled as a part of a SDIO chip's power sequencing
> is something needed for many platforms, and that this provider should
> be extended to support that (something like commit msg in patch 05/12).
>
> And then in the kylin DTS change (patch 08/12), I would explain why a
> chained regulators approach is used/needed instead of the reset-gpios
> due any platform limitations.

Okay,
I 'm agreed with your points in here.

The reset-gpios/pwrsq can meet the demand of some wlan chips trigger 
condition.
No matter whatever is the BT_EN or WL_EN  triggers pin.

>> Heiko
>>
> Best regards,


-- 
Thanks,
Caesar



From heiko at sntech.de  Fri Jan 15 01:22:21 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Fri, 15 Jan 2016 10:22:21 +0100
Subject: [PATCH 1/2] ARM: dts: rockchip: increase vdd_arm voltage for
 rk3066a-rayeager board
In-Reply-To: <56984734.6000806@rock-chips.com>
References: <1452774780-16493-1-git-send-email-andy.yan@rock-chips.com>
 <CANCyOWak0wMNRPxkJtE2DJ1UvCneWxuNXjwjxjM3+_Jjuaahvw@mail.gmail.com>
 <56984734.6000806@rock-chips.com>
Message-ID: <24626895.xt4u2Kuv7p@phil>

Hi Andy, Eddie,

Am Freitag, 15. Januar 2016, 09:11:16 schrieb Andy Yan:
> On 2016?01?14? 21:50, Eddie Cai wrote:
> > Hi Heiko
> > 
> > I don't want open source version kernel support too high frequency.
> > Many people use RK3066/PX2 on their board. We don't know if their
> > layout is good enough to support higher frequency. ie. bad layout will
> > cause more voltage drop on the VDD_CPU power rail. So it may need
> > higher voltage. I think stable is more important here. People want
> > higher frequency can add more opp depend on their board's layout.
> > 
> > 2016-01-14 21:20 GMT+08:00 Andy Yan <andy.yan at rock-chips.com>:
> >> Hi Heiko:
> >> 
> >> On 2016?01?14? 20:45, Heiko Stuebner wrote:
> >>> Hi Andy,
> >>> 
> >>> Am Donnerstag, 14. Januar 2016, 20:34:20 schrieb Andy Yan:
> >>>> The current vdd_arm voltage is too low, increase it will make
> >>>> the system more stable.
> >>>> 
> >>>> Signed-off-by: Andy Yan <andy.yan at rock-chips.com>
> >>>> ---
> >>>> 
> >>>>    arch/arm/boot/dts/rk3066a-rayeager.dts | 8 ++++++++
> >>>>    1 file changed, 8 insertions(+)
> >>>> 
> >>>> diff --git a/arch/arm/boot/dts/rk3066a-rayeager.dts
> >>>> b/arch/arm/boot/dts/rk3066a-rayeager.dts index 0bf6876..3d4c104
> >>>> 100644
> >>>> --- a/arch/arm/boot/dts/rk3066a-rayeager.dts
> >>>> +++ b/arch/arm/boot/dts/rk3066a-rayeager.dts
> >>>> @@ -159,6 +159,14 @@
> >>>> 
> >>>>    &cpu0 {
> >>>>    
> >>>>          cpu0-supply = <&vdd_arm>;
> >>>> 
> >>>> +       operating-points = <
> >>>> +               /* kHz    uV */
> >>>> +               1008000 1125000
> >>>> +               816000  1125000
> >>>> +               600000  1100000
> >>>> +               504000  1100000
> >>>> +               312000  1075000
> >>>> +       >;
> >>>> 
> >>>>    };
> >>>>    
> >>>>    &emac {
> >>> 
> >>> Does this only apply to the rayeager-board / px2 soc or for all
> >>> rk3066a/px2
> >>> boards? I.e. the original values come from some sdk kernel and I
> >>> might've
> >>> transcribed that wrong at the time and I remember having problems
> >>> trying
> >>> to
> >>> reach higher frequencies as well.
> >>> 
> >>> So could you check what the full operating points looks like for both
> >>> and
> >>> if
> >>> this can move to the rk3066a.dtsi, as both should be able to reach at
> >>> least
> >>> 1.4GHz / 1.6GHz.
> >>> 
> >>> 
> >>> Thanks
> >>> Heiko
> >>> 
> >>> ______
> >>> 
> >>      I have checked the sdk kernel, the opp for the sdk board is like 
bellow:
> >>   /**
> >>   * dvfs_cpu_logic_table: table for arm and logic dvfs
> >>   * @frequency   : arm frequency
> >>   * @cpu_volt    : arm voltage depend on frequency
> >>   * @logic_volt  : logic voltage arm requests depend on frequency
> >>   * comments     : min arm/logic voltage
> >>   */
> >> 
> >> static struct dvfs_arm_table dvfs_cpu_logic_table[] = {
> >> 
> >>          {.frequency = 252 * 1000,       .cpu_volt = 1075 * 1000,
> >> 
> >> .logic_volt = 1125 * 1000},//0.975V/1.000V
> >> 
> >>          {.frequency = 504 * 1000,       .cpu_volt = 1100 * 1000,
> >> 
> >> .logic_volt = 1125 * 1000},//0.975V/1.000V
> >> 
> >>          {.frequency = 816 * 1000,       .cpu_volt = 1125 * 1000,
> >> 
> >> .logic_volt = 1150 * 1000},//1.000V/1.025V
> >> 
> >>          {.frequency = 1008 * 1000,      .cpu_volt = 1125 * 1000,
> >> 
> >> .logic_volt = 1150 * 1000},//1.025V/1.050V
> >> 
> >>          {.frequency = 1200 * 1000,      .cpu_volt = 1175 * 1000,
> >> 
> >> .logic_volt = 1200 * 1000},//1.100V/1.050V
> >> 
> >>          {.frequency = 1272 * 1000,      .cpu_volt = 1225 * 1000,
> >> 
> >> .logic_volt = 1200 * 1000},//1.150V/1.100V
> >> 
> >>          {.frequency = 1416 * 1000,      .cpu_volt = 1300 * 1000,
> >> 
> >> .logic_volt = 1200 * 1000},//1.225V/1.100V
> >> 
> >>          {.frequency = 1512 * 1000,      .cpu_volt = 1350 * 1000,
> >> 
> >> .logic_volt = 1250 * 1000},//1.300V/1.150V
> >> 
> >>          {.frequency = 1608 * 1000,      .cpu_volt = 1425 * 1000,
> >> 
> >> .logic_volt = 1300 * 1000},//1.325V/1.175V
> >> 
> >>          {.frequency = CPUFREQ_TABLE_END},
> >> 
> >> };
> >> 
> >>   This table is made base on mass stress test. So is a safe opp table
> >>   for
> >> 
> >> most rk3066a base boards. But it some
> >> 
> >>   board is not so well in power supply circuit design, the may also
> >>   need to
> >> 
> >> adjust it.
> >> 
> >>   From the table we see that the logic_voltage need to increase  as the
> >> 
> >> cpufreq increase. But it seems that the current mainline kernel does
> >> not
> >> support adjust the logic voltage when cpufreq change. So we can only
> >> scale the cpufreq to 1.4GHZ by fixed the logic_volt at 1.2V.
> >> 
> >>   If you like, I can move it to rk3066a.dtsi with the max freq at
> >>   1008MHZ, as>> 
> >> I haven't  test to 1.4GHZ  with the mainline kernel. Or give me some
> >> time to test to see if the rayeager board can run to 1.4G stable with
> >> the mainline .
>    I have run Rayeager  board with cpufreq freely change from
> 312MHZ~1416MHZ for 11 hours now, it's stable. Maybe we can set the
> default opp table for rk3066a/PX2 from 312MHZ~1416HZ in current mainline.

Thanks for doing that stress test and making that the default setting would 
be a nice way to go :-) .

I just checked, the other 2 rk3066a boards we currently support (Hayou 
Marsboard and BQ Curie2) use the exact same power-layout (tps65210 + pwm-
regulator for vdd_log), so should be quite save to do.


Heiko
Heiko


From andy.yan at rock-chips.com  Fri Jan 15 01:34:31 2016
From: andy.yan at rock-chips.com (Andy Yan)
Date: Fri, 15 Jan 2016 17:34:31 +0800
Subject: [PATCH 1/2] ARM: dts: rockchip: increase vdd_arm voltage for
 rk3066a-rayeager board
In-Reply-To: <24626895.xt4u2Kuv7p@phil>
References: <1452774780-16493-1-git-send-email-andy.yan@rock-chips.com>
 <CANCyOWak0wMNRPxkJtE2DJ1UvCneWxuNXjwjxjM3+_Jjuaahvw@mail.gmail.com>
 <56984734.6000806@rock-chips.com> <24626895.xt4u2Kuv7p@phil>
Message-ID: <5698BD27.3050700@rock-chips.com>

Hi Heiko:

On 2016?01?15? 17:22, Heiko Stuebner wrote:
> Hi Andy, Eddie,
>
> Am Freitag, 15. Januar 2016, 09:11:16 schrieb Andy Yan:
>> On 2016?01?14? 21:50, Eddie Cai wrote:
>>> Hi Heiko
>>>
>>> I don't want open source version kernel support too high frequency.
>>> Many people use RK3066/PX2 on their board. We don't know if their
>>> layout is good enough to support higher frequency. ie. bad layout will
>>> cause more voltage drop on the VDD_CPU power rail. So it may need
>>> higher voltage. I think stable is more important here. People want
>>> higher frequency can add more opp depend on their board's layout.
>>>
>>> 2016-01-14 21:20 GMT+08:00 Andy Yan <andy.yan at rock-chips.com>:
>>>> Hi Heiko:
>>>>
>>>> On 2016?01?14? 20:45, Heiko Stuebner wrote:
>>>>> Hi Andy,
>>>>>
>>>>> Am Donnerstag, 14. Januar 2016, 20:34:20 schrieb Andy Yan:
>>>>>> The current vdd_arm voltage is too low, increase it will make
>>>>>> the system more stable.
>>>>>>
>>>>>> Signed-off-by: Andy Yan <andy.yan at rock-chips.com>
>>>>>> ---
>>>>>>
>>>>>>     arch/arm/boot/dts/rk3066a-rayeager.dts | 8 ++++++++
>>>>>>     1 file changed, 8 insertions(+)
>>>>>>
>>>>>> diff --git a/arch/arm/boot/dts/rk3066a-rayeager.dts
>>>>>> b/arch/arm/boot/dts/rk3066a-rayeager.dts index 0bf6876..3d4c104
>>>>>> 100644
>>>>>> --- a/arch/arm/boot/dts/rk3066a-rayeager.dts
>>>>>> +++ b/arch/arm/boot/dts/rk3066a-rayeager.dts
>>>>>> @@ -159,6 +159,14 @@
>>>>>>
>>>>>>     &cpu0 {
>>>>>>     
>>>>>>           cpu0-supply = <&vdd_arm>;
>>>>>>
>>>>>> +       operating-points = <
>>>>>> +               /* kHz    uV */
>>>>>> +               1008000 1125000
>>>>>> +               816000  1125000
>>>>>> +               600000  1100000
>>>>>> +               504000  1100000
>>>>>> +               312000  1075000
>>>>>> +       >;
>>>>>>
>>>>>>     };
>>>>>>     
>>>>>>     &emac {
>>>>> Does this only apply to the rayeager-board / px2 soc or for all
>>>>> rk3066a/px2
>>>>> boards? I.e. the original values come from some sdk kernel and I
>>>>> might've
>>>>> transcribed that wrong at the time and I remember having problems
>>>>> trying
>>>>> to
>>>>> reach higher frequencies as well.
>>>>>
>>>>> So could you check what the full operating points looks like for both
>>>>> and
>>>>> if
>>>>> this can move to the rk3066a.dtsi, as both should be able to reach at
>>>>> least
>>>>> 1.4GHz / 1.6GHz.
>>>>>
>>>>>
>>>>> Thanks
>>>>> Heiko
>>>>>
>>>>> ______
>>>>>
>>>>       I have checked the sdk kernel, the opp for the sdk board is like
> bellow:
>>>>    /**
>>>>    * dvfs_cpu_logic_table: table for arm and logic dvfs
>>>>    * @frequency   : arm frequency
>>>>    * @cpu_volt    : arm voltage depend on frequency
>>>>    * @logic_volt  : logic voltage arm requests depend on frequency
>>>>    * comments     : min arm/logic voltage
>>>>    */
>>>>
>>>> static struct dvfs_arm_table dvfs_cpu_logic_table[] = {
>>>>
>>>>           {.frequency = 252 * 1000,       .cpu_volt = 1075 * 1000,
>>>>
>>>> .logic_volt = 1125 * 1000},//0.975V/1.000V
>>>>
>>>>           {.frequency = 504 * 1000,       .cpu_volt = 1100 * 1000,
>>>>
>>>> .logic_volt = 1125 * 1000},//0.975V/1.000V
>>>>
>>>>           {.frequency = 816 * 1000,       .cpu_volt = 1125 * 1000,
>>>>
>>>> .logic_volt = 1150 * 1000},//1.000V/1.025V
>>>>
>>>>           {.frequency = 1008 * 1000,      .cpu_volt = 1125 * 1000,
>>>>
>>>> .logic_volt = 1150 * 1000},//1.025V/1.050V
>>>>
>>>>           {.frequency = 1200 * 1000,      .cpu_volt = 1175 * 1000,
>>>>
>>>> .logic_volt = 1200 * 1000},//1.100V/1.050V
>>>>
>>>>           {.frequency = 1272 * 1000,      .cpu_volt = 1225 * 1000,
>>>>
>>>> .logic_volt = 1200 * 1000},//1.150V/1.100V
>>>>
>>>>           {.frequency = 1416 * 1000,      .cpu_volt = 1300 * 1000,
>>>>
>>>> .logic_volt = 1200 * 1000},//1.225V/1.100V
>>>>
>>>>           {.frequency = 1512 * 1000,      .cpu_volt = 1350 * 1000,
>>>>
>>>> .logic_volt = 1250 * 1000},//1.300V/1.150V
>>>>
>>>>           {.frequency = 1608 * 1000,      .cpu_volt = 1425 * 1000,
>>>>
>>>> .logic_volt = 1300 * 1000},//1.325V/1.175V
>>>>
>>>>           {.frequency = CPUFREQ_TABLE_END},
>>>>
>>>> };
>>>>
>>>>    This table is made base on mass stress test. So is a safe opp table
>>>>    for
>>>>
>>>> most rk3066a base boards. But it some
>>>>
>>>>    board is not so well in power supply circuit design, the may also
>>>>    need to
>>>>
>>>> adjust it.
>>>>
>>>>    From the table we see that the logic_voltage need to increase  as the
>>>>
>>>> cpufreq increase. But it seems that the current mainline kernel does
>>>> not
>>>> support adjust the logic voltage when cpufreq change. So we can only
>>>> scale the cpufreq to 1.4GHZ by fixed the logic_volt at 1.2V.
>>>>
>>>>    If you like, I can move it to rk3066a.dtsi with the max freq at
>>>>    1008MHZ, as>>
>>>> I haven't  test to 1.4GHZ  with the mainline kernel. Or give me some
>>>> time to test to see if the rayeager board can run to 1.4G stable with
>>>> the mainline .
>>     I have run Rayeager  board with cpufreq freely change from
>> 312MHZ~1416MHZ for 11 hours now, it's stable. Maybe we can set the
>> default opp table for rk3066a/PX2 from 312MHZ~1416HZ in current mainline.
> Thanks for doing that stress test and making that the default setting would
> be a nice way to go :-) .
>
> I just checked, the other 2 rk3066a boards we currently support (Hayou
> Marsboard and BQ Curie2) use the exact same power-layout (tps65210 + pwm-
> regulator for vdd_log), so should be quite save to do.
>
>
> Heiko
> Heiko
>
>
>
>
   Actually, all the rockchip rk3066 base platform use the same power 
supply design( control vdd_logic voltage by pwm).
   So I can move the opp table with max freq 1416MHZ to rk3066a.dtsi and 
keep the pwm-regulator in board specific dts?




From ykk at rock-chips.com  Fri Jan 15 01:36:38 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 15 Jan 2016 17:36:38 +0800
Subject: [PATCH v3 0/2] Introduce Innosilicon HDMI driver on Rockchip platforms
Message-ID: <1452850598-30859-1-git-send-email-ykk@rock-chips.com>


Here are a brief introduction to Innosilicon HDMI IP:
 - Support HDMI 1.4a, HDCP 1.2 and DVI 1.0 standard compliant transmitter
 - Support HDMI1.4 a/b 3D function defined in HDMI 1.4 a/b spec
 - Digital video interface supports a pixel size of 24, 30, 36, 48bits color
   depth in RGB
 - S/PDIF output supports PCM, Dolby Digital, DTS digital audio transmission
   (32-192kHz Fs) using IEC60958 and IEC 61937
 - The EDID and CEC function are also supported by Innosilicon HDMI Transmitter
   Controlle

This series is based on Mark's RK3036 VOP series, I have created an test
branch that collected his changes:
[https://github.com/rockchip-linux/kernel/tree/kylin-develop4.4-test]

Best regards,
- Yakir


Changes in v3:
- Use encoder enable/disable function, and remove the encoder DPMS function
- Keep HDMI PLL power on in standby mode

Changes in v2:
- Using DRM atomic helper functions for connector init (Mark)
- Remove "hdmi->connector.encoder = encoder;" (Mark)
- Correct the misspell "rk3036-dw-hdmi" (Heiko)

Yakir Yang (2):
  FORMLIST: drm: rockchip/hdmi: add Innosilicon HDMI support
  dt-bindings: add document for Innosilicon HDMI on Rockchip platform

 .../display/rockchip/inno_hdmi-rockchip.txt        |  50 ++
 drivers/gpu/drm/rockchip/Kconfig                   |   8 +
 drivers/gpu/drm/rockchip/Makefile                  |   1 +
 drivers/gpu/drm/rockchip/inno_hdmi.c               | 999 +++++++++++++++++++++
 drivers/gpu/drm/rockchip/inno_hdmi.h               | 364 ++++++++
 5 files changed, 1422 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt
 create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.c
 create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.h

-- 
1.9.1




From ykk at rock-chips.com  Fri Jan 15 01:38:10 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 15 Jan 2016 17:38:10 +0800
Subject: [PATCH v3 1/2] drm: rockchip/hdmi: add Innosilicon HDMI support
In-Reply-To: <1452850598-30859-1-git-send-email-ykk@rock-chips.com>
References: <1452850598-30859-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1452850690-31760-1-git-send-email-ykk@rock-chips.com>

The Innosilicon HDMI is a low power HDMI 1.4 transmitter
IP, and it have been integrated on some rockchip CPUs
(like RK3036, RK312x).

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v3:
- Use encoder enable/disable function, and remove the encoder DPMS function
- Keep HDMI PLL power on in standby mode

Changes in v2:
- Using DRM atomic helper functions for connector init (Mark)
- Remove "hdmi->connector.encoder = encoder;" (Mark)

 drivers/gpu/drm/rockchip/Kconfig     |   8 +
 drivers/gpu/drm/rockchip/Makefile    |   1 +
 drivers/gpu/drm/rockchip/inno_hdmi.c | 999 +++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/inno_hdmi.h | 364 +++++++++++++
 4 files changed, 1372 insertions(+)
 create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.c
 create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.h

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 35215f6..a5014e0 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -25,3 +25,11 @@ config ROCKCHIP_DW_HDMI
 	  for the Synopsys DesignWare HDMI driver. If you want to
 	  enable HDMI on RK3288 based SoC, you should selet this
 	  option.
+
+config ROCKCHIP_INNO_HDMI
+	tristate "Rockchip specific extensions for Innosilicon HDMI"
+        depends on DRM_ROCKCHIP
+        help
+	  This selects support for Rockchip SoC specific extensions
+	  for the Innosilicon HDMI driver. If you want to enable
+	  HDMI on RK3036 based SoC, you should selet this option.
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index a9d380f..da2bf76 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -6,6 +6,7 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
 		rockchip_drm_gem.o
 
 obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
+obj-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
 
 obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_drm_vop.o \
 				rockchip_vop_reg.o
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
new file mode 100644
index 0000000..dc98179
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -0,0 +1,999 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ *    Zheng Yang <zhengyang at rock-chips.com>
+ *    Yakir Yang <ykk at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/hdmi.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+
+#include <drm/drm_of.h>
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_encoder_slave.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_vop.h"
+
+#include "inno_hdmi.h"
+
+#define to_inno_hdmi(x)	container_of(x, struct inno_hdmi, x)
+
+struct hdmi_data_info {
+	int vic;
+	bool sink_is_hdmi;
+	bool sink_has_audio;
+	unsigned int enc_in_format;
+	unsigned int enc_out_format;
+	unsigned int colorimetry;
+};
+
+struct inno_hdmi_i2c {
+	struct i2c_adapter adap;
+
+	u8 ddc_addr;
+	u8 segment_addr;
+
+	struct mutex lock;
+	struct completion cmp;
+};
+
+struct inno_hdmi {
+	struct device *dev;
+	struct drm_device *drm_dev;
+
+	int irq;
+	struct clk *pclk;
+	void __iomem *regs;
+
+	struct drm_connector	connector;
+	struct drm_encoder	encoder;
+
+	struct inno_hdmi_i2c *i2c;
+	struct i2c_adapter *ddc;
+
+	unsigned int tmds_rate;
+
+	struct hdmi_data_info	hdmi_data;
+	struct drm_display_mode previous_mode;
+};
+
+enum {
+	CSC_ITU601_16_235_TO_RGB_0_255_8BIT,
+	CSC_ITU601_0_255_TO_RGB_0_255_8BIT,
+	CSC_ITU709_16_235_TO_RGB_0_255_8BIT,
+	CSC_RGB_0_255_TO_ITU601_16_235_8BIT,
+	CSC_RGB_0_255_TO_ITU709_16_235_8BIT,
+	CSC_RGB_0_255_TO_RGB_16_235_8BIT,
+};
+
+static const char coeff_csc[][24] = {
+	/*
+	 * YUV2RGB:601 SD mode(Y[16:235], UV[16:240], RGB[0:255]):
+	 *   R = 1.164*Y + 1.596*V - 204
+	 *   G = 1.164*Y - 0.391*U - 0.813*V + 154
+	 *   B = 1.164*Y + 2.018*U - 258
+	 */
+	{
+		0x04, 0xa7, 0x00, 0x00, 0x06, 0x62, 0x02, 0xcc,
+		0x04, 0xa7, 0x11, 0x90, 0x13, 0x40, 0x00, 0x9a,
+		0x04, 0xa7, 0x08, 0x12, 0x00, 0x00, 0x03, 0x02
+	},
+	/*
+	 * YUV2RGB:601 SD mode(YUV[0:255],RGB[0:255]):
+	 *   R = Y + 1.402*V - 248
+	 *   G = Y - 0.344*U - 0.714*V + 135
+	 *   B = Y + 1.772*U - 227
+	 */
+	{
+		0x04, 0x00, 0x00, 0x00, 0x05, 0x9b, 0x02, 0xf8,
+		0x04, 0x00, 0x11, 0x60, 0x12, 0xdb, 0x00, 0x87,
+		0x04, 0x00, 0x07, 0x16, 0x00, 0x00, 0x02, 0xe3
+	},
+	/*
+	 * YUV2RGB:709 HD mode(Y[16:235],UV[16:240],RGB[0:255]):
+	 *   R = 1.164*Y + 1.793*V - 248
+	 *   G = 1.164*Y - 0.213*U - 0.534*V + 77
+	 *   B = 1.164*Y + 2.115*U - 289
+	 */
+	{
+		0x04, 0xa7, 0x00, 0x00, 0x07, 0x2c, 0x02, 0xf8,
+		0x04, 0xa7, 0x10, 0xda, 0x12, 0x22, 0x00, 0x4d,
+		0x04, 0xa7, 0x08, 0x74, 0x00, 0x00, 0x03, 0x21
+	},
+
+	/*
+	 * RGB2YUV:601 SD mode:
+	 *   Cb = -0.291G - 0.148R + 0.439B + 128
+	 *   Y  = 0.504G  + 0.257R + 0.098B + 16
+	 *   Cr = -0.368G + 0.439R - 0.071B + 128
+	 */
+	{
+		0x11, 0x5f, 0x01, 0x82, 0x10, 0x23, 0x00, 0x80,
+		0x02, 0x1c, 0x00, 0xa1, 0x00, 0x36, 0x00, 0x1e,
+		0x11, 0x29, 0x10, 0x59, 0x01, 0x82, 0x00, 0x80
+	},
+	/*
+	 * RGB2YUV:709 HD mode:
+	 *   Cb = - 0.338G - 0.101R + 0.439B + 128
+	 *   Y  = 0.614G   + 0.183R + 0.062B + 16
+	 *   Cr = - 0.399G + 0.439R - 0.040B + 128
+	 */
+	{
+		0x11, 0x98, 0x01, 0xc1, 0x10, 0x28, 0x00, 0x80,
+		0x02, 0x74, 0x00, 0xbb, 0x00, 0x3f, 0x00, 0x10,
+		0x11, 0x5a, 0x10, 0x67, 0x01, 0xc1, 0x00, 0x80
+	},
+	/*
+	 * RGB[0:255]2RGB[16:235]:
+	 *   R' = R x (235-16)/255 + 16;
+	 *   G' = G x (235-16)/255 + 16;
+	 *   B' = B x (235-16)/255 + 16;
+	 */
+	{
+		0x00, 0x00, 0x03, 0x6F, 0x00, 0x00, 0x00, 0x10,
+		0x03, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+		0x00, 0x00, 0x00, 0x00, 0x03, 0x6F, 0x00, 0x10
+	},
+};
+
+static inline u8 hdmi_readb(struct inno_hdmi *hdmi, u16 offset)
+{
+	return readl_relaxed(hdmi->regs + (offset) * 0x04);
+}
+
+static inline void hdmi_writeb(struct inno_hdmi *hdmi, u16 offset, u32 val)
+{
+	writel_relaxed(val, hdmi->regs + (offset) * 0x04);
+}
+
+static inline void hdmi_modb(struct inno_hdmi *hdmi, u16 offset,
+			     u32 msk, u32 val)
+{
+	u8 temp = hdmi_readb(hdmi, offset) & ~msk;
+
+	temp |= val & msk;
+	hdmi_writeb(hdmi, offset, temp);
+}
+
+static void inno_hdmi_i2c_init(struct inno_hdmi *hdmi)
+{
+	int ddc_bus_freq;
+
+	ddc_bus_freq = (hdmi->tmds_rate >> 2) / HDMI_SCL_RATE;
+
+	hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
+	hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
+
+	/* Clear the EDID interrupt flag and mute the interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+}
+
+static void inno_hdmi_sys_power(struct inno_hdmi *hdmi, bool enable)
+{
+	if (enable)
+		hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON);
+	else
+		hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
+}
+
+static void inno_hdmi_set_pwr_mode(struct inno_hdmi *hdmi, int mode)
+{
+	switch (mode) {
+	case NORMAL:
+		inno_hdmi_sys_power(hdmi, false);
+
+		hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x6f);
+		hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0xbb);
+
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x14);
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x10);
+		hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x0f);
+		hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x01);
+
+		inno_hdmi_sys_power(hdmi, true);
+		break;
+
+	case LOWER_PWR:
+		inno_hdmi_sys_power(hdmi, false);
+		hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
+
+		break;
+
+	default:
+		dev_err(hdmi->dev, "Unknown power mode %d\n", mode);
+	}
+}
+
+static void inno_hdmi_reset(struct inno_hdmi *hdmi)
+{
+	u32 val;
+	u32 msk;
+
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_DIGITAL, v_NOT_RST_DIGITAL);
+	udelay(100);
+
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_ANALOG, v_NOT_RST_ANALOG);
+	udelay(100);
+
+	msk = m_REG_CLK_INV | m_REG_CLK_SOURCE | m_POWER | m_INT_POL;
+	val = v_REG_CLK_INV | v_REG_CLK_SOURCE_SYS | v_PWR_ON | v_INT_POL_HIGH;
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, msk, val);
+
+	inno_hdmi_set_pwr_mode(hdmi, NORMAL);
+}
+
+static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi)
+{
+	char info[HDMI_SIZE_AVI_INFOFRAME] = {0};
+	int avi_color_mode;
+	int i;
+
+	hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_AVI);
+
+	info[0] = 0x82;
+	info[1] = 0x02;
+	info[2] = 0x0D;
+	info[3] = info[0] + info[1] + info[2];
+
+	if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_RGB)
+		avi_color_mode = AVI_COLOR_MODE_RGB;
+	else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV444)
+		avi_color_mode = AVI_COLOR_MODE_YCBCR444;
+	else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV422)
+		avi_color_mode = AVI_COLOR_MODE_YCBCR422;
+	else
+		avi_color_mode = AVI_COLOR_MODE_RGB;
+
+	info[4] = (avi_color_mode << 5);
+	info[5] = (AVI_COLORIMETRY_NO_DATA << 6) |
+		  (AVI_CODED_FRAME_ASPECT_NO_DATA << 4) |
+		  ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME;
+
+	info[6] = 0;
+	info[7] = hdmi->hdmi_data.vic;
+
+	if (hdmi->hdmi_data.vic == 6 || hdmi->hdmi_data.vic == 7 ||
+	    hdmi->hdmi_data.vic == 21 || hdmi->hdmi_data.vic == 22)
+		info[8] = 1;
+	else
+		info[8] = 0;
+
+	/* Calculate avi info frame checKsum */
+	for (i = 4; i < HDMI_SIZE_AVI_INFOFRAME; i++)
+		info[3] += info[i];
+	info[3] = 0x100 - info[3];
+
+	for (i = 0; i < HDMI_SIZE_AVI_INFOFRAME; i++)
+		hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i, info[i]);
+
+	return 0;
+}
+
+static int inno_hdmi_config_video_vsi(struct inno_hdmi *hdmi)
+{
+	char info[HDMI_SIZE_VSI_INFOFRAME] = {0};
+	int i;
+
+	hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, m_PACKET_VSI_EN,
+		  v_PACKET_VSI_EN(0));
+
+	hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_VSI);
+
+	/* Header Bytes */
+	info[0] = 0x81;
+	info[1] = 0x01;
+
+	/* PB1 - PB3 contain the 24bit IEEE Registration Identifier */
+	info[4] = 0x03;
+	info[5] = 0x0c;
+	info[6] = 0x00;
+
+	/* PB4 - HDMI_Video_Format into bits 7:5 */
+	info[7] = 0;
+
+	/*
+	 * PB5 - Depending on the video format, this byte will contain
+	 * either the HDMI_VIC code in buts 7:0, OR the 3D_Structure in
+	 * bits 7:4.
+	 */
+	info[2] = 0x06 - 2;
+	info[8] = 0;
+	info[9] = 0;
+
+	info[3] = info[0] + info[1] + info[2];
+
+	/* Calculate info frame checKsum */
+	for (i = 4; i < HDMI_SIZE_VSI_INFOFRAME; i++)
+		info[3] += info[i];
+	info[3] = 0x100 - info[3];
+
+	for (i = 0; i < HDMI_SIZE_VSI_INFOFRAME; i++)
+		hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i, info[i]);
+
+	hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, m_PACKET_VSI_EN,
+		  v_PACKET_VSI_EN(1));
+
+	return 0;
+}
+
+static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
+{
+	struct hdmi_data_info *data = &hdmi->hdmi_data;
+	int c0_c2_change = 0;
+	int csc_enable = 0;
+	int csc_mode = 0;
+	int auto_csc = 0;
+	int value;
+	int i;
+
+	/* Input video mode is SDR RGB24bit, data enable signal from external */
+	hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL1, v_DE_EXTERNAL |
+		    v_VIDEO_INPUT_FORMAT(VIDEO_INPUT_SDR_RGB444));
+
+	/* Input color hardcode to RGB, and output color hardcode to RGB888 */
+	value = v_VIDEO_INPUT_BITS(VIDEO_INPUT_8BITS) |
+		v_VIDEO_OUTPUT_COLOR(0) |
+		v_VIDEO_INPUT_CSP(0);
+	hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL2, value);
+
+	if (data->enc_out_format == data->enc_out_format) {
+		if ((data->enc_in_format == HDMI_COLORSPACE_RGB) ||
+		    (data->enc_in_format >= HDMI_COLORSPACE_YUV444)) {
+			value = v_SOF_DISABLE | v_COLOR_DEPTH_NOT_INDICATED(1);
+			hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
+
+			hdmi_modb(hdmi, HDMI_VIDEO_CONTRL,
+				  m_VIDEO_AUTO_CSC | m_VIDEO_C0_C2_SWAP,
+				  v_VIDEO_AUTO_CSC(AUTO_CSC_DISABLE) |
+				  v_VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE));
+			return 0;
+		}
+	}
+
+	if (data->colorimetry == HDMI_COLORIMETRY_ITU_601) {
+		if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
+		    (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
+			csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT;
+			auto_csc = AUTO_CSC_DISABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = v_CSC_ENABLE;
+		} else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
+			   (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
+			csc_mode = CSC_ITU601_16_235_TO_RGB_0_255_8BIT;
+			auto_csc = AUTO_CSC_ENABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = v_CSC_DISABLE;
+		}
+	} else {
+		if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
+		    (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
+			csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT;
+			auto_csc = AUTO_CSC_DISABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = v_CSC_ENABLE;
+		} else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
+			   (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
+			csc_mode = CSC_ITU709_16_235_TO_RGB_0_255_8BIT;
+			auto_csc = AUTO_CSC_ENABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = v_CSC_DISABLE;
+		}
+	}
+
+	for (i = 0; i < 24; i++)
+		hdmi_writeb(hdmi, HDMI_VIDEO_CSC_COEF + i,
+			    coeff_csc[csc_mode][i]);
+
+	value = v_SOF_DISABLE | csc_enable | v_COLOR_DEPTH_NOT_INDICATED(1);
+	hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
+	hdmi_modb(hdmi, HDMI_VIDEO_CONTRL, m_VIDEO_AUTO_CSC |
+		  m_VIDEO_C0_C2_SWAP, v_VIDEO_AUTO_CSC(auto_csc) |
+		  v_VIDEO_C0_C2_SWAP(c0_c2_change));
+
+	return 0;
+}
+
+static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi,
+					 struct drm_display_mode *mode)
+{
+	int value;
+
+	/* Set detail external video timing polarity and interlace mode */
+	value = v_EXTERANL_VIDEO(1);
+	value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
+		 v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
+	value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
+		 v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
+	value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
+		 v_INETLACE(1) : v_INETLACE(0);
+	hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
+
+	/* Set detail external video timing */
+	value = mode->htotal;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
+
+	value = mode->htotal - mode->hdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
+
+	value = mode->hsync_start - mode->hdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
+
+	value = mode->hsync_end - mode->hsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
+
+	value = mode->vtotal;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
+
+	value = mode->vtotal - mode->vdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
+
+	value = mode->vsync_start - mode->vdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
+
+	value = mode->vsync_end - mode->vsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
+
+	hdmi_writeb(hdmi, HDMI_PHY_PRE_DIV_RATIO, 0x1e);
+	hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_LOW, 0x2c);
+	hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH, 0x01);
+
+	return 0;
+}
+
+static int inno_hdmi_setup(struct inno_hdmi *hdmi,
+			   struct drm_display_mode *mode)
+{
+	int value;
+
+	hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
+
+	hdmi->hdmi_data.enc_in_format = HDMI_COLORSPACE_RGB;
+	hdmi->hdmi_data.enc_out_format = HDMI_COLORSPACE_RGB;
+
+	if ((hdmi->hdmi_data.vic == 6) || (hdmi->hdmi_data.vic == 7) ||
+	    (hdmi->hdmi_data.vic == 21) || (hdmi->hdmi_data.vic == 22) ||
+	    (hdmi->hdmi_data.vic == 2) || (hdmi->hdmi_data.vic == 3) ||
+	    (hdmi->hdmi_data.vic == 17) || (hdmi->hdmi_data.vic == 18))
+		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601;
+	else
+		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
+
+	/* Mute video and audio output */
+	hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
+		  v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1));
+
+	/* Set HDMI Mode */
+	hdmi_writeb(hdmi, HDMI_HDCP_CTRL,
+		    v_HDMI_DVI(hdmi->hdmi_data.sink_is_hdmi));
+
+	inno_hdmi_config_video_timing(hdmi, mode);
+
+	inno_hdmi_config_video_csc(hdmi);
+
+	if (hdmi->hdmi_data.sink_is_hdmi) {
+		inno_hdmi_config_video_avi(hdmi);
+		inno_hdmi_config_video_vsi(hdmi);
+	}
+
+	/*
+	 * When IP controller have configured to an accurate video
+	 * timing, then the TMDS clock source would be switched to
+	 * DCLK_LCDC, so we need to init the TMDS rate to mode pixel
+	 * clock rate, and reconfigure the DDC clock.
+	 */
+	hdmi->tmds_rate = mode->clock * 1000;
+	inno_hdmi_i2c_init(hdmi);
+
+	/* Unmute video and audio output */
+	hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
+		  v_AUDIO_MUTE(0) | v_VIDEO_MUTE(0));
+
+	return 0;
+}
+
+static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder,
+				       struct drm_display_mode *mode,
+				       struct drm_display_mode *adj_mode)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+
+	inno_hdmi_setup(hdmi, adj_mode);
+
+	/* Store the display mode for plugin/DKMS poweron events */
+	memcpy(&hdmi->previous_mode, adj_mode, sizeof(hdmi->previous_mode));
+}
+
+static void inno_hdmi_encoder_enable(struct drm_encoder *encoder)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+
+	inno_hdmi_set_pwr_mode(hdmi, NORMAL);
+}
+
+static void inno_hdmi_encoder_disable(struct drm_encoder *encoder)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+
+	inno_hdmi_set_pwr_mode(hdmi, LOWER_PWR);
+}
+
+static void inno_hdmi_encoder_commit(struct drm_encoder *encoder)
+{
+}
+
+static void inno_hdmi_encoder_prepare(struct drm_encoder *encoder)
+{
+	rockchip_drm_crtc_mode_config(encoder->crtc, DRM_MODE_CONNECTOR_HDMIA,
+				      ROCKCHIP_OUT_MODE_P888);
+}
+
+static bool inno_hdmi_encoder_mode_fixup(struct drm_encoder *encoder,
+					 const struct drm_display_mode *mode,
+					 struct drm_display_mode *adj_mode)
+{
+	return true;
+}
+
+static struct drm_encoder_helper_funcs inno_hdmi_encoder_helper_funcs = {
+	.enable     = inno_hdmi_encoder_enable,
+	.disable    = inno_hdmi_encoder_disable,
+	.mode_fixup = inno_hdmi_encoder_mode_fixup,
+	.mode_set   = inno_hdmi_encoder_mode_set,
+	.prepare    = inno_hdmi_encoder_prepare,
+	.commit     = inno_hdmi_encoder_commit,
+};
+
+static struct drm_encoder_funcs inno_hdmi_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static enum drm_connector_status
+inno_hdmi_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+
+	return (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
+		connector_status_connected : connector_status_disconnected;
+}
+
+static int inno_hdmi_connector_get_modes(struct drm_connector *connector)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+	struct edid *edid;
+	int ret = 0;
+
+	if (!hdmi->ddc)
+		return 0;
+
+	edid = drm_get_edid(connector, hdmi->ddc);
+	if (edid) {
+		hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
+		hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
+		drm_mode_connector_update_edid_property(connector, edid);
+		ret = drm_add_edid_modes(connector, edid);
+		kfree(edid);
+	}
+
+	return ret;
+}
+
+static enum drm_mode_status
+inno_hdmi_connector_mode_valid(struct drm_connector *connector,
+			       struct drm_display_mode *mode)
+{
+	return MODE_OK;
+}
+
+static struct drm_encoder *
+inno_hdmi_connector_best_encoder(struct drm_connector *connector)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+
+	return &hdmi->encoder;
+}
+
+static int
+inno_hdmi_probe_single_connector_modes(struct drm_connector *connector,
+				       uint32_t maxX, uint32_t maxY)
+{
+	return drm_helper_probe_single_connector_modes(connector, 1920, 1080);
+}
+
+static void inno_hdmi_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs inno_hdmi_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.fill_modes = inno_hdmi_probe_single_connector_modes,
+	.detect = inno_hdmi_connector_detect,
+	.destroy = inno_hdmi_connector_destroy,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static struct drm_connector_helper_funcs inno_hdmi_connector_helper_funcs = {
+	.get_modes = inno_hdmi_connector_get_modes,
+	.mode_valid = inno_hdmi_connector_mode_valid,
+	.best_encoder = inno_hdmi_connector_best_encoder,
+};
+
+static int inno_hdmi_register(struct drm_device *drm, struct inno_hdmi *hdmi)
+{
+	struct drm_encoder *encoder = &hdmi->encoder;
+
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, hdmi->dev->of_node);
+	/*
+	 * If we failed to find the CRTC(s) which this encoder is
+	 * supposed to be connected to, it's because the CRTC has
+	 * not been registered yet.  Defer probing, and hope that
+	 * the required CRTC is added later.
+	 */
+	if (encoder->possible_crtcs == 0)
+		return -EPROBE_DEFER;
+
+	drm_encoder_helper_add(encoder, &inno_hdmi_encoder_helper_funcs);
+	drm_encoder_init(drm, encoder, &inno_hdmi_encoder_funcs,
+			 DRM_MODE_ENCODER_TMDS, NULL);
+
+	hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
+
+	drm_connector_helper_add(&hdmi->connector,
+				 &inno_hdmi_connector_helper_funcs);
+	drm_connector_init(drm, &hdmi->connector, &inno_hdmi_connector_funcs,
+			   DRM_MODE_CONNECTOR_HDMIA);
+
+	drm_mode_connector_attach_encoder(&hdmi->connector, encoder);
+
+	return 0;
+}
+
+static irqreturn_t inno_hdmi_i2c_irq(struct inno_hdmi *hdmi)
+{
+	struct inno_hdmi_i2c *i2c = hdmi->i2c;
+	u8 stat;
+
+	stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
+	if (!(stat & m_INT_EDID_READY))
+		return IRQ_NONE;
+
+	/* Clear HDMI EDID interrupt flag */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+
+	complete(&i2c->cmp);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t inno_hdmi_hardirq(int irq, void *dev_id)
+{
+	struct inno_hdmi *hdmi = dev_id;
+	irqreturn_t ret = IRQ_NONE;
+	u8 interrupt;
+
+	if (hdmi->i2c)
+		ret = inno_hdmi_i2c_irq(hdmi);
+
+	interrupt = hdmi_readb(hdmi, HDMI_STATUS);
+	if (interrupt & m_INT_HOTPLUG) {
+		hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG);
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	return ret;
+}
+
+static irqreturn_t inno_hdmi_irq(int irq, void *dev_id)
+{
+	struct inno_hdmi *hdmi = dev_id;
+
+	drm_helper_hpd_irq_event(hdmi->connector.dev);
+
+	return IRQ_HANDLED;
+}
+
+static int inno_hdmi_i2c_wait(struct inno_hdmi *hdmi)
+{
+	struct inno_hdmi_i2c *i2c = hdmi->i2c;
+	int stat;
+
+	stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
+	if (!stat) {
+		stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
+		if (!stat)
+			return -EAGAIN;
+	}
+
+	return 0;
+}
+
+static int inno_hdmi_i2c_read(struct inno_hdmi *hdmi, struct i2c_msg *msgs)
+{
+	int length = msgs->len;
+	u8 *buf = msgs->buf;
+	int ret;
+
+	ret = inno_hdmi_i2c_wait(hdmi);
+	if (ret)
+		return ret;
+
+	while (length--)
+		*buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
+
+	return 0;
+}
+
+static int inno_hdmi_i2c_write(struct inno_hdmi *hdmi, struct i2c_msg *msgs)
+{
+	struct inno_hdmi_i2c *i2c = hdmi->i2c;
+
+	/*
+	 * The DDC module only support read EDID message, so
+	 * we assume that each word write to this i2c adapter
+	 * should be the offset of EDID word address.
+	 */
+	if ((msgs->len != 1) ||
+	    ((msgs->addr != DDC_ADDR) && (msgs->addr != DDC_SEGMENT_ADDR)))
+		return -EINVAL;
+
+	reinit_completion(&i2c->cmp);
+
+	if (msgs->addr == DDC_SEGMENT_ADDR)
+		hdmi->i2c->segment_addr = msgs->buf[0];
+	if (msgs->addr == DDC_ADDR)
+		hdmi->i2c->ddc_addr = msgs->buf[0];
+
+	/* Set edid fifo first addr */
+	hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
+
+	/* Set edid word address 0x00/0x80 */
+	hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
+
+	/* Set edid segment pointer */
+	hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr);
+
+	return 0;
+}
+
+static int inno_hdmi_i2c_xfer(struct i2c_adapter *adap,
+			      struct i2c_msg *msgs, int num)
+{
+	struct inno_hdmi *hdmi = i2c_get_adapdata(adap);
+	struct inno_hdmi_i2c *i2c = hdmi->i2c;
+	int i, ret = 0;
+
+	mutex_lock(&i2c->lock);
+
+	/* Clear the EDID interrupt flag and unmute the interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY);
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+
+	for (i = 0; i < num; i++) {
+		dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n",
+			i + 1, num, msgs[i].len, msgs[i].flags);
+
+		if (msgs[i].flags & I2C_M_RD)
+			ret = inno_hdmi_i2c_read(hdmi, &msgs[i]);
+		else
+			ret = inno_hdmi_i2c_write(hdmi, &msgs[i]);
+
+		if (ret < 0)
+			break;
+	}
+
+	if (!ret)
+		ret = num;
+
+	/* Mute HDMI EDID interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
+
+	mutex_unlock(&i2c->lock);
+
+	return ret;
+}
+
+static u32 inno_hdmi_i2c_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm inno_hdmi_algorithm = {
+	.master_xfer	= inno_hdmi_i2c_xfer,
+	.functionality	= inno_hdmi_i2c_func,
+};
+
+static struct i2c_adapter *inno_hdmi_i2c_adapter(struct inno_hdmi *hdmi)
+{
+	struct i2c_adapter *adap;
+	struct inno_hdmi_i2c *i2c;
+	int ret;
+
+	i2c = devm_kzalloc(hdmi->dev, sizeof(*i2c), GFP_KERNEL);
+	if (!i2c)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_init(&i2c->lock);
+	init_completion(&i2c->cmp);
+
+	adap = &i2c->adap;
+	adap->class = I2C_CLASS_DDC;
+	adap->owner = THIS_MODULE;
+	adap->dev.parent = hdmi->dev;
+	adap->dev.of_node = hdmi->dev->of_node;
+	adap->algo = &inno_hdmi_algorithm;
+	strlcpy(adap->name, "Inno HDMI", sizeof(adap->name));
+	i2c_set_adapdata(adap, hdmi);
+
+	ret = i2c_add_adapter(adap);
+	if (ret) {
+		dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name);
+		devm_kfree(hdmi->dev, i2c);
+		return ERR_PTR(ret);
+	}
+
+	hdmi->i2c = i2c;
+
+	dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name);
+
+	return adap;
+}
+
+static int inno_hdmi_bind(struct device *dev, struct device *master,
+				 void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = data;
+	struct inno_hdmi *hdmi;
+	struct resource *iores;
+	int irq;
+	int ret;
+
+	hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
+	if (!hdmi)
+		return -ENOMEM;
+
+	hdmi->dev = dev;
+	hdmi->drm_dev = drm;
+
+	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!iores)
+		return -ENXIO;
+
+	hdmi->regs = devm_ioremap_resource(dev, iores);
+	if (IS_ERR(hdmi->regs))
+		return PTR_ERR(hdmi->regs);
+
+	hdmi->pclk = devm_clk_get(hdmi->dev, "pclk");
+	if (IS_ERR(hdmi->pclk)) {
+		dev_err(hdmi->dev, "Unable to get HDMI pclk clk\n");
+		return PTR_ERR(hdmi->pclk);
+	}
+
+	ret = clk_prepare_enable(hdmi->pclk);
+	if (ret) {
+		dev_err(hdmi->dev, "Cannot enable HDMI pclk clock: %d\n", ret);
+		return ret;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	inno_hdmi_reset(hdmi);
+
+	hdmi->ddc = inno_hdmi_i2c_adapter(hdmi);
+	if (IS_ERR(hdmi->ddc)) {
+		hdmi->ddc = NULL;
+		return PTR_ERR(hdmi->ddc);
+	}
+
+	/*
+	 * When IP controller haven't configured to an accurate video
+	 * timing, then the TMDS clock source would be switched to
+	 * PCLK_HDMI, so we need to init the TMDS rate to PCLK rate,
+	 * and reconfigure the DDC clock.
+	 */
+	hdmi->tmds_rate = clk_get_rate(hdmi->pclk);
+	inno_hdmi_i2c_init(hdmi);
+
+	ret = inno_hdmi_register(drm, hdmi);
+	if (ret)
+		return ret;
+
+	dev_set_drvdata(dev, hdmi);
+
+	/* Unmute hotplug interrupt */
+	hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
+
+	ret = devm_request_threaded_irq(dev, irq, inno_hdmi_hardirq,
+					inno_hdmi_irq, IRQF_SHARED,
+					dev_name(dev), hdmi);
+
+	return ret;
+}
+
+static void inno_hdmi_unbind(struct device *dev, struct device *master,
+			     void *data)
+{
+	struct inno_hdmi *hdmi = dev_get_drvdata(dev);
+
+	hdmi->connector.funcs->destroy(&hdmi->connector);
+	hdmi->encoder.funcs->destroy(&hdmi->encoder);
+
+	clk_disable_unprepare(hdmi->pclk);
+	i2c_put_adapter(hdmi->ddc);
+}
+
+static const struct component_ops inno_hdmi_ops = {
+	.bind	= inno_hdmi_bind,
+	.unbind	= inno_hdmi_unbind,
+};
+
+static int inno_hdmi_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &inno_hdmi_ops);
+}
+
+static int inno_hdmi_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &inno_hdmi_ops);
+
+	return 0;
+}
+
+static const struct of_device_id inno_hdmi_dt_ids[] = {
+	{ .compatible = "rockchip,rk3036-inno-hdmi",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, inno_hdmi_dt_ids);
+
+static struct platform_driver inno_hdmi_driver = {
+	.probe  = inno_hdmi_probe,
+	.remove = inno_hdmi_remove,
+	.driver = {
+		.name = "innohdmi-rockchip",
+		.of_match_table = inno_hdmi_dt_ids,
+	},
+};
+
+module_platform_driver(inno_hdmi_driver);
+
+MODULE_AUTHOR("Zheng Yang <zhengyang at rock-chips.com>");
+MODULE_AUTHOR("Yakir Yang <ykk at rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip Specific INNO-HDMI Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:innohdmi-rockchip");
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.h b/drivers/gpu/drm/rockchip/inno_hdmi.h
new file mode 100644
index 0000000..4ff17ad
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.h
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ *    Zheng Yang <zhengyang at rock-chips.com>
+ *    Yakir Yang <ykk at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __INNO_HDMI_H__
+#define __INNO_HDMI_H__
+
+#define DDC_SEGMENT_ADDR		0x30
+
+enum PWR_MODE {
+	NORMAL,
+	LOWER_PWR,
+};
+
+#define HDMI_SCL_RATE			(100*1000)
+#define DDC_BUS_FREQ_L			0x4b
+#define DDC_BUS_FREQ_H			0x4c
+
+#define HDMI_SYS_CTRL			0x00
+#define m_RST_ANALOG			(1 << 6)
+#define v_RST_ANALOG			(0 << 6)
+#define v_NOT_RST_ANALOG		(1 << 6)
+#define m_RST_DIGITAL			(1 << 5)
+#define v_RST_DIGITAL			(0 << 5)
+#define v_NOT_RST_DIGITAL		(1 << 5)
+#define m_REG_CLK_INV			(1 << 4)
+#define v_REG_CLK_NOT_INV		(0 << 4)
+#define v_REG_CLK_INV			(1 << 4)
+#define m_VCLK_INV			(1 << 3)
+#define v_VCLK_NOT_INV			(0 << 3)
+#define v_VCLK_INV			(1 << 3)
+#define m_REG_CLK_SOURCE		(1 << 2)
+#define v_REG_CLK_SOURCE_TMDS		(0 << 2)
+#define v_REG_CLK_SOURCE_SYS		(1 << 2)
+#define m_POWER				(1 << 1)
+#define v_PWR_ON			(0 << 1)
+#define v_PWR_OFF			(1 << 1)
+#define m_INT_POL			(1 << 0)
+#define v_INT_POL_HIGH			1
+#define v_INT_POL_LOW			0
+
+#define HDMI_VIDEO_CONTRL1		0x01
+#define m_VIDEO_INPUT_FORMAT		(7 << 1)
+#define m_DE_SOURCE			(1 << 0)
+#define v_VIDEO_INPUT_FORMAT(n)		(n << 1)
+#define v_DE_EXTERNAL			1
+#define v_DE_INTERNAL			0
+enum {
+	VIDEO_INPUT_SDR_RGB444 = 0,
+	VIDEO_INPUT_DDR_RGB444 = 5,
+	VIDEO_INPUT_DDR_YCBCR422 = 6
+};
+
+#define HDMI_VIDEO_CONTRL2		0x02
+#define m_VIDEO_OUTPUT_COLOR		(3 << 6)
+#define m_VIDEO_INPUT_BITS		(3 << 4)
+#define m_VIDEO_INPUT_CSP		(1 << 0)
+#define v_VIDEO_OUTPUT_COLOR(n)		(((n) & 0x3) << 6)
+#define v_VIDEO_INPUT_BITS(n)		(n << 4)
+#define v_VIDEO_INPUT_CSP(n)		(n << 0)
+enum {
+	VIDEO_INPUT_12BITS = 0,
+	VIDEO_INPUT_10BITS = 1,
+	VIDEO_INPUT_REVERT = 2,
+	VIDEO_INPUT_8BITS = 3,
+};
+
+#define HDMI_VIDEO_CONTRL		0x03
+#define m_VIDEO_AUTO_CSC		(1 << 7)
+#define v_VIDEO_AUTO_CSC(n)		(n << 7)
+#define m_VIDEO_C0_C2_SWAP		(1 << 0)
+#define v_VIDEO_C0_C2_SWAP(n)		(n << 0)
+enum {
+	C0_C2_CHANGE_ENABLE = 0,
+	C0_C2_CHANGE_DISABLE = 1,
+	AUTO_CSC_DISABLE = 0,
+	AUTO_CSC_ENABLE = 1,
+};
+
+#define HDMI_VIDEO_CONTRL3		0x04
+#define m_COLOR_DEPTH_NOT_INDICATED	(1 << 4)
+#define m_SOF				(1 << 3)
+#define m_COLOR_RANGE			(1 << 2)
+#define m_CSC				(1 << 0)
+#define v_COLOR_DEPTH_NOT_INDICATED(n)	((n) << 4)
+#define v_SOF_ENABLE			(0 << 3)
+#define v_SOF_DISABLE			(1 << 3)
+#define v_COLOR_RANGE_FULL		(1 << 2)
+#define v_COLOR_RANGE_LIMITED		(0 << 2)
+#define v_CSC_ENABLE			1
+#define v_CSC_DISABLE			0
+
+#define HDMI_AV_MUTE			0x05
+#define m_AVMUTE_CLEAR			(1 << 7)
+#define m_AVMUTE_ENABLE			(1 << 6)
+#define m_AUDIO_MUTE			(1 << 1)
+#define m_VIDEO_BLACK			(1 << 0)
+#define v_AVMUTE_CLEAR(n)		(n << 7)
+#define v_AVMUTE_ENABLE(n)		(n << 6)
+#define v_AUDIO_MUTE(n)			(n << 1)
+#define v_VIDEO_MUTE(n)			(n << 0)
+
+#define HDMI_VIDEO_TIMING_CTL		0x08
+#define v_HSYNC_POLARITY(n)		(n << 3)
+#define v_VSYNC_POLARITY(n)		(n << 2)
+#define v_INETLACE(n)			(n << 1)
+#define v_EXTERANL_VIDEO(n)		(n << 0)
+
+#define HDMI_VIDEO_EXT_HTOTAL_L		0x09
+#define HDMI_VIDEO_EXT_HTOTAL_H		0x0a
+#define HDMI_VIDEO_EXT_HBLANK_L		0x0b
+#define HDMI_VIDEO_EXT_HBLANK_H		0x0c
+#define HDMI_VIDEO_EXT_HDELAY_L		0x0d
+#define HDMI_VIDEO_EXT_HDELAY_H		0x0e
+#define HDMI_VIDEO_EXT_HDURATION_L	0x0f
+#define HDMI_VIDEO_EXT_HDURATION_H	0x10
+#define HDMI_VIDEO_EXT_VTOTAL_L		0x11
+#define HDMI_VIDEO_EXT_VTOTAL_H		0x12
+#define HDMI_VIDEO_EXT_VBLANK		0x13
+#define HDMI_VIDEO_EXT_VDELAY		0x14
+#define HDMI_VIDEO_EXT_VDURATION	0x15
+
+#define HDMI_VIDEO_CSC_COEF		0x18
+
+#define HDMI_AUDIO_CTRL1		0x35
+enum {
+	CTS_SOURCE_INTERNAL = 0,
+	CTS_SOURCE_EXTERNAL = 1,
+};
+#define v_CTS_SOURCE(n)			(n << 7)
+
+enum {
+	DOWNSAMPLE_DISABLE = 0,
+	DOWNSAMPLE_1_2 = 1,
+	DOWNSAMPLE_1_4 = 2,
+};
+#define v_DOWN_SAMPLE(n)		(n << 5)
+
+enum {
+	AUDIO_SOURCE_IIS = 0,
+	AUDIO_SOURCE_SPDIF = 1,
+};
+#define v_AUDIO_SOURCE(n)		(n << 3)
+
+#define v_MCLK_ENABLE(n)		(n << 2)
+enum {
+	MCLK_128FS = 0,
+	MCLK_256FS = 1,
+	MCLK_384FS = 2,
+	MCLK_512FS = 3,
+};
+#define v_MCLK_RATIO(n)			(n)
+
+#define AUDIO_SAMPLE_RATE		0x37
+enum {
+	AUDIO_32K = 0x3,
+	AUDIO_441K = 0x0,
+	AUDIO_48K = 0x2,
+	AUDIO_882K = 0x8,
+	AUDIO_96K = 0xa,
+	AUDIO_1764K = 0xc,
+	AUDIO_192K = 0xe,
+};
+
+#define AUDIO_I2S_MODE			0x38
+enum {
+	I2S_CHANNEL_1_2 = 1,
+	I2S_CHANNEL_3_4 = 3,
+	I2S_CHANNEL_5_6 = 7,
+	I2S_CHANNEL_7_8 = 0xf
+};
+#define v_I2S_CHANNEL(n)		((n) << 2)
+enum {
+	I2S_STANDARD = 0,
+	I2S_LEFT_JUSTIFIED = 1,
+	I2S_RIGHT_JUSTIFIED = 2,
+};
+#define v_I2S_MODE(n)			(n)
+
+#define AUDIO_I2S_MAP			0x39
+#define AUDIO_I2S_SWAPS_SPDIF		0x3a
+#define v_SPIDF_FREQ(n)			(n)
+
+#define N_32K				0x1000
+#define N_441K				0x1880
+#define N_882K				0x3100
+#define N_1764K				0x6200
+#define N_48K				0x1800
+#define N_96K				0x3000
+#define N_192K				0x6000
+
+#define HDMI_AUDIO_CHANNEL_STATUS	0x3e
+#define m_AUDIO_STATUS_NLPCM		(1 << 7)
+#define m_AUDIO_STATUS_USE		(1 << 6)
+#define m_AUDIO_STATUS_COPYRIGHT	(1 << 5)
+#define m_AUDIO_STATUS_ADDITION		(3 << 2)
+#define m_AUDIO_STATUS_CLK_ACCURACY	(2 << 0)
+#define v_AUDIO_STATUS_NLPCM(n)		((n & 1) << 7)
+#define AUDIO_N_H			0x3f
+#define AUDIO_N_M			0x40
+#define AUDIO_N_L			0x41
+
+#define HDMI_AUDIO_CTS_H		0x45
+#define HDMI_AUDIO_CTS_M		0x46
+#define HDMI_AUDIO_CTS_L		0x47
+
+#define HDMI_DDC_CLK_L			0x4b
+#define HDMI_DDC_CLK_H			0x4c
+
+#define HDMI_EDID_SEGMENT_POINTER	0x4d
+#define HDMI_EDID_WORD_ADDR		0x4e
+#define HDMI_EDID_FIFO_OFFSET		0x4f
+#define HDMI_EDID_FIFO_ADDR		0x50
+
+#define HDMI_PACKET_SEND_MANUAL		0x9c
+#define HDMI_PACKET_SEND_AUTO		0x9d
+#define m_PACKET_GCP_EN			(1 << 7)
+#define m_PACKET_MSI_EN			(1 << 6)
+#define m_PACKET_SDI_EN			(1 << 5)
+#define m_PACKET_VSI_EN			(1 << 4)
+#define v_PACKET_GCP_EN(n)		((n & 1) << 7)
+#define v_PACKET_MSI_EN(n)		((n & 1) << 6)
+#define v_PACKET_SDI_EN(n)		((n & 1) << 5)
+#define v_PACKET_VSI_EN(n)		((n & 1) << 4)
+
+#define HDMI_CONTROL_PACKET_BUF_INDEX	0x9f
+enum {
+	INFOFRAME_VSI = 0x05,
+	INFOFRAME_AVI = 0x06,
+	INFOFRAME_AAI = 0x08,
+};
+
+#define HDMI_CONTROL_PACKET_ADDR	0xa0
+#define HDMI_SIZE_VSI_INFOFRAME		0x0A
+#define HDMI_SIZE_AVI_INFOFRAME		0x11
+#define HDMI_SIZE_AUDIO_INFOFRAME	0x0F
+enum {
+	AVI_COLOR_MODE_RGB = 0,
+	AVI_COLOR_MODE_YCBCR422 = 1,
+	AVI_COLOR_MODE_YCBCR444 = 2,
+	AVI_COLORIMETRY_NO_DATA = 0,
+
+	AVI_COLORIMETRY_SMPTE_170M = 1,
+	AVI_COLORIMETRY_ITU709 = 2,
+	AVI_COLORIMETRY_EXTENDED = 3,
+
+	AVI_CODED_FRAME_ASPECT_NO_DATA = 0,
+	AVI_CODED_FRAME_ASPECT_4_3 = 1,
+	AVI_CODED_FRAME_ASPECT_16_9 = 2,
+
+	ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME = 0x08,
+	ACTIVE_ASPECT_RATE_4_3 = 0x09,
+	ACTIVE_ASPECT_RATE_16_9 = 0x0A,
+	ACTIVE_ASPECT_RATE_14_9 = 0x0B,
+};
+
+#define HDMI_HDCP_CTRL			0x52
+#define m_HDMI_DVI			(1 << 1)
+#define v_HDMI_DVI(n)			(n << 1)
+
+#define HDMI_INTERRUPT_MASK1		0xc0
+#define HDMI_INTERRUPT_STATUS1		0xc1
+#define	m_INT_ACTIVE_VSYNC		(1 << 5)
+#define m_INT_EDID_READY		(1 << 2)
+
+#define HDMI_INTERRUPT_MASK2		0xc2
+#define HDMI_INTERRUPT_STATUS2		0xc3
+#define m_INT_HDCP_ERR			(1 << 7)
+#define m_INT_BKSV_FLAG			(1 << 6)
+#define m_INT_HDCP_OK			(1 << 4)
+
+#define HDMI_STATUS			0xc8
+#define m_HOTPLUG			(1 << 7)
+#define m_MASK_INT_HOTPLUG		(1 << 5)
+#define m_INT_HOTPLUG			(1 << 1)
+#define v_MASK_INT_HOTPLUG(n)		((n & 0x1) << 5)
+
+#define HDMI_COLORBAR                   0xc9
+
+#define HDMI_PHY_SYNC			0xce
+#define HDMI_PHY_SYS_CTL		0xe0
+#define m_TMDS_CLK_SOURCE		(1 << 5)
+#define v_TMDS_FROM_PLL			(0 << 5)
+#define v_TMDS_FROM_GEN			(1 << 5)
+#define m_PHASE_CLK			(1 << 4)
+#define v_DEFAULT_PHASE			(0 << 4)
+#define v_SYNC_PHASE			(1 << 4)
+#define m_TMDS_CURRENT_PWR		(1 << 3)
+#define v_TURN_ON_CURRENT		(0 << 3)
+#define v_CAT_OFF_CURRENT		(1 << 3)
+#define m_BANDGAP_PWR			(1 << 2)
+#define v_BANDGAP_PWR_UP		(0 << 2)
+#define v_BANDGAP_PWR_DOWN		(1 << 2)
+#define m_PLL_PWR			(1 << 1)
+#define v_PLL_PWR_UP			(0 << 1)
+#define v_PLL_PWR_DOWN			(1 << 1)
+#define m_TMDS_CHG_PWR			(1 << 0)
+#define v_TMDS_CHG_PWR_UP		(0 << 0)
+#define v_TMDS_CHG_PWR_DOWN		(1 << 0)
+
+#define HDMI_PHY_CHG_PWR		0xe1
+#define v_CLK_CHG_PWR(n)		((n & 1) << 3)
+#define v_DATA_CHG_PWR(n)		((n & 7) << 0)
+
+#define HDMI_PHY_DRIVER			0xe2
+#define v_CLK_MAIN_DRIVER(n)		(n << 4)
+#define v_DATA_MAIN_DRIVER(n)		(n << 0)
+
+#define HDMI_PHY_PRE_EMPHASIS		0xe3
+#define v_PRE_EMPHASIS(n)		((n & 7) << 4)
+#define v_CLK_PRE_DRIVER(n)		((n & 3) << 2)
+#define v_DATA_PRE_DRIVER(n)		((n & 3) << 0)
+
+#define HDMI_PHY_FEEDBACK_DIV_RATIO_LOW		0xe7
+#define v_FEEDBACK_DIV_LOW(n)			(n & 0xff)
+#define HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH	0xe8
+#define v_FEEDBACK_DIV_HIGH(n)			(n & 1)
+
+#define HDMI_PHY_PRE_DIV_RATIO		0xed
+#define v_PRE_DIV_RATIO(n)		(n & 0x1f)
+
+#define HDMI_CEC_CTRL			0xd0
+#define m_ADJUST_FOR_HISENSE		(1 << 6)
+#define m_REJECT_RX_BROADCAST		(1 << 5)
+#define m_BUSFREETIME_ENABLE		(1 << 2)
+#define m_REJECT_RX			(1 << 1)
+#define m_START_TX			(1 << 0)
+
+#define HDMI_CEC_DATA			0xd1
+#define HDMI_CEC_TX_OFFSET		0xd2
+#define HDMI_CEC_RX_OFFSET		0xd3
+#define HDMI_CEC_CLK_H			0xd4
+#define HDMI_CEC_CLK_L			0xd5
+#define HDMI_CEC_TX_LENGTH		0xd6
+#define HDMI_CEC_RX_LENGTH		0xd7
+#define HDMI_CEC_TX_INT_MASK		0xd8
+#define m_TX_DONE			(1 << 3)
+#define m_TX_NOACK			(1 << 2)
+#define m_TX_BROADCAST_REJ		(1 << 1)
+#define m_TX_BUSNOTFREE			(1 << 0)
+
+#define HDMI_CEC_RX_INT_MASK		0xd9
+#define m_RX_LA_ERR			(1 << 4)
+#define m_RX_GLITCH			(1 << 3)
+#define m_RX_DONE			(1 << 0)
+
+#define HDMI_CEC_TX_INT			0xda
+#define HDMI_CEC_RX_INT			0xdb
+#define HDMI_CEC_BUSFREETIME_L		0xdc
+#define HDMI_CEC_BUSFREETIME_H		0xdd
+#define HDMI_CEC_LOGICADDR		0xde
+
+#endif /* __INNO_HDMI_H__ */
-- 
1.9.1




From ykk at rock-chips.com  Fri Jan 15 01:38:19 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 15 Jan 2016 17:38:19 +0800
Subject: [PATCH v3 2/2] dt-bindings: add document for Innosilicon HDMI on
 Rockchip platform
In-Reply-To: <1452850598-30859-1-git-send-email-ykk@rock-chips.com>
References: <1452850598-30859-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1452850699-31811-1-git-send-email-ykk@rock-chips.com>

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Acked-by: Rob Herring <robh at kernel.org>
---
Changes in v3: None
Changes in v2:
- Correct the misspell "rk3036-dw-hdmi" (Heiko)

 .../display/rockchip/inno_hdmi-rockchip.txt        | 50 ++++++++++++++++++++++
 1 file changed, 50 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt

diff --git a/Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt
new file mode 100644
index 0000000..8096a29
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt
@@ -0,0 +1,50 @@
+Rockchip specific extensions to the Innosilicon HDMI
+================================
+
+Required properties:
+- compatible:
+	"rockchip,rk3036-inno-hdmi";
+- reg:
+	Physical base address and length of the controller's registers.
+- clocks, clock-names:
+	Phandle to hdmi controller clock, name should be "pclk"
+- interrupts:
+	HDMI interrupt number
+- ports:
+	Contain one port node with endpoint definitions as defined in
+	Documentation/devicetree/bindings/graph.txt.
+- pinctrl-0, pinctrl-name:
+	Switch the iomux of HPD/CEC pins to HDMI function.
+
+Example:
+hdmi: hdmi at 20034000 {
+	compatible = "rockchip,rk3036-inno-hdmi";
+	reg = <0x20034000 0x4000>;
+	interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
+	clocks = <&cru  PCLK_HDMI>;
+	clock-names = "pclk";
+	pinctrl-names = "default";
+	pinctrl-0 = <&hdmi_ctl>;
+	status = "disabled";
+
+	hdmi_in: port {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		hdmi_in_lcdc: endpoint at 0 {
+			reg = <0>;
+			remote-endpoint = <&lcdc_out_hdmi>;
+		};
+	};
+};
+
+&pinctrl {
+	hdmi {
+		hdmi_ctl: hdmi-ctl {
+			rockchip,pins = <1 8  RK_FUNC_1 &pcfg_pull_none>,
+					<1 9  RK_FUNC_1 &pcfg_pull_none>,
+					<1 10 RK_FUNC_1 &pcfg_pull_none>,
+					<1 11 RK_FUNC_1 &pcfg_pull_none>;
+		};
+	};
+
+};
-- 
1.9.1




From wdc at rock-chips.com  Fri Jan 15 01:39:13 2016
From: wdc at rock-chips.com (David.Wu)
Date: Fri, 15 Jan 2016 17:39:13 +0800
Subject: [PATCH v3 3/4] i2c: rk3x: new method to calculate i2c clocks
In-Reply-To: <CAD=FV=UXMzEQiKcMa7VjDx+arcbRo1e-0HPHBnG8GP7k6uFt1g@mail.gmail.com>
References: <1452774699-57455-1-git-send-email-david.wu@rock-chips.com>
 <1452774699-57455-4-git-send-email-david.wu@rock-chips.com>
 <CAD=FV=UXMzEQiKcMa7VjDx+arcbRo1e-0HPHBnG8GP7k6uFt1g@mail.gmail.com>
Message-ID: <5698BE41.30005@rock-chips.com>

Hi Doug?

? 2016/1/15 0:12, Doug Anderson ??:
> Hi,
>
> On Thu, Jan 14, 2016 at 4:31 AM, David Wu <david.wu at rock-chips.com> wrote:
>> There was an timing issue about "repeated start" time at the I2C
>> controller of version0, controller appears to drop SDA at .875x (7/8)
>> programmed clk high. The rule(.875x) isn't enough to meet tSU;STA
>> requirements on 100k's Standard-mode. To resolve this issue,
>> data_upd_st, start_setup_cnt and stop_setup_cnt configs for I2C
>> timing information are added, new rules are designed to calculate
>> the timing information at new v1.
> I'm curious: does new hardware behave differently and that's why you
> need v1?
Yes , i didn't describe clearly about difference between old and new.
Old and new hardware behave the same except some timing rules.

v0 is the same with the v1 for tHigh and tLow :
  tHigh = 8 * divh * pclk_cycle;
  tLow = 8 * divl * pclk_cycle;

v0 rules' difference:
     start setup? 7/8 * tHigh + 1 pclk cycle
     start hold :    2 * ?7/8 * tHigh? - 1 pclk cycle
     stop setup :   7/8 * tHigh + 1 pclk cycle

     data setup:    1/2 tLow - 1 pclk cycle
     data hold :    1/2  tLow + 1  pclk cycle

For 100k's example:
     spec_min_low_ns = 4700;
     spec_min_high_ns = 4000;
     spec_min_setup_start_ns = 4700;

We could calculate the timing info by the rules of v0, and ignore effect 
of the pclk cycle here.
     tSU;sta >=4700;
     tHigh_min = tSU;sta * 8/7 >= 5372ns;
     tHigh_min  = max(tHigh_min, spec_min_high_ns) = 5372ns;

We get the final scl clk rate is 99k(1000000000 / (5372ns + 4700ns)),  
it looks ok for the 100k
Standard mode. But the timing point of repeat start and low time is very 
critical, it is dangerous
to some slave devices which are perhaps not so specified.
So need to give some time margin for them, that would increase the cycle 
time and reduce
the clk rate, the final rate we get may be 97k or 96k.

In fact, we must think about scl rise time and scl fall time, and get 
final clk may be 93k or less.
After that, we get about 7 percent lost at clk rate, it would reduce the 
efficiency of i2c transfer.

In other words, we could say there is a timing issue about "repeat 
start" time when we want
accurate 100k's rate, it is short to meet I2C spec requirements.
3.4M clk rate also has the same issue.

The start_setup count is added to fix this issue, tHigh is not need to 
considered of "repeat start" time.
After divs calculated, the count would be calculated to meet i2c spec.
v1 rule:
start setup? tHigh + 1 pclk cycle
start hold:  [8h * (u + 1) - 1] * T;
  tSU;sto = (8h * p + 1) * T;
---------------------------------------------------------------------------------------------------------------

The data_upd_st is added for the Data set-up time and Data hold time on 
Highspeed mode.
For 1.7M's example on v0:
     tHD;DAT = 1/2 tLow?
     tHD;DAT <= spec_max_data_hold_ns = 150ns
     tLow <= 2 * tHD;DAT <= 300ns;
     tLow >= spec_min_low_ns = 320ns;
According to these, we could not get a value for tLow, need changes for 
the rule: tHD;DAT = 1/2 tLow.

Cut the tLow into eight euqal parts?the range of data_upd_st is 1 ~ 7.
It seems that v1 rule could resolve the issue.
v1 rule:
     tHD;sda = n/8 * tLow;
     tSU;sda = [(8 - n)/8 * tLow;

3.4M clk rate also has the same issue.

>   ...or does old and new hardware behave the same and you're
> just introducing v1 so you don't mess with how old boards are working?
The registers of counts added would not effect old boards.
No matter what values of count was written in regs, that old i2c 
controller didn't care,
it worked by original timing rule.

After picking this patch, pmic-rk818 and touchscreen-gt911 worked well 
on the rk3368 sdk
board which use old method.

>
> >From the description it sounds as if the old code had problems as 100k too...
>
> If the new controller is different, I'd probably reword like the
> following (you'd have to re-wordwrap):
>
> There was an timing issue about "repeated start" time at the I2C
> controller of version0, controller appears to drop SDA at .875x (7/8)
> programmed clk high. On version 1 of the controller, the rule(.875x)
> isn't enough to meet tSU;STA
> requirements on 100k's Standard-mode. To resolve this issue,
> data_upd_st, start_setup_cnt and stop_setup_cnt configs for I2C
> timing information are added, new rules are designed to calculate
> the timing information at new v1.

There was an timing issue about "repeated start" time at the I2C
controller of version0, controller appears to drop SDA at .875x (7/8)
programmed clk high. On version 1 of the controller, the rule(.875x)
isn't enough to meet tSU;STA requirements on 100k's Standard-mode.

To resolve this issue,data_upd_st, start_setup_cnt and stop_setup_cnt
configs for I2C timing information are added, new rules are designed
to calculate the timing information at new v1.

> -Doug
>
>
>




From heiko at sntech.de  Fri Jan 15 01:58:51 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Fri, 15 Jan 2016 10:58:51 +0100
Subject: [PATCH 1/2] ARM: dts: rockchip: increase vdd_arm voltage for
 rk3066a-rayeager board
In-Reply-To: <5698BD27.3050700@rock-chips.com>
References: <1452774780-16493-1-git-send-email-andy.yan@rock-chips.com>
 <24626895.xt4u2Kuv7p@phil> <5698BD27.3050700@rock-chips.com>
Message-ID: <4106425.0qUJ88UPmV@phil>

Am Freitag, 15. Januar 2016, 17:34:31 schrieb Andy Yan:
> Hi Heiko:
> 
> On 2016?01?15? 17:22, Heiko Stuebner wrote:
> > Hi Andy, Eddie,
> > 
> > Am Freitag, 15. Januar 2016, 09:11:16 schrieb Andy Yan:
> >> On 2016?01?14? 21:50, Eddie Cai wrote:
> >>> Hi Heiko
> >>> 
> >>> I don't want open source version kernel support too high frequency.
> >>> Many people use RK3066/PX2 on their board. We don't know if their
> >>> layout is good enough to support higher frequency. ie. bad layout will
> >>> cause more voltage drop on the VDD_CPU power rail. So it may need
> >>> higher voltage. I think stable is more important here. People want
> >>> higher frequency can add more opp depend on their board's layout.
> >>> 
> >>> 2016-01-14 21:20 GMT+08:00 Andy Yan <andy.yan at rock-chips.com>:
> >>>> Hi Heiko:
> >>>> 
> >>>> On 2016?01?14? 20:45, Heiko Stuebner wrote:
> >>>>> Hi Andy,
> >>>>> 
> >>>>> Am Donnerstag, 14. Januar 2016, 20:34:20 schrieb Andy Yan:
> >>>>>> The current vdd_arm voltage is too low, increase it will make
> >>>>>> the system more stable.
> >>>>>> 
> >>>>>> Signed-off-by: Andy Yan <andy.yan at rock-chips.com>
> >>>>>> ---
> >>>>>> 
> >>>>>>     arch/arm/boot/dts/rk3066a-rayeager.dts | 8 ++++++++
> >>>>>>     1 file changed, 8 insertions(+)
> >>>>>> 
> >>>>>> diff --git a/arch/arm/boot/dts/rk3066a-rayeager.dts
> >>>>>> b/arch/arm/boot/dts/rk3066a-rayeager.dts index 0bf6876..3d4c104
> >>>>>> 100644
> >>>>>> --- a/arch/arm/boot/dts/rk3066a-rayeager.dts
> >>>>>> +++ b/arch/arm/boot/dts/rk3066a-rayeager.dts
> >>>>>> @@ -159,6 +159,14 @@
> >>>>>> 
> >>>>>>     &cpu0 {
> >>>>>>     
> >>>>>>           cpu0-supply = <&vdd_arm>;
> >>>>>> 
> >>>>>> +       operating-points = <
> >>>>>> +               /* kHz    uV */
> >>>>>> +               1008000 1125000
> >>>>>> +               816000  1125000
> >>>>>> +               600000  1100000
> >>>>>> +               504000  1100000
> >>>>>> +               312000  1075000
> >>>>>> +       >;
> >>>>>> 
> >>>>>>     };
> >>>>>>     
> >>>>>>     &emac {
> >>>>> 
> >>>>> Does this only apply to the rayeager-board / px2 soc or for all
> >>>>> rk3066a/px2
> >>>>> boards? I.e. the original values come from some sdk kernel and I
> >>>>> might've
> >>>>> transcribed that wrong at the time and I remember having problems
> >>>>> trying
> >>>>> to
> >>>>> reach higher frequencies as well.
> >>>>> 
> >>>>> So could you check what the full operating points looks like for
> >>>>> both
> >>>>> and
> >>>>> if
> >>>>> this can move to the rk3066a.dtsi, as both should be able to reach
> >>>>> at
> >>>>> least
> >>>>> 1.4GHz / 1.6GHz.
> >>>>> 
> >>>>> 
> >>>>> Thanks
> >>>>> Heiko
> >>>>> 
> >>>>> ______
> >>>>> 
> >>>>       I have checked the sdk kernel, the opp for the sdk board is
> >>>>       like
> > 
> > bellow:
> >>>>    /**
> >>>>    * dvfs_cpu_logic_table: table for arm and logic dvfs
> >>>>    * @frequency   : arm frequency
> >>>>    * @cpu_volt    : arm voltage depend on frequency
> >>>>    * @logic_volt  : logic voltage arm requests depend on frequency
> >>>>    * comments     : min arm/logic voltage
> >>>>    */
> >>>> 
> >>>> static struct dvfs_arm_table dvfs_cpu_logic_table[] = {
> >>>> 
> >>>>           {.frequency = 252 * 1000,       .cpu_volt = 1075 * 1000,
> >>>> 
> >>>> .logic_volt = 1125 * 1000},//0.975V/1.000V
> >>>> 
> >>>>           {.frequency = 504 * 1000,       .cpu_volt = 1100 * 1000,
> >>>> 
> >>>> .logic_volt = 1125 * 1000},//0.975V/1.000V
> >>>> 
> >>>>           {.frequency = 816 * 1000,       .cpu_volt = 1125 * 1000,
> >>>> 
> >>>> .logic_volt = 1150 * 1000},//1.000V/1.025V
> >>>> 
> >>>>           {.frequency = 1008 * 1000,      .cpu_volt = 1125 * 1000,
> >>>> 
> >>>> .logic_volt = 1150 * 1000},//1.025V/1.050V
> >>>> 
> >>>>           {.frequency = 1200 * 1000,      .cpu_volt = 1175 * 1000,
> >>>> 
> >>>> .logic_volt = 1200 * 1000},//1.100V/1.050V
> >>>> 
> >>>>           {.frequency = 1272 * 1000,      .cpu_volt = 1225 * 1000,
> >>>> 
> >>>> .logic_volt = 1200 * 1000},//1.150V/1.100V
> >>>> 
> >>>>           {.frequency = 1416 * 1000,      .cpu_volt = 1300 * 1000,
> >>>> 
> >>>> .logic_volt = 1200 * 1000},//1.225V/1.100V
> >>>> 
> >>>>           {.frequency = 1512 * 1000,      .cpu_volt = 1350 * 1000,
> >>>> 
> >>>> .logic_volt = 1250 * 1000},//1.300V/1.150V
> >>>> 
> >>>>           {.frequency = 1608 * 1000,      .cpu_volt = 1425 * 1000,
> >>>> 
> >>>> .logic_volt = 1300 * 1000},//1.325V/1.175V
> >>>> 
> >>>>           {.frequency = CPUFREQ_TABLE_END},
> >>>> 
> >>>> };
> >>>> 
> >>>>    This table is made base on mass stress test. So is a safe opp
> >>>>    table
> >>>>    for
> >>>> 
> >>>> most rk3066a base boards. But it some
> >>>> 
> >>>>    board is not so well in power supply circuit design, the may also
> >>>>    need to
> >>>> 
> >>>> adjust it.
> >>>> 
> >>>>    From the table we see that the logic_voltage need to increase  as
> >>>>    the
> >>>> 
> >>>> cpufreq increase. But it seems that the current mainline kernel does
> >>>> not
> >>>> support adjust the logic voltage when cpufreq change. So we can only
> >>>> scale the cpufreq to 1.4GHZ by fixed the logic_volt at 1.2V.
> >>>> 
> >>>>    If you like, I can move it to rk3066a.dtsi with the max freq at
> >>>>    1008MHZ, as>>
> >>>> 
> >>>> I haven't  test to 1.4GHZ  with the mainline kernel. Or give me some
> >>>> time to test to see if the rayeager board can run to 1.4G stable with
> >>>> the mainline .
> >>>> 
> >>     I have run Rayeager  board with cpufreq freely change from
> >> 
> >> 312MHZ~1416MHZ for 11 hours now, it's stable. Maybe we can set the
> >> default opp table for rk3066a/PX2 from 312MHZ~1416HZ in current
> >> mainline.
> > 
> > Thanks for doing that stress test and making that the default setting
> > would be a nice way to go :-) .
> > 
> > I just checked, the other 2 rk3066a boards we currently support (Hayou
> > Marsboard and BQ Curie2) use the exact same power-layout (tps65210 +
> > pwm-
> > regulator for vdd_log), so should be quite save to do.
> > 
> > 
> > Heiko
> > Heiko
> 
>    Actually, all the rockchip rk3066 base platform use the same power
> supply design( control vdd_logic voltage by pwm).
>    So I can move the opp table with max freq 1416MHZ to rk3066a.dtsi and
> keep the pwm-regulator in board specific dts?

Yep, opp in dtsi and pwm-regulator in board files is the correct split.

I.e. that the pwm3 is hooked up to vdd_log is still a board-specific setup as 
there is a tiny chance that some (or future) board might diverge from that.
For the same reason we're having the nearly same setup for the act8846 in 
most board files and not in the core dtsi.


Heiko


From andy.yan at rock-chips.com  Fri Jan 15 02:41:52 2016
From: andy.yan at rock-chips.com (Andy Yan)
Date: Fri, 15 Jan 2016 18:41:52 +0800
Subject: [PATCH 2/2] ARM: dts: rockchip: enable pwm3 as pwm regulator for
 rk3066a-rayeager board
In-Reply-To: <2237362.fjXobafQhV@phil>
References: <1452774780-16493-1-git-send-email-andy.yan@rock-chips.com>
 <1452774956-16621-1-git-send-email-andy.yan@rock-chips.com>
 <2237362.fjXobafQhV@phil>
Message-ID: <5698CCF0.8040406@rock-chips.com>

Hi Heiko:

On 2016?01?14? 20:41, Heiko Stuebner wrote:
> Hi Andy,
>
> Am Donnerstag, 14. Januar 2016, 20:35:56 schrieb Andy Yan:
>> Rayeager board use pwm3 modulate the vdd_logic voltage, so enable it.
>>
>> Signed-off-by: Andy Yan <andy.yan at rock-chips.com>
>> ---
>>
>>   arch/arm/boot/dts/rk3066a-rayeager.dts | 16 ++++++++++++++++
>>   1 file changed, 16 insertions(+)
>>
>> diff --git a/arch/arm/boot/dts/rk3066a-rayeager.dts
>> b/arch/arm/boot/dts/rk3066a-rayeager.dts index 3d4c104..d17a5b8 100644
>> --- a/arch/arm/boot/dts/rk3066a-rayeager.dts
>> +++ b/arch/arm/boot/dts/rk3066a-rayeager.dts
>> @@ -78,6 +78,18 @@
>>   		};
>>   	};
>>
>> +	pwm_regulator: pwm-regulator {
>> +		compatible = "pwm-regulator";
>> +		pwms = <&pwm3 0 1000>;
>> +		regulator-name = "vdd_logic";
>> +		regulator-min-microvolt = <1200000>;
>> +		regulator-max-microvolt = <1200000>;
> I guess this is to make sure it stays at 1.2V at all times, or is the min-
> microvolt settings supposed to be lower?
>
>
> Thanks
> Heiko
>
>
>
    Yes, I want to fix it at 1.2V. When I read the regulator_register 
function,  I found it call regulator_do_set_voltate only when min=max. 
So I set it like what you see.
    regulator_register--->
                    set_machine_constrains--->
                                    machine_constrains_voltage-->





From ykk at rock-chips.com  Fri Jan 15 03:54:14 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 15 Jan 2016 19:54:14 +0800
Subject: [PATCH] clk: rockchip: rk3036: enable the CLK_IGNORE_UNUSED flag for
 hclk_vio_bus
Message-ID: <1452858854-841-1-git-send-email-ykk@rock-chips.com>

HCLK_VIO_BUS is the noc bus controller clock for display module,
due to it shouldn't belong to any driver, but we need it enabled,
so just mark it as the CLK_IGNORE_UNUSED flag.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
 drivers/clk/rockchip/clk-rk3036.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/clk/rockchip/clk-rk3036.c b/drivers/clk/rockchip/clk-rk3036.c
index 484de46..00fac21 100644
--- a/drivers/clk/rockchip/clk-rk3036.c
+++ b/drivers/clk/rockchip/clk-rk3036.c
@@ -379,7 +379,7 @@ static struct rockchip_clk_branch rk3036_clk_branches[] __initdata = {
 	GATE(ACLK_VIO, "aclk_vio", "aclk_disp1_pre", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(6), 13, GFLAGS),
 	GATE(ACLK_LCDC, "aclk_lcdc", "aclk_disp1_pre", 0, RK2928_CLKGATE_CON(9), 6, GFLAGS),
 
-	GATE(HCLK_VIO_BUS, "hclk_vio_bus", "hclk_disp_pre", 0, RK2928_CLKGATE_CON(6), 12, GFLAGS),
+	GATE(HCLK_VIO_BUS, "hclk_vio_bus", "hclk_disp_pre", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(6), 12, GFLAGS),
 	GATE(HCLK_LCDC, "hclk_lcdc", "hclk_disp_pre", 0, RK2928_CLKGATE_CON(9), 5, GFLAGS),
 
 	/* hclk_video gates */
-- 
1.9.1




From matthias.bgg at gmail.com  Fri Jan 15 05:06:31 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Fri, 15 Jan 2016 14:06:31 +0100
Subject: [PATCH 1/2] devicetree: bindings: Add vendor prefix for Tronsmart
Message-ID: <1452863192-29231-1-git-send-email-matthias.bgg@gmail.com>

Tronsmart is a China based company building consumer electronic
devices.

Signed-off-by: Matthias Brugger <mbrugger at suse.com>
---
 Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 55df1d4..f787742 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -231,6 +231,7 @@ toshiba	Toshiba Corporation
 toumaz	Toumaz
 tplink	TP-LINK Technologies Co., Ltd.
 tronfy	Tronfy
+tronsmart	Tronsmart
 truly	Truly Semiconductors Limited
 upisemi	uPI Semiconductor Corp.
 usi	Universal Scientific Industrial Co., Ltd.
-- 
2.6.2



From matthias.bgg at gmail.com  Fri Jan 15 05:06:32 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Fri, 15 Jan 2016 14:06:32 +0100
Subject: [PATCH 2/2] arm64: dts: rockchip: Add basic support for orion-r68
In-Reply-To: <1452863192-29231-1-git-send-email-matthias.bgg@gmail.com>
References: <1452863192-29231-1-git-send-email-matthias.bgg@gmail.com>
Message-ID: <1452863192-29231-2-git-send-email-matthias.bgg@gmail.com>

This patch adds basic support for the Tronsmart orion r86 set-top-box.

Signed-off-by: Matthias Brugger <mbrugger at suse.com>
---
 Documentation/devicetree/bindings/arm/rockchip.txt |   4 +
 arch/arm64/boot/dts/rockchip/Makefile              |   1 +
 arch/arm64/boot/dts/rockchip/rk3368-orion-r68.dts  | 370 +++++++++++++++++++++
 3 files changed, 375 insertions(+)
 create mode 100644 arch/arm64/boot/dts/rockchip/rk3368-orion-r68.dts

diff --git a/Documentation/devicetree/bindings/arm/rockchip.txt b/Documentation/devicetree/bindings/arm/rockchip.txt
index 8e985dd..fa97d93 100644
--- a/Documentation/devicetree/bindings/arm/rockchip.txt
+++ b/Documentation/devicetree/bindings/arm/rockchip.txt
@@ -72,3 +72,7 @@ Rockchip platforms device tree bindings
 - Rockchip R88 board:
     Required root node properties:
       - compatible = "rockchip,r88", "rockchip,rk3368";
+
+- Tronsmart Orion R68
+    Required root node properties:
+      - compatible = "tronsmart,orion-r68", "rockchip,rk3368";
diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile
index 601e6a2..950fe63 100644
--- a/arch/arm64/boot/dts/rockchip/Makefile
+++ b/arch/arm64/boot/dts/rockchip/Makefile
@@ -1,4 +1,5 @@
 dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-r88.dtb
+dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-orion-r68.dtb
 
 always		:= $(dtb-y)
 subdir-y	:= $(dts-dirs)
diff --git a/arch/arm64/boot/dts/rockchip/rk3368-orion-r68.dts b/arch/arm64/boot/dts/rockchip/rk3368-orion-r68.dts
new file mode 100644
index 0000000..3f71d506
--- /dev/null
+++ b/arch/arm64/boot/dts/rockchip/rk3368-orion-r68.dts
@@ -0,0 +1,370 @@
+/*
+ * Copyright (c) 2015 Matthias Brugger <mbrugger at suse.com>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of the
+ *     License, or (at your option) any later version.
+ *
+ *     This file is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     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.
+ */
+
+/dts-v1/;
+#include "rk3368.dtsi"
+
+/ {
+	model = "Rockchip Orion R68";
+	compatible = "tronsmart,orion-r68", "rockchip,rk3368";
+
+	chosen {
+		stdout-path = "serial2:115200n8";
+	};
+
+	memory {
+		device_type = "memory";
+		reg = <0x0 0x0 0x0 0x40000000>;
+	};
+
+	emmc_pwrseq: emmc-pwrseq {
+		compatible = "mmc-pwrseq-emmc";
+		pinctrl-0 = <&emmc_reset>;
+		pinctrl-names = "default";
+		reset-gpios = <&gpio2 3 GPIO_ACTIVE_HIGH>;
+	};
+
+	ext_gmac: external-gmac-clock {
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+		clock-frequency = <125000000>;
+		clock-output-names = "ext_gmac";
+	};
+
+	keys: gpio-keys {
+		compatible = "gpio-keys";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pwr_key>;
+
+		button at 0 {
+			gpio-key,wakeup = <1>;
+			gpios = <&gpio0 5 GPIO_ACTIVE_HIGH>;
+			label = "GPIO Power";
+			linux,code = <116>;
+		};
+	};
+
+	leds: gpio-leds {
+		compatible = "gpio-leds";
+
+		work {
+			gpios = <&gpio3 29 GPIO_ACTIVE_HIGH>;
+			label = "orion:purple:led";
+			pinctrl-names = "default";
+			pinctrl-0 = <&led_ctl>;
+			default-state = "on";
+		};
+	};
+
+	vcc_18: vcc18-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_18";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+		regulator-always-on;
+		regulator-boot-on;
+		vin-supply = <&vcc_sys>;
+	};
+
+	/* supplies both host and otg */
+	vcc_host: vcc-host-regulator {
+		compatible = "regulator-fixed";
+		gpio = <&gpio0 4 GPIO_ACTIVE_LOW>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&host_vbus_drv>;
+		regulator-name = "vcc_host";
+		regulator-always-on;
+		regulator-boot-on;
+		vin-supply = <&vcc_sys>;
+	};
+
+	vccio_sd: vcc-io-sd-regulator {
+		regulator-name= "vccio_sd";
+		gpio = <&gpio0 9 GPIO_ACTIVE_LOW>;
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+	};
+
+	vcc_sd: vcc-sd-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_sd";
+		gpio = <&gpio3 11 GPIO_ACTIVE_LOW>;
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-always-on;
+		regulator-boot-on;
+		vin-supply = <&vcc_io>;
+	};
+
+	vcc_io: vcc-io-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_io";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-always-on;
+		regulator-boot-on;
+		vin-supply = <&vcc_sys>;
+	};
+
+	vcc_lan: vcc-lan-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_lan";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-always-on;
+		regulator-boot-on;
+		vin-supply = <&vcc_io>;
+	};
+
+	vcc_sys: vcc-sys-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_sys";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		regulator-always-on;
+		regulator-boot-on;
+	};
+
+	vccio_wl: vccio-wl-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vccio_wl";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-always-on;
+		regulator-boot-on;
+		vin-supply = <&vcc_io>;
+	};
+
+	vdd_10: vdd-10-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vdd_10";
+		regulator-min-microvolt = <1000000>;
+		regulator-max-microvolt = <1000000>;
+		regulator-always-on;
+		regulator-boot-on;
+		vin-supply = <&vcc_sys>;
+	};
+};
+
+&emmc {
+	broken-cd;
+	bus-width = <8>;
+	cap-mmc-highspeed;
+	disable-wp;
+	mmc-pwrseq = <&emmc_pwrseq>;
+	non-removable;
+	num-slots = <1>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&emmc_clk &emmc_cmd &emmc_bus8>;
+	status = "okay";
+};
+
+&gmac {
+	assigned-clocks = <&cru SCLK_MAC>;
+	assigned-clock-parents = <&ext_gmac>;
+	phy-supply = <&vcc_lan>;
+	phy-mode = "rgmii";
+	clock_in_out = "input";
+	snps,reset-gpio = <&gpio3 12 0>;
+	snps,reset-active-low;
+	snps,reset-delays-us = <0 10000 1000000>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&rgmii_pins>;
+	tx_delay = <0x30>;
+	rx_delay = <0x10>;
+	status = "ok";
+};
+
+&i2c0 {
+	status = "okay";
+
+	vdd_cpu: syr827 at 40 {
+		compatible = "silergy,syr827";
+		reg = <0x40>;
+		fcs,suspend-voltage-selector = <1>;
+		regulator-name = "vdd_cpu";
+		regulator-enable-ramp-delay = <300>;
+		regulator-min-microvolt = <712500>;
+		regulator-max-microvolt = <1500000>;
+		regulator-ramp-delay = <8000>;
+		regulator-always-on;
+		regulator-boot-on;
+		vin-supply = <&vcc_sys>;
+	};
+
+	hym8563: hym8563 at 51 {
+		compatible = "haoyu,hym8563";
+		reg = <0x51>;
+		#clock-cells = <0>;
+		clock-frequency = <32768>;
+		clock-output-names = "xin32k";
+		/* rtc_int is not connected */
+	};
+};
+
+&pinctrl {
+	pcfg_pull_none_drv_8ma: pcfg-pull-none-drv-8ma {
+		bias-disable;
+		drive-strength = <8>;
+	};
+
+	pcfg_pull_up_drv_8ma: pcfg-pull-up-drv-8ma {
+		bias-pull-up;
+		drive-strength = <8>;
+	};
+
+	emmc {
+		emmc_bus8: emmc-bus8 {
+			rockchip,pins = <1 18 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
+					<1 19 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
+					<1 20 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
+					<1 21 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
+					<1 22 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
+					<1 23 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
+					<1 24 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
+					<1 25 RK_FUNC_2 &pcfg_pull_up_drv_8ma>;
+		};
+
+		emmc-clk {
+			rockchip,pins = <2 4 RK_FUNC_2 &pcfg_pull_none_drv_8ma>;
+		};
+
+		emmc-cmd {
+			rockchip,pins = <1 26 RK_FUNC_2 &pcfg_pull_up_drv_8ma>;
+		};
+
+		emmc_reset: emmc-reset {
+			rockchip,pins = <2 3 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+
+	keys {
+		pwr_key: pwr-key {
+			rockchip,pins = <0 5 RK_FUNC_GPIO &pcfg_pull_down>;
+		};
+	};
+
+	leds {
+		stby_pwren: stby-pwren {
+			rockchip,pins = <0 12 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+
+		led_ctl: led-ctl {
+			rockchip,pins = <3 29 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+
+	sdmmc {
+		sdmmc_clk: sdmmc-clk {
+			rockchip,pins = <2 9 RK_FUNC_1 &pcfg_pull_none_drv_8ma>;
+		};
+
+		sdmmc_cmd: sdmmc-cmd {
+			rockchip,pins = <2 10 RK_FUNC_1 &pcfg_pull_up_drv_8ma>;
+		};
+
+		sdmmc_cd: sdmmc-cd {
+			rockchip,pins = <2 11 RK_FUNC_1 &pcfg_pull_up_drv_8ma>;
+		};
+
+		sdmmc_bus1: sdmmc-bus1 {
+			rockchip,pins = <2 5 RK_FUNC_1 &pcfg_pull_up_drv_8ma>;
+		};
+
+		sdmmc_bus4: sdmmc-bus4 {
+			rockchip,pins = <2 5 RK_FUNC_1 &pcfg_pull_up_drv_8ma>,
+					<2 6 RK_FUNC_1 &pcfg_pull_up_drv_8ma>,
+					<2 7 RK_FUNC_1 &pcfg_pull_up_drv_8ma>,
+					<2 8 RK_FUNC_1 &pcfg_pull_up_drv_8ma>;
+		};
+	};
+
+	usb {
+		host_vbus_drv: host-vbus-drv {
+			rockchip,pins = <0 4 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+};
+
+&saradc {
+	vref-supply = <&vcc_18>;
+	status = "okay";
+};
+
+&sdmmc {
+	clock-frequency = <50000000>;
+	clock-freq-min-max = <400000 50000000>;
+	cap-sd-highspeed;
+	supports-sd;
+	broken-cd;
+	card-detect-delay = <200>;
+
+	pinctrl-names = "default";
+	pinctrl-0 = <&sdmmc_clk &sdmmc_cmd &sdmmc_cd &sdmmc_bus4>;
+	bus-width = <4>;
+	num-slots = <1>;
+	ignore-pm-notify;
+	keep-power-in-suspend;
+
+	vmmc-supply = <&vcc_sd>;
+	vqmmc-supply = <&vccio_sd>;
+	status = "okay";
+};
+
+&uart2 {
+	status = "okay";
+};
+
+&usb_host0_ehci {
+	status = "okay";
+};
+
+&usb_otg {
+	dr_mode = "host";
+	status = "okay";
+};
+
+&wdt {
+	status = "okay";
+};
-- 
2.6.2



From andy.yan at rock-chips.com  Fri Jan 15 05:23:43 2016
From: andy.yan at rock-chips.com (Andy Yan)
Date: Fri, 15 Jan 2016 21:23:43 +0800
Subject: [PATCH v2 0/2] fix rk3066a based boards hang when cpufreq changes
Message-ID: <1452864223-423-1-git-send-email-andy.yan@rock-chips.com>


I found the rayeager board always hang in the system boot
process or when cpufreq change. When check the BSP code[0]
from ChipSpark, I found the current vdd_arm voltage is too
low and so is the vdd_logic voltage, which should be modulated
by PWM3.

So I increase the vdd_arm voltage and enabled pwm3 as a pwm
regulator here.

[0] https://github.com/RockchipOpensourceCommunity/px2-android-kernel-3.0/blob/master/arch/arm/mach-rkpx2/board-rkpx2-sdk.c

Changes in v2:
- move the opp table to rk3066a.dtsi, make it as the default opp table
  for rk3066a based board.
- also enable the pwm regulator for bqcurie2 and Marsboard

Andy Yan (2):
  ARM: dts: rockchip: increase vdd_arm voltage for rk3066a based boards
  ARM: dts: rockchip: enable pwm3 as pwm regulator for rk3066a based
    boards

 arch/arm/boot/dts/rk3066a-bqcurie2.dts  | 16 ++++++++++++++++
 arch/arm/boot/dts/rk3066a-marsboard.dts | 16 ++++++++++++++++
 arch/arm/boot/dts/rk3066a-rayeager.dts  | 16 ++++++++++++++++
 arch/arm/boot/dts/rk3066a.dtsi          | 12 +++++++-----
 4 files changed, 55 insertions(+), 5 deletions(-)

-- 
1.9.1





From andy.yan at rock-chips.com  Fri Jan 15 05:25:21 2016
From: andy.yan at rock-chips.com (Andy Yan)
Date: Fri, 15 Jan 2016 21:25:21 +0800
Subject: [PATCH v2 1/2] ARM: dts: rockchip: increase vdd_arm voltage for
 rk3066a based boards
In-Reply-To: <1452864223-423-1-git-send-email-andy.yan@rock-chips.com>
References: <1452864223-423-1-git-send-email-andy.yan@rock-chips.com>
Message-ID: <1452864321-505-1-git-send-email-andy.yan@rock-chips.com>

The current vdd_arm voltage is too low, increase it will make
the system more stable.

Signed-off-by: Andy Yan <andy.yan at rock-chips.com>

---

Changes in v2:
- move the opp table to rk3066a.dtsi, make it as the default opp table
  for rk3066a based board.

 arch/arm/boot/dts/rk3066a.dtsi | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/arch/arm/boot/dts/rk3066a.dtsi b/arch/arm/boot/dts/rk3066a.dtsi
index 946f187..ff0530e 100644
--- a/arch/arm/boot/dts/rk3066a.dtsi
+++ b/arch/arm/boot/dts/rk3066a.dtsi
@@ -61,11 +61,13 @@
 			reg = <0x0>;
 			operating-points = <
 				/* kHz    uV */
-				1008000 1075000
-				 816000 1025000
-				 600000 1025000
-				 504000 1000000
-				 312000  975000
+				1416000 1300000
+				1200000 1175000
+				1008000 1125000
+				816000  1125000
+				600000  1100000
+				504000  1100000
+				312000  1075000
 			>;
 			clock-latency = <40000>;
 			clocks = <&cru ARMCLK>;
-- 
1.9.1





From andy.yan at rock-chips.com  Fri Jan 15 05:26:18 2016
From: andy.yan at rock-chips.com (Andy Yan)
Date: Fri, 15 Jan 2016 21:26:18 +0800
Subject: [PATCH v2 2/2] ARM: dts: rockchip: enable pwm3 as pwm regulator for
 rk3066a based boards
In-Reply-To: <1452864223-423-1-git-send-email-andy.yan@rock-chips.com>
References: <1452864223-423-1-git-send-email-andy.yan@rock-chips.com>
Message-ID: <1452864378-554-1-git-send-email-andy.yan@rock-chips.com>

Rayeager/Bqcurie2/Marsboard use pwm3 modulate the vdd_logic voltage,
so enable it.

Signed-off-by: Andy Yan <andy.yan at rock-chips.com>

---

Changes in v2:
- also enable the pwm regulator for bqcurie2 and Marsboard

 arch/arm/boot/dts/rk3066a-bqcurie2.dts  | 16 ++++++++++++++++
 arch/arm/boot/dts/rk3066a-marsboard.dts | 16 ++++++++++++++++
 arch/arm/boot/dts/rk3066a-rayeager.dts  | 16 ++++++++++++++++
 3 files changed, 48 insertions(+)

diff --git a/arch/arm/boot/dts/rk3066a-bqcurie2.dts b/arch/arm/boot/dts/rk3066a-bqcurie2.dts
index 38c91a8..90a4c4d 100644
--- a/arch/arm/boot/dts/rk3066a-bqcurie2.dts
+++ b/arch/arm/boot/dts/rk3066a-bqcurie2.dts
@@ -53,6 +53,18 @@
 		reg = <0x60000000 0x40000000>;
 	};
 
+	pwm_regulator: pwm-regulator {
+		compatible = "pwm-regulator";
+		pwms = <&pwm3 0 1000>;
+		regulator-name = "vdd_logic";
+		regulator-min-microvolt = <1200000>;
+		regulator-max-microvolt = <1200000>;
+		regulator-always-on;
+		voltage-table = <1000000 100>,
+				<1200000 42>;
+		status = "okay";
+	};
+
 	vcc_sd0: fixed-regulator {
 		compatible = "regulator-fixed";
 		regulator-name = "sdmmc-supply";
@@ -203,6 +215,10 @@
 	disable-wp;
 };
 
+&pwm3 {
+	status = "okay";
+};
+
 &uart0 {
 	status = "okay";
 };
diff --git a/arch/arm/boot/dts/rk3066a-marsboard.dts b/arch/arm/boot/dts/rk3066a-marsboard.dts
index 7cdc308..4852e32 100644
--- a/arch/arm/boot/dts/rk3066a-marsboard.dts
+++ b/arch/arm/boot/dts/rk3066a-marsboard.dts
@@ -52,6 +52,18 @@
 		reg = <0x60000000 0x40000000>;
 	};
 
+	pwm_regulator: pwm-regulator {
+		compatible = "pwm-regulator";
+		pwms = <&pwm3 0 1000>;
+		regulator-name = "vdd_logic";
+		regulator-min-microvolt = <1200000>;
+		regulator-max-microvolt = <1200000>;
+		regulator-always-on;
+		voltage-table = <1000000 100>,
+				<1200000 42>;
+		status = "okay";
+	};
+
 	vcc_sd0: sdmmc-regulator {
 		compatible = "regulator-fixed";
 		regulator-name = "sdmmc-supply";
@@ -194,6 +206,10 @@
 	};
 };
 
+&pwm3 {
+	status = "okay";
+};
+
 &uart0 {
 	status = "okay";
 };
diff --git a/arch/arm/boot/dts/rk3066a-rayeager.dts b/arch/arm/boot/dts/rk3066a-rayeager.dts
index 0bf6876..7127e3f 100644
--- a/arch/arm/boot/dts/rk3066a-rayeager.dts
+++ b/arch/arm/boot/dts/rk3066a-rayeager.dts
@@ -78,6 +78,18 @@
 		};
 	};
 
+	pwm_regulator: pwm-regulator {
+		compatible = "pwm-regulator";
+		pwms = <&pwm3 0 1000>;
+		regulator-name = "vdd_logic";
+		regulator-min-microvolt = <1200000>;
+		regulator-max-microvolt = <1200000>;
+		regulator-always-on;
+		voltage-table = <1000000 100>,
+				<1200000 42>;
+		status = "okay";
+	};
+
 	vsys: vsys-regulator {
 		compatible = "regulator-fixed";
 		regulator-name = "vsys";
@@ -435,6 +447,10 @@
 	status = "okay";
 };
 
+&pwm3 {
+	status = "okay";
+};
+
 &saradc {
 	vref-supply = <&vcc_25>;
 	status = "okay";
-- 
1.9.1





From javier at dowhile0.org  Fri Jan 15 05:28:44 2016
From: javier at dowhile0.org (Javier Martinez Canillas)
Date: Fri, 15 Jan 2016 10:28:44 -0300
Subject: [PATCH 2/2] arm64: dts: rockchip: Add basic support for orion-r68
In-Reply-To: <1452863192-29231-2-git-send-email-matthias.bgg@gmail.com>
References: <1452863192-29231-1-git-send-email-matthias.bgg@gmail.com>
 <1452863192-29231-2-git-send-email-matthias.bgg@gmail.com>
Message-ID: <CABxcv=m+hK3xS8_fYNns-xE5DaLHD0D_Yj8McjM72Y_5V=YmSw@mail.gmail.com>

Hello Matthias,

This is not a complete review but I just wanted to comment on two
things that I noticed:

On Fri, Jan 15, 2016 at 10:06 AM, Matthias Brugger
<matthias.bgg at gmail.com> wrote:

[snip]

> +       };
> +
> +       vcc_18: vcc18-regulator {
> +               compatible = "regulator-fixed";
> +               regulator-name = "vcc_18";
> +               regulator-min-microvolt = <1800000>;
> +               regulator-max-microvolt = <1800000>;
> +               regulator-always-on;
> +               regulator-boot-on;
> +               vin-supply = <&vcc_sys>;
> +       };
> +
> +       /* supplies both host and otg */
> +       vcc_host: vcc-host-regulator {
> +               compatible = "regulator-fixed";
> +               gpio = <&gpio0 4 GPIO_ACTIVE_LOW>;
> +               pinctrl-names = "default";
> +               pinctrl-0 = <&host_vbus_drv>;
> +               regulator-name = "vcc_host";
> +               regulator-always-on;
> +               regulator-boot-on;
> +               vin-supply = <&vcc_sys>;
> +       };
> +
> +       vccio_sd: vcc-io-sd-regulator {
> +               regulator-name= "vccio_sd";
> +               gpio = <&gpio0 9 GPIO_ACTIVE_LOW>;
> +               regulator-min-microvolt = <3300000>;
> +               regulator-max-microvolt = <3300000>;
> +       };
> +
> +       vcc_sd: vcc-sd-regulator {
> +               compatible = "regulator-fixed";
> +               regulator-name = "vcc_sd";
> +               gpio = <&gpio3 11 GPIO_ACTIVE_LOW>;
> +               regulator-min-microvolt = <3300000>;
> +               regulator-max-microvolt = <3300000>;
> +               regulator-always-on;
> +               regulator-boot-on;
> +               vin-supply = <&vcc_io>;
> +       };
> +
> +       vcc_io: vcc-io-regulator {
> +               compatible = "regulator-fixed";
> +               regulator-name = "vcc_io";
> +               regulator-min-microvolt = <3300000>;
> +               regulator-max-microvolt = <3300000>;
> +               regulator-always-on;
> +               regulator-boot-on;
> +               vin-supply = <&vcc_sys>;
> +       };
> +
> +       vcc_lan: vcc-lan-regulator {
> +               compatible = "regulator-fixed";
> +               regulator-name = "vcc_lan";
> +               regulator-min-microvolt = <3300000>;
> +               regulator-max-microvolt = <3300000>;
> +               regulator-always-on;
> +               regulator-boot-on;
> +               vin-supply = <&vcc_io>;
> +       };
> +
> +       vcc_sys: vcc-sys-regulator {
> +               compatible = "regulator-fixed";
> +               regulator-name = "vcc_sys";
> +               regulator-min-microvolt = <5000000>;
> +               regulator-max-microvolt = <5000000>;
> +               regulator-always-on;
> +               regulator-boot-on;
> +       };
> +
> +       vccio_wl: vccio-wl-regulator {
> +               compatible = "regulator-fixed";
> +               regulator-name = "vccio_wl";
> +               regulator-min-microvolt = <3300000>;
> +               regulator-max-microvolt = <3300000>;
> +               regulator-always-on;
> +               regulator-boot-on;
> +               vin-supply = <&vcc_io>;
> +       };
> +
> +       vdd_10: vdd-10-regulator {
> +               compatible = "regulator-fixed";
> +               regulator-name = "vdd_10";
> +               regulator-min-microvolt = <1000000>;
> +               regulator-max-microvolt = <1000000>;
> +               regulator-always-on;
> +               regulator-boot-on;
> +               vin-supply = <&vcc_sys>;
> +       };
> +};

There is only one regulator that is not marked as always-on. This will
prevent the regulator subsystem to disable unused regulators. Do you
really need all of them to be always-on?

> +
> +&emmc {
> +       broken-cd;
> +       bus-width = <8>;
> +       cap-mmc-highspeed;
> +       disable-wp;
> +       mmc-pwrseq = <&emmc_pwrseq>;
> +       non-removable;

Non-removable assumes always present so there is no need for
broken-cd. In fact, these two properties are mutually exclusive
according to Documentation/devicetree/bindings/mmc/mmc.txt.

Best regards,
Javier


From wxt at rock-chips.com  Fri Jan 15 05:49:47 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Fri, 15 Jan 2016 21:49:47 +0800
Subject: [PATCH v3 0/9] Add the family patches to support for kylin board
Message-ID: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>

Hi all,

This series patches are based on kernel 4.4.0 release version
(Linux version 4.4.0+ (wxt at ubuntu) (...) #167 SMP Fri Jan 15 19:18:28 CST 2016)

This series pacthes are verified on the following github:
https://github.com/Caesar-github/rockchip/commits/kylin-next

This series has 9 patches: (9--->1)
2b1afd6 ARM: dts: rockchip: add pl330-broken-no-flushp quirk for rk3036 SoCs
cc706ee ARM: dts: rockchip: add reboot-mode node for rk3036 SoCs
cdad8f3 ARM: dts: rockchip: add the sdmmc for kylin board
9ece822 ARM: dts: rockchip: add the sdio power sequence for kylin board
28eba08 ARM: dts: rockchip: enable the high speed on sdio for kylin board
858e2cc ARM: dts: rockchip: enable the uart0 for kylin board
59aecc9 ASoC: rockchip: i2s: add support for grabbing output clock to codec
d2b5890 ARM: dts: rockchip: add i2s_clk_out to list of clocks used on rk3036 i2s
98c9c09 ARM: dts: rockchip: add hdmi/vop device node for rk3036
..

This series patches can apply into Heiko v4.6-armsoc/dts32 branch.
https://git.kernel.org/cgit/linux/kernel/git/mmind/linux-rockchip.git/log/?h=v4.6-armsoc/dts32
---

This series patches have the following decriptions:

PATCH[1/9]: ====> add hdmi/vop display node for rk3036
ARM: dts: rockchip: add hdmi/vop device node for rk3036

I fetch some vop and drm/rockchip patches from the Mark Yao branch.
https://github.com/markyzq/kernel-drm-rockchip/commits/drm-rockchip-next-2015-12-28

Add the following patches, verified on my github.
(https://github.com/Caesar-github/rockchip/commits/kylin-next)

....
cf2631b FORMLIST: clk: rockchip: rk3036: enable the CLK_IGNORE_UNUSED flag for hclk_vio_bus
a80ae4e FORMLIST: dt-bindings: add document for Innosilicon HDMI on Rockchip platform
b460291 FORMLIST: drm: rockchip/hdmi: add Innosilicon HDMI support
fe52935 FORMLIST: dt-bindings: add document for rk3036-vop
b556daa FORMLIST: drm/rockchip: vop: add rk3036 vop support
447e2b8 drm/rockchip: vop: spilt scale regsters
4ec4740 drm/rockchip: vop: spilt register related into rockchip_reg_vop.c
da528d7 drm/rockchip: vop: move interrupt registers into vop_data
a207639 drm/rockchip: vop: merge vop cfg_done into vop_data
1656da5 drm/rockchip: dw_hdmi: use encoder enable function
2b5f45b drm/rockchip: direct config connecter gate and out_mode
dcf2718 drm/rockchip: support atomic asynchronous commit
722dd38 drm/rockchip: Optimization vop mode set
f1b407a drm/rockchip: Convert to support atomic API
6ec2936 drm/rockchip: vop: replace dpms with enable/disable
5cfe7d1 drm/rockchip: Use new vblank api drm_crtc_vblank_*
ff1b1a3 UPSTREAM: drm: use dev_name as default unique name in drm_dev_alloc()
2155d11 drm: make drm_dev_set_unique() not use a format string
649f441 drm/rockchip: Constify function pointer structs
5096c80 UPSTREAM: drm: Pass 'name' to drm_encoder_init()
26c4d13 UPSTREAM: drm: Pass 'name' to drm_universal_plane_init()
0b795a0 UPSTREAM: drm: Pass 'name' to drm_crtc_init_with_planes()
...
----

PATCH[2/9-3/9]:====> Add the i2c clock output for rk3036

59aecc9 ASoC: rockchip: i2s: add support for grabbing output clock to codec
d2b5890 ARM: dts: rockchip: add i2s_clk_out to list of clocks used on rk3036 i2s

This patches is working for codec rt5616 on kylin board. (RK3036 SoCs)

That's needed the following rt5616 driver from Mark Brown Branch.
(cherry picked from git.kernel.org broonie/sound.git for-next)

cf94b7a UPSTREAM: ASoC: rt5616: rename some alsa control names
86f561d UPSTREAM: ASoC: rt5616: add devicetree document for rt5616
0e0f5f8 UPSTREAM: ASoC: rt5616: add an of_match table
263a330 UPSTREAM: ASoC: rt5616: Return error if device ID mismatch
0094139 UPSTREAM: ASoC: rt5616: add rt5616 codec driver

Notes:
I'm run it with the ubuntu 15.04 to set the configure.
./amixer "HP Playback Switch" 1
./amixer "HP Playback Volume" 20
./amixer "HPO MIX DAC1 Switch" 1
./amixer "OUT MIXR DAC R1 Switch" 1
./amixer "OUT MIXL DAC L1 Switch" 1
./amixer "Stereo DAC MIXR DAC R1 Switch" 1
./amixer "Stereo DAC MIXL DAC L1 Switch" 1

root at linaro-alip:/media/aplay# ./aplay pcm.wav
Playing sample: 2 ch, 48000 hz, 16 bit
..
That can be normal playing the music.
----

PATCH[4/9-6/9]: ====> Support wifi/bt module for kylin board
858e2cc ARM: dts: rockchip: enable the uart0 for kylin board
28eba08 ARM: dts: rockchip: enable the high speed on sdio for kylin board
9ece822 ARM: dts: rockchip: add the sdio power sequence for kylin board

The wifi/bt driver hasn't land in mainline, the driver is in here:
https://github.com/Caesar-github/rockchip/commit/76d91ce5ac2bff226f77b000e8a736bcd72b034f

/* create wpa_supplicant in /etc/wifi */
ctrl_interface=/var/run/wpa_supplicant
update_config=1

wpa_supplicant -Dnl80211 -c /etc/wifi/wpa_supplicant.conf -i wlan0 -B
wpa_cli add_network
wpa_cli set_network 1 ssid \"wxt\"
wpa_cli set_network 1 psk \"123456789\"
wpa_cli select_network 1
wpa_cli save_config

ifconfig wlan0 192.168.1.105
route add default gw 192.168.1.1

root at linaro-alip:/# ping 192.168.1.1
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=6.83 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=184 ms
64 bytes from 192.168.1.1: icmp_seq=3 ttl=64 time=9.21 ms
64 bytes from 192.168.1.1: icmp_seq=4 ttl=64 time=6.84 ms
64 bytes from 192.168.1.1: icmp_seq=5 ttl=64 time=11.2 ms
64 bytes from 192.168.1.1: icmp_seq=6 ttl=64 time=9.24 ms
64 bytes from 192.168.1.1: icmp_seq=7 ttl=64 time=12.9 ms
64 bytes from 192.168.1.1: icmp_seq=8 ttl=64 time=7.02 ms
64 bytes from 192.168.1.1: icmp_seq=9 ttl=64 time=24.3 ms
64 bytes from 192.168.1.1: icmp_seq=10 ttl=64 time=13.6 ms
64 bytes from 192.168.1.1: icmp_seq=11 ttl=64 time=3.87 m
...

The wifi throughput:

iperf -s -i 1
...
[  4] 116.0-117.0 sec  2.88 MBytes  24.1 Mbits/sec
[  4] 117.0-118.0 sec  2.99 MBytes  25.0 Mbits/sec
[  4] 118.0-119.0 sec  3.01 MBytes  25.2 Mbits/sec
[  4] 119.0-120.0 sec  2.89 MBytes  24.2 Mbits/sec
[  4]  0.0-120.4 sec   332 MBytes  23.1 Mbits/sec
----

PATCH[7/9]: ====> sdmmc patch for kylin board
ARM: dts: rockchip: add the sdmmc for kylin board

You should mark it "okay" if you want to use the sdmmc.
This patch default is disabing the sdmmc since the sdmmc and
uart2(debug port) is reusing pin.
----

PATCH[8/9]: ====> reboot mode support
ARM: dts: rockchip: add reboot-mode node for rk3036 SoCs

This patch is depend on Andy's patches on
http://www.spinics.net/lists/kernel/msg2160362.html

----

PATCH[9/9]:====> DMA Patchs
ARM: dts: rockchip: add pl330-broken-no-flushp quirk for rk3036 SoCs

In order to be better read/write for emmc.
This patch is based on the following:(Fix broken DMAFLUSHP on Rockchips platform)

3055695 FROMLIST: DMA: dmaengine: expose max burst capability to clientmZ
(am from https://patchwork.kernel.org/patch/7399361/)
ed56bee FROMLIST: DMA: pl330: add quirk for broken no flushp
(am from https://patchwork.kernel.org/patch/7399371/)
0c43bc3 FROMLIST: Documentation: arm-pl330: add description of arm, pl330-broken-no-flushp
(am from https://patchwork.kernel.org/patch/7399371/)
623a699 FROMLIST: DMA: pl330: support burst mode for dev-to-mem and mem-to-dev transmit
(am from https://patchwork.kernel.org/patch/7399381/)
----


Changes in v3:
- As the perious discuss on https://patchwork.kernel.org/patch/5427131/,
  I think Mark likes do it in codec driver, I have to say I agree this
  patch in here since that's the i2s block output. I don't know if the
  Mark has changed his mind.
- Add the suspend/resume handle the clock.
- As Javier/Heiko discuss on https://patchwork.kernel.org/patch/7974311/,
  try to change dts with reset-gpios handle.
- fix the commit content in sdmmc patch.
- fix the commit content in pl330* quirk patch.

Caesar Wang (8):
  ARM: dts: rockchip: add hdmi/vop device node for rk3036
  ARM: dts: rockchip: add i2s_clk_out to list of clocks used on rk3036
    i2s
  ARM: dts: rockchip: enable the uart0 for kylin board
  ARM: dts: rockchip: enable the high speed on sdio for kylin board
  ARM: dts: rockchip: add the sdio power sequence for kylin board
  ARM: dts: rockchip: add the sdmmc for kylin board
  ARM: dts: rockchip: add reboot-mode node for rk3036 SoCs
  ARM: dts: rockchip: add pl330-broken-no-flushp quirk for rk3036 SoCs

Sonny Rao (1):
  ASoC: rockchip: i2s: add support for grabbing output clock to codec

 arch/arm/boot/dts/rk3036-kylin.dts | 62 ++++++++++++++++++++++++
 arch/arm/boot/dts/rk3036.dtsi      | 99 ++++++++++++++++++++++++++++++++++++--
 sound/soc/rockchip/rockchip_i2s.c  | 24 +++++++++
 3 files changed, 182 insertions(+), 3 deletions(-)

-- 
1.9.1



From wxt at rock-chips.com  Fri Jan 15 05:49:48 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Fri, 15 Jan 2016 21:49:48 +0800
Subject: [PATCH v3 1/9] ARM: dts: rockchip: add hdmi/vop device node for rk3036
In-Reply-To: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
References: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1452865796-23527-2-git-send-email-wxt@rock-chips.com>

This patch adds the needed display info for rk3036 SOCs.

The rk3036 support two overlay plane and one hwc plane,
it support IOMMU, and its IOMMU same as rk3288's.
Meanwhile, add the inno hdmi for HDMI display.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
---

Changes in v3: None

 arch/arm/boot/dts/rk3036-kylin.dts | 12 +++++++
 arch/arm/boot/dts/rk3036.dtsi      | 66 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 78 insertions(+)

diff --git a/arch/arm/boot/dts/rk3036-kylin.dts b/arch/arm/boot/dts/rk3036-kylin.dts
index b287625..30a9aca 100644
--- a/arch/arm/boot/dts/rk3036-kylin.dts
+++ b/arch/arm/boot/dts/rk3036-kylin.dts
@@ -88,6 +88,10 @@
 	status = "okay";
 };
 
+&hdmi {
+	status = "okay";
+};
+
 &i2c1 {
 	clock-frequency = <400000>;
 
@@ -320,6 +324,14 @@
 	status = "okay";
 };
 
+&vop {
+	status = "okay";
+};
+
+&vop_mmu {
+	status = "okay";
+};
+
 &pinctrl {
 	pmic {
 		pmic_int: pmic-int {
diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
index ee457a2..0b128be 100644
--- a/arch/arm/boot/dts/rk3036.dtsi
+++ b/arch/arm/boot/dts/rk3036.dtsi
@@ -147,6 +147,42 @@
 		};
 	};
 
+	vop: vop at 10118000 {
+		compatible = "rockchip,rk3036-vop";
+		reg = <0x10118000 0x19c>;
+		interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru ACLK_LCDC>, <&cru SCLK_LCDC>, <&cru HCLK_LCDC>;
+		clock-names = "aclk_vop", "dclk_vop", "hclk_vop";
+		resets = <&cru SRST_LCDC1_A>, <&cru SRST_LCDC1_H>, <&cru SRST_LCDC1_D>;
+		reset-names = "axi", "ahb", "dclk";
+		iommus = <&vop_mmu>;
+
+		status = "disabled";
+
+		vop_out: port {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			vop_out_hdmi: endpoint at 0 {
+				reg = <1>;
+				remote-endpoint = <&hdmi_in_vop>;
+			};
+		};
+	};
+
+	display-subsystem {
+		compatible = "rockchip,display-subsystem";
+		ports = <&vop_out>;
+	};
+
+	vop_mmu: iommu at 10118300 {
+		compatible = "rockchip,iommu";
+		reg = <0x10118300 0x100>;
+		interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-names = "vop_mmu";
+		#iommu-cells = <0>;
+		status = "disabled";
+	};
+
 	gic: interrupt-controller at 10139000 {
 		compatible = "arm,gic-400";
 		interrupt-controller;
@@ -274,6 +310,27 @@
 		status = "disabled";
 	};
 
+	hdmi: hdmi at 20034000 {
+		compatible = "rockchip,rk3036-inno-hdmi";
+		reg = <0x20034000 0x4000>;
+		interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru  PCLK_HDMI>;
+		clock-names = "pclk";
+		rockchip,grf = <&grf>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&hdmi_ctl>;
+		status = "disabled";
+
+		hdmi_in: port {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			hdmi_in_vop: endpoint at 0 {
+				reg = <0>;
+				remote-endpoint = <&vop_out_hdmi>;
+			};
+		};
+	};
+
 	timer: timer at 20044000 {
 		compatible = "rockchip,rk3036-timer", "rockchip,rk3288-timer";
 		reg = <0x20044000 0x20>;
@@ -588,6 +645,15 @@
 			};
 		};
 
+		hdmi {
+			hdmi_ctl: hdmi-ctl {
+				rockchip,pins = <1 8  RK_FUNC_1 &pcfg_pull_none>,
+						<1 9  RK_FUNC_1 &pcfg_pull_none>,
+						<1 10 RK_FUNC_1 &pcfg_pull_none>,
+						<1 11 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
 		uart0 {
 			uart0_xfer: uart0-xfer {
 				rockchip,pins = <0 16 RK_FUNC_1 &pcfg_pull_default>,
-- 
1.9.1



From wxt at rock-chips.com  Fri Jan 15 05:49:49 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Fri, 15 Jan 2016 21:49:49 +0800
Subject: [PATCH v3 2/9] ARM: dts: rockchip: add i2s_clk_out to list of clocks
 used on rk3036 i2s
In-Reply-To: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
References: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1452865796-23527-3-git-send-email-wxt@rock-chips.com>

The I2S block that it needs to claim that output clock. This also
adds properties to model the relationship between the output clock and
the i2s clock itself, to ensure that the clock muxing is set correctly.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
---

Changes in v3: None

 arch/arm/boot/dts/rk3036.dtsi | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
index 0b128be..cf0968c 100644
--- a/arch/arm/boot/dts/rk3036.dtsi
+++ b/arch/arm/boot/dts/rk3036.dtsi
@@ -277,8 +277,8 @@
 		interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;
 		#address-cells = <1>;
 		#size-cells = <0>;
-		clock-names = "i2s_clk", "i2s_hclk";
-		clocks = <&cru SCLK_I2S>, <&cru HCLK_I2S>;
+		clock-names = "i2s_clk", "i2s_hclk", "i2s_clk_out";
+		clocks = <&cru SCLK_I2S>, <&cru HCLK_I2S>, <&cru SCLK_I2S_OUT>;
 		dmas = <&pdma 0>, <&pdma 1>;
 		dma-names = "tx", "rx";
 		pinctrl-names = "default";
-- 
1.9.1



From wxt at rock-chips.com  Fri Jan 15 05:49:50 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Fri, 15 Jan 2016 21:49:50 +0800
Subject: [PATCH v3 3/9] ASoC: rockchip: i2s: add support for grabbing output
 clock to codec
In-Reply-To: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
References: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1452865796-23527-4-git-send-email-wxt@rock-chips.com>

From: Sonny Rao <sonnyrao at chromium.org>

We need to claim the clock which is driving the codec so that when we
enable clock gating, we continue to clock the codec when needed.
I make this an optional clock since there might be some applications
where we don't need it but can still use the I2S block.

Signed-off-by: Sonny Rao <sonnyrao at chromium.org>
Signed-off-by: Jianqun Xu <jay.xu at rock-chips.com>
Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v3:
- As the perious discuss on https://patchwork.kernel.org/patch/5427131/,
  I think Mark likes do it in codec driver, I have to say I agree this
  patch in here since that's the i2s block output. I don't know if the
  Mark has changed his mind.
- Add the suspend/resume handle the clock.

 sound/soc/rockchip/rockchip_i2s.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c
index 58ee645..5889bba 100644
--- a/sound/soc/rockchip/rockchip_i2s.c
+++ b/sound/soc/rockchip/rockchip_i2s.c
@@ -28,6 +28,7 @@ struct rk_i2s_dev {
 
 	struct clk *hclk;
 	struct clk *mclk;
+	struct clk *oclk;
 
 	struct snd_dmaengine_dai_dma_data capture_dma_data;
 	struct snd_dmaengine_dai_dma_data playback_dma_data;
@@ -47,6 +48,9 @@ static int i2s_runtime_suspend(struct device *dev)
 {
 	struct rk_i2s_dev *i2s = dev_get_drvdata(dev);
 
+	if (i2s->oclk)
+		clk_disable_unprepare(i2s->oclk);
+
 	clk_disable_unprepare(i2s->mclk);
 
 	return 0;
@@ -63,6 +67,14 @@ static int i2s_runtime_resume(struct device *dev)
 		return ret;
 	}
 
+	if (i2s->oclk) {
+		ret = clk_prepare_enable(i2s->oclk);
+		if (ret) {
+			dev_err(i2s->dev, "oclk enable failed %d\n", ret);
+			return ret;
+		}
+	}
+
 	return 0;
 }
 
@@ -480,6 +492,15 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
 		return PTR_ERR(i2s->mclk);
 	}
 
+	i2s->oclk = devm_clk_get(&pdev->dev, "i2s_clk_out");
+	if (IS_ERR(i2s->oclk)) {
+		dev_dbg(&pdev->dev, "Didn't find output clock\n");
+		i2s->oclk = NULL;
+	}
+
+	if (i2s->oclk)
+		ret = clk_prepare_enable(i2s->oclk);
+
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	regs = devm_ioremap_resource(&pdev->dev, res);
 	if (IS_ERR(regs))
@@ -552,6 +573,9 @@ static int rockchip_i2s_remove(struct platform_device *pdev)
 	if (!pm_runtime_status_suspended(&pdev->dev))
 		i2s_runtime_suspend(&pdev->dev);
 
+	if (i2s->oclk)
+		clk_disable_unprepare(i2s->oclk);
+
 	clk_disable_unprepare(i2s->mclk);
 	clk_disable_unprepare(i2s->hclk);
 
-- 
1.9.1



From wxt at rock-chips.com  Fri Jan 15 05:49:51 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Fri, 15 Jan 2016 21:49:51 +0800
Subject: [PATCH v3 4/9] ARM: dts: rockchip: enable the uart0 for kylin board
In-Reply-To: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
References: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1452865796-23527-5-git-send-email-wxt@rock-chips.com>

This patch is enabling the uart0 for bluetooth module.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
---

Changes in v3: None

 arch/arm/boot/dts/rk3036-kylin.dts | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/arm/boot/dts/rk3036-kylin.dts b/arch/arm/boot/dts/rk3036-kylin.dts
index 30a9aca..2a45be7 100644
--- a/arch/arm/boot/dts/rk3036-kylin.dts
+++ b/arch/arm/boot/dts/rk3036-kylin.dts
@@ -316,6 +316,10 @@
 	status = "okay";
 };
 
+&uart0 {
+	status = "okay";
+};
+
 &usb_host {
 	status = "okay";
 };
-- 
1.9.1



From wxt at rock-chips.com  Fri Jan 15 05:49:52 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Fri, 15 Jan 2016 21:49:52 +0800
Subject: [PATCH v3 5/9] ARM: dts: rockchip: enable the high speed on sdio for
 kylin board
In-Reply-To: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
References: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1452865796-23527-6-git-send-email-wxt@rock-chips.com>

We want to the higher speed for wifi module working.

Bootup kernel log:
...
mmc_host mmc0: Bus speed (slot 0) = 37125000Hz (slot req 37500000Hz,
actual 37125000HZ div = 0)

or run 'cat /sys/kernel/debug/clk/clk_summary |grep phase -C 1' to check
Otherwise, the mmc0 will run 400khz defalult value to work.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
---

Changes in v3: None

 arch/arm/boot/dts/rk3036-kylin.dts | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/arch/arm/boot/dts/rk3036-kylin.dts b/arch/arm/boot/dts/rk3036-kylin.dts
index 2a45be7..4fbd0a3 100644
--- a/arch/arm/boot/dts/rk3036-kylin.dts
+++ b/arch/arm/boot/dts/rk3036-kylin.dts
@@ -303,6 +303,11 @@
 
 	broken-cd;
 	bus-width = <4>;
+	cap-sd-highspeed;
+	sd-uhs-sdr12;
+	sd-uhs-sdr25;
+	sd-uhs-sdr50;
+	sd-uhs-sdr104;
 	cap-sdio-irq;
 	default-sample-phase = <90>;
 	keep-power-in-suspend;
-- 
1.9.1



From wxt at rock-chips.com  Fri Jan 15 05:49:54 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Fri, 15 Jan 2016 21:49:54 +0800
Subject: [PATCH v3 7/9] ARM: dts: rockchip: add the sdmmc for kylin board
In-Reply-To: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
References: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1452865796-23527-8-git-send-email-wxt@rock-chips.com>

Although We can add the sdmmc node, shouldn't enable it.
Since the sdmmc is reusing the same pin with uart2.
Unfortunately, the uart2 is used by the debug port, so that will cause
the debug information can't display on console if enabling the sdmmc.

As we have supported the sdmmc (sd card) on hardware for kylin board.
So, maybe we can have the sdmmc node in kylin dts, not to enable it.

Anyway, you only need add the okay status if someone want to enable the
sdmmc.

e.g.
if you use the adb to debug with android os.
You can add the
status = "okay" to enable the sdmmc for sd card working.
The defauly status is disabling it.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v3:
- fix the commit content in sdmmc patch.

 arch/arm/boot/dts/rk3036-kylin.dts | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/arch/arm/boot/dts/rk3036-kylin.dts b/arch/arm/boot/dts/rk3036-kylin.dts
index d30a6dd..8bdbb95 100644
--- a/arch/arm/boot/dts/rk3036-kylin.dts
+++ b/arch/arm/boot/dts/rk3036-kylin.dts
@@ -335,6 +335,17 @@
 	pinctrl-0 = <&sdio_clk &sdio_cmd &sdio_bus4>;
 };
 
+&sdmmc {
+	bus-width = <4>;
+	cap-mmc-highspeed;
+	cap-sd-highspeed;
+	card-detect-delay = <200>;
+	disable-wp;
+	num-slots = <1>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&sdmmc_clk>, <&sdmmc_cmd>, <&sdmmc_cd>, <&sdmmc_bus4>;
+};
+
 &uart2 {
 	status = "okay";
 };
@@ -372,6 +383,12 @@
 		};
 	};
 
+	sdmmc {
+		sdmmc_pwr: sdmmc-pwr {
+			rockchip,pins = <2 28 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+
 	sleep {
 		global_pwroff: global-pwroff {
 			rockchip,pins = <2 7 RK_FUNC_1 &pcfg_pull_none>;
-- 
1.9.1



From wxt at rock-chips.com  Fri Jan 15 05:49:53 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Fri, 15 Jan 2016 21:49:53 +0800
Subject: [PATCH v3 6/9] ARM: dts: rockchip: add the sdio power sequence for
 kylin board
In-Reply-To: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
References: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1452865796-23527-7-git-send-email-wxt@rock-chips.com>

This patch adds the sdio power sequence for kylin board.
The WLAN attached to a SDIO interface, wifi/bluetooth have
reset and power been needed to enable.

AFAIK, the simple power sequence provider sets a value for multiple GPIOs.
So the reset and power of WlAN chip can be handled in mmc power sequence.
On the module itself this is one of these, that should can be handled
by reset GPIOs in simple mmc power sequence.

The Bluetooth host wake is high active from bootup, this patch is also
set pinctrl bias as the default to enable the pull up in soc internal.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v3:
- As Javier/Heiko discuss on https://patchwork.kernel.org/patch/7974311/,
  try to change dts with reset-gpios handle.

 arch/arm/boot/dts/rk3036-kylin.dts | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/arch/arm/boot/dts/rk3036-kylin.dts b/arch/arm/boot/dts/rk3036-kylin.dts
index 4fbd0a3..d30a6dd 100644
--- a/arch/arm/boot/dts/rk3036-kylin.dts
+++ b/arch/arm/boot/dts/rk3036-kylin.dts
@@ -78,6 +78,23 @@
 		regulator-always-on;
 		regulator-boot-on;
 	};
+
+	sdio_pwrseq: sdio-pwrseq {
+		compatible = "mmc-pwrseq-simple";
+		pinctrl-names = "default";
+		pinctrl-0 = <&bt_wake_h>;
+
+		/*
+		 * On the module itself this is one of these (depending
+		 * on the actual card populated):
+		 * - SDIO_RESET_L_WL_REG_ON
+		 * - SDIO_RESET_L_WL_RST
+		 * - SDIO_RESET_L_BT_EN
+		 */
+		reset-gpios = <&gpio0 26 GPIO_ACTIVE_LOW>, /* WL_REG_ON */
+			      <&gpio0 27 GPIO_ACTIVE_LOW>, /* WL_RST */
+			      <&gpio2 9  GPIO_ACTIVE_LOW>; /* BT_EN */
+	};
 };
 
 &acodec {
@@ -311,6 +328,7 @@
 	cap-sdio-irq;
 	default-sample-phase = <90>;
 	keep-power-in-suspend;
+	mmc-pwrseq = <&sdio_pwrseq>;
 	non-removable;
 	num-slots = <1>;
 	pinctrl-names = "default";
@@ -348,6 +366,12 @@
 		};
 	};
 
+	sdio {
+		bt_wake_h: bt-wake-h {
+			rockchip,pins = <2 8 RK_FUNC_GPIO &pcfg_pull_default>;
+		};
+	};
+
 	sleep {
 		global_pwroff: global-pwroff {
 			rockchip,pins = <2 7 RK_FUNC_1 &pcfg_pull_none>;
-- 
1.9.1



From wxt at rock-chips.com  Fri Jan 15 05:49:55 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Fri, 15 Jan 2016 21:49:55 +0800
Subject: [PATCH v3 8/9] ARM: dts: rockchip: add reboot-mode node for rk3036
 SoCs
In-Reply-To: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
References: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1452865796-23527-9-git-send-email-wxt@rock-chips.com>

Add reboot mode driver DT node for rk3036 platform.
This patch adds the needed reboot mode node for rk3036.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
---

Changes in v3: None

 arch/arm/boot/dts/rk3036.dtsi | 28 +++++++++++++++++++++++++++-
 1 file changed, 27 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
index cf0968c..f5ecfff 100644
--- a/arch/arm/boot/dts/rk3036.dtsi
+++ b/arch/arm/boot/dts/rk3036.dtsi
@@ -43,6 +43,7 @@
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 #include <dt-bindings/pinctrl/rockchip.h>
 #include <dt-bindings/clock/rk3036-cru.h>
+#include <dt-bindings/soc/rockchip_boot-mode.h>
 #include "skeleton.dtsi"
 
 / {
@@ -297,8 +298,33 @@
 	};
 
 	grf: syscon at 20008000 {
-		compatible = "rockchip,rk3036-grf", "syscon";
+		compatible = "rockchip,rk3036-grf", "syscon", "simple-mfd";
 		reg = <0x20008000 0x1000>;
+
+		reboot-mode {
+			compatible = "syscon-reboot-mode";
+			offset = <0x1d8>;
+
+			normal {
+				linux,mode = "normal";
+				loader,magic = <BOOT_NORMAL>;
+			};
+
+			loader {
+				linux,mode = "loader";
+				loader,magic = <BOOT_LOADER>;
+			};
+
+			recovery {
+				linux,mode = "recovery";
+				loader,magic = <BOOT_RECOVERY>;
+			};
+
+			fastboot {
+				linux,mode = "fastboot";
+				loader,magic = <BOOT_FASTBOOT>;
+			};
+		};
 	};
 
 	acodec: acodec-ana at 20030000 {
-- 
1.9.1



From wxt at rock-chips.com  Fri Jan 15 05:49:56 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Fri, 15 Jan 2016 21:49:56 +0800
Subject: [PATCH v3 9/9] ARM: dts: rockchip: add pl330-broken-no-flushp quirk
 for rk3036 SoCs
In-Reply-To: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
References: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1452865796-23527-10-git-send-email-wxt@rock-chips.com>

Pl330 integrated in rk3036 platform that doesn't support
DMAFLUSHP function. So we add 'arm,pl330-broken-no-flushp' quirk
for rk3036.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v3:
- fix the commit content in pl330* quirk patch.

 arch/arm/boot/dts/rk3036.dtsi | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
index f5ecfff..c029f6f 100644
--- a/arch/arm/boot/dts/rk3036.dtsi
+++ b/arch/arm/boot/dts/rk3036.dtsi
@@ -103,6 +103,7 @@
 		pdma: pdma at 20078000 {
 			compatible = "arm,pl330", "arm,primecell";
 			reg = <0x20078000 0x4000>;
+			arm,pl330-broken-no-flushp;
 			interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>,
 				     <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;
 			#dma-cells = <1>;
-- 
1.9.1



From heiko at sntech.de  Fri Jan 15 06:03:52 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Fri, 15 Jan 2016 15:03:52 +0100
Subject: [PATCH 2/2] arm64: dts: rockchip: Add basic support for orion-r68
In-Reply-To: <CABxcv=m+hK3xS8_fYNns-xE5DaLHD0D_Yj8McjM72Y_5V=YmSw@mail.gmail.com>
References: <1452863192-29231-1-git-send-email-matthias.bgg@gmail.com>
 <1452863192-29231-2-git-send-email-matthias.bgg@gmail.com>
 <CABxcv=m+hK3xS8_fYNns-xE5DaLHD0D_Yj8McjM72Y_5V=YmSw@mail.gmail.com>
Message-ID: <2174023.jkjdoEECkq@phil>

Hi Javier,

Am Freitag, 15. Januar 2016, 10:28:44 schrieb Javier Martinez Canillas:
> This is not a complete review but I just wanted to comment on two
> things that I noticed:

same for me for now :-)


> On Fri, Jan 15, 2016 at 10:06 AM, Matthias Brugger
> <matthias.bgg at gmail.com> wrote:
> 
> [snip]
> 
> > +       };
> > +
> > +       vcc_18: vcc18-regulator {
> > +               compatible = "regulator-fixed";
> > +               regulator-name = "vcc_18";
> > +               regulator-min-microvolt = <1800000>;
> > +               regulator-max-microvolt = <1800000>;
> > +               regulator-always-on;
> > +               regulator-boot-on;
> > +               vin-supply = <&vcc_sys>;
> > +       };
> > +
> > +       /* supplies both host and otg */
> > +       vcc_host: vcc-host-regulator {
> > +               compatible = "regulator-fixed";
> > +               gpio = <&gpio0 4 GPIO_ACTIVE_LOW>;
> > +               pinctrl-names = "default";
> > +               pinctrl-0 = <&host_vbus_drv>;
> > +               regulator-name = "vcc_host";
> > +               regulator-always-on;
> > +               regulator-boot-on;
> > +               vin-supply = <&vcc_sys>;
> > +       };
> > +
> > +       vccio_sd: vcc-io-sd-regulator {
> > +               regulator-name= "vccio_sd";
> > +               gpio = <&gpio0 9 GPIO_ACTIVE_LOW>;
> > +               regulator-min-microvolt = <3300000>;
> > +               regulator-max-microvolt = <3300000>;
> > +       };
> > +
> > +       vcc_sd: vcc-sd-regulator {
> > +               compatible = "regulator-fixed";
> > +               regulator-name = "vcc_sd";
> > +               gpio = <&gpio3 11 GPIO_ACTIVE_LOW>;
> > +               regulator-min-microvolt = <3300000>;
> > +               regulator-max-microvolt = <3300000>;
> > +               regulator-always-on;
> > +               regulator-boot-on;
> > +               vin-supply = <&vcc_io>;
> > +       };
> > +
> > +       vcc_io: vcc-io-regulator {
> > +               compatible = "regulator-fixed";
> > +               regulator-name = "vcc_io";
> > +               regulator-min-microvolt = <3300000>;
> > +               regulator-max-microvolt = <3300000>;
> > +               regulator-always-on;
> > +               regulator-boot-on;
> > +               vin-supply = <&vcc_sys>;
> > +       };
> > +
> > +       vcc_lan: vcc-lan-regulator {
> > +               compatible = "regulator-fixed";
> > +               regulator-name = "vcc_lan";
> > +               regulator-min-microvolt = <3300000>;
> > +               regulator-max-microvolt = <3300000>;
> > +               regulator-always-on;
> > +               regulator-boot-on;
> > +               vin-supply = <&vcc_io>;
> > +       };
> > +
> > +       vcc_sys: vcc-sys-regulator {
> > +               compatible = "regulator-fixed";
> > +               regulator-name = "vcc_sys";
> > +               regulator-min-microvolt = <5000000>;
> > +               regulator-max-microvolt = <5000000>;
> > +               regulator-always-on;
> > +               regulator-boot-on;
> > +       };
> > +
> > +       vccio_wl: vccio-wl-regulator {
> > +               compatible = "regulator-fixed";
> > +               regulator-name = "vccio_wl";
> > +               regulator-min-microvolt = <3300000>;
> > +               regulator-max-microvolt = <3300000>;
> > +               regulator-always-on;
> > +               regulator-boot-on;
> > +               vin-supply = <&vcc_io>;
> > +       };
> > +
> > +       vdd_10: vdd-10-regulator {
> > +               compatible = "regulator-fixed";
> > +               regulator-name = "vdd_10";
> > +               regulator-min-microvolt = <1000000>;
> > +               regulator-max-microvolt = <1000000>;
> > +               regulator-always-on;
> > +               regulator-boot-on;
> > +               vin-supply = <&vcc_sys>;
> > +       };
> > +};
> 
> There is only one regulator that is not marked as always-on. This will
> prevent the regulator subsystem to disable unused regulators. Do you
> really need all of them to be always-on?

I do believe the regulators should be more or less correct. Sadly there are 
no real schematics for that specific device available, but the reference 
design for rk3368-based tv-boxes uses a hirarchy of hard-wired individual 
regulators without any ability to control for most of them [aka always on].

And due to the r68 not sporting any different real pmic, I'd believe the 
Orion will most likely also follow that scheme.


Heiko


From mark.rutland at arm.com  Fri Jan 15 06:21:15 2016
From: mark.rutland at arm.com (Mark Rutland)
Date: Fri, 15 Jan 2016 14:21:15 +0000
Subject: [PATCH 2/2] arm64: dts: rockchip: Add basic support for orion-r68
In-Reply-To: <1452863192-29231-2-git-send-email-matthias.bgg@gmail.com>
References: <1452863192-29231-1-git-send-email-matthias.bgg@gmail.com>
 <1452863192-29231-2-git-send-email-matthias.bgg@gmail.com>
Message-ID: <20160115142114.GH3262@leverpostej>

On Fri, Jan 15, 2016 at 02:06:32PM +0100, Matthias Brugger wrote:
> This patch adds basic support for the Tronsmart orion r86 set-top-box.

As far as I can tell, "Orion r68" is a family of products, for which the
"Meta" [1] and "Pro" [2] differ at least in the amount of RAM and flash,
and also appear to differ in wireless chipset (ap6335 for Meta, ap6212
for Pro).

Judging by the memory node I'm guessing this is for the "Pro".

This should be reflected in the model and compatible strings (in
addition to the commit message).

Thanks,
Mark.

[1] http://www.tronsmart.com/products/tronsmart-orion-r68-meta
[2] http://www.tronsmart.com/products/tronsmart-orion-r68-pro

> 
> Signed-off-by: Matthias Brugger <mbrugger at suse.com>
> ---
>  Documentation/devicetree/bindings/arm/rockchip.txt |   4 +
>  arch/arm64/boot/dts/rockchip/Makefile              |   1 +
>  arch/arm64/boot/dts/rockchip/rk3368-orion-r68.dts  | 370 +++++++++++++++++++++
>  3 files changed, 375 insertions(+)
>  create mode 100644 arch/arm64/boot/dts/rockchip/rk3368-orion-r68.dts
> 
> diff --git a/Documentation/devicetree/bindings/arm/rockchip.txt b/Documentation/devicetree/bindings/arm/rockchip.txt
> index 8e985dd..fa97d93 100644
> --- a/Documentation/devicetree/bindings/arm/rockchip.txt
> +++ b/Documentation/devicetree/bindings/arm/rockchip.txt
> @@ -72,3 +72,7 @@ Rockchip platforms device tree bindings
>  - Rockchip R88 board:
>      Required root node properties:
>        - compatible = "rockchip,r88", "rockchip,rk3368";
> +
> +- Tronsmart Orion R68
> +    Required root node properties:
> +      - compatible = "tronsmart,orion-r68", "rockchip,rk3368";
> diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile
> index 601e6a2..950fe63 100644
> --- a/arch/arm64/boot/dts/rockchip/Makefile
> +++ b/arch/arm64/boot/dts/rockchip/Makefile
> @@ -1,4 +1,5 @@
>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-r88.dtb
> +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-orion-r68.dtb
>  
>  always		:= $(dtb-y)
>  subdir-y	:= $(dts-dirs)
> diff --git a/arch/arm64/boot/dts/rockchip/rk3368-orion-r68.dts b/arch/arm64/boot/dts/rockchip/rk3368-orion-r68.dts
> new file mode 100644
> index 0000000..3f71d506
> --- /dev/null
> +++ b/arch/arm64/boot/dts/rockchip/rk3368-orion-r68.dts
> @@ -0,0 +1,370 @@
> +/*
> + * Copyright (c) 2015 Matthias Brugger <mbrugger at suse.com>
> + *
> + * This file is dual-licensed: you can use it either under the terms
> + * of the GPL or the X11 license, at your option. Note that this dual
> + * licensing only applies to this file, and not this project as a
> + * whole.
> + *
> + *  a) This file is free software; you can redistribute it and/or
> + *     modify it under the terms of the GNU General Public License as
> + *     published by the Free Software Foundation; either version 2 of the
> + *     License, or (at your option) any later version.
> + *
> + *     This file is distributed in the hope that it will be useful,
> + *     but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *     GNU General Public License for more details.
> + *
> + * Or, alternatively,
> + *
> + *  b) Permission is hereby granted, free of charge, to any person
> + *     obtaining a copy of this software and associated documentation
> + *     files (the "Software"), to deal in the Software without
> + *     restriction, including without limitation the rights to use,
> + *     copy, modify, merge, publish, distribute, sublicense, and/or
> + *     sell copies of the Software, and to permit persons to whom the
> + *     Software is furnished to do so, subject to the following
> + *     conditions:
> + *
> + *     The above copyright notice and this permission notice shall be
> + *     included in all copies or substantial portions of the Software.
> + *
> + *     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.
> + */
> +
> +/dts-v1/;
> +#include "rk3368.dtsi"
> +
> +/ {
> +	model = "Rockchip Orion R68";
> +	compatible = "tronsmart,orion-r68", "rockchip,rk3368";
> +
> +	chosen {
> +		stdout-path = "serial2:115200n8";
> +	};
> +
> +	memory {
> +		device_type = "memory";
> +		reg = <0x0 0x0 0x0 0x40000000>;
> +	};
> +
> +	emmc_pwrseq: emmc-pwrseq {
> +		compatible = "mmc-pwrseq-emmc";
> +		pinctrl-0 = <&emmc_reset>;
> +		pinctrl-names = "default";
> +		reset-gpios = <&gpio2 3 GPIO_ACTIVE_HIGH>;
> +	};
> +
> +	ext_gmac: external-gmac-clock {
> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +		clock-frequency = <125000000>;
> +		clock-output-names = "ext_gmac";
> +	};
> +
> +	keys: gpio-keys {
> +		compatible = "gpio-keys";
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&pwr_key>;
> +
> +		button at 0 {
> +			gpio-key,wakeup = <1>;
> +			gpios = <&gpio0 5 GPIO_ACTIVE_HIGH>;
> +			label = "GPIO Power";
> +			linux,code = <116>;
> +		};
> +	};
> +
> +	leds: gpio-leds {
> +		compatible = "gpio-leds";
> +
> +		work {
> +			gpios = <&gpio3 29 GPIO_ACTIVE_HIGH>;
> +			label = "orion:purple:led";
> +			pinctrl-names = "default";
> +			pinctrl-0 = <&led_ctl>;
> +			default-state = "on";
> +		};
> +	};
> +
> +	vcc_18: vcc18-regulator {
> +		compatible = "regulator-fixed";
> +		regulator-name = "vcc_18";
> +		regulator-min-microvolt = <1800000>;
> +		regulator-max-microvolt = <1800000>;
> +		regulator-always-on;
> +		regulator-boot-on;
> +		vin-supply = <&vcc_sys>;
> +	};
> +
> +	/* supplies both host and otg */
> +	vcc_host: vcc-host-regulator {
> +		compatible = "regulator-fixed";
> +		gpio = <&gpio0 4 GPIO_ACTIVE_LOW>;
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&host_vbus_drv>;
> +		regulator-name = "vcc_host";
> +		regulator-always-on;
> +		regulator-boot-on;
> +		vin-supply = <&vcc_sys>;
> +	};
> +
> +	vccio_sd: vcc-io-sd-regulator {
> +		regulator-name= "vccio_sd";
> +		gpio = <&gpio0 9 GPIO_ACTIVE_LOW>;
> +		regulator-min-microvolt = <3300000>;
> +		regulator-max-microvolt = <3300000>;
> +	};
> +
> +	vcc_sd: vcc-sd-regulator {
> +		compatible = "regulator-fixed";
> +		regulator-name = "vcc_sd";
> +		gpio = <&gpio3 11 GPIO_ACTIVE_LOW>;
> +		regulator-min-microvolt = <3300000>;
> +		regulator-max-microvolt = <3300000>;
> +		regulator-always-on;
> +		regulator-boot-on;
> +		vin-supply = <&vcc_io>;
> +	};
> +
> +	vcc_io: vcc-io-regulator {
> +		compatible = "regulator-fixed";
> +		regulator-name = "vcc_io";
> +		regulator-min-microvolt = <3300000>;
> +		regulator-max-microvolt = <3300000>;
> +		regulator-always-on;
> +		regulator-boot-on;
> +		vin-supply = <&vcc_sys>;
> +	};
> +
> +	vcc_lan: vcc-lan-regulator {
> +		compatible = "regulator-fixed";
> +		regulator-name = "vcc_lan";
> +		regulator-min-microvolt = <3300000>;
> +		regulator-max-microvolt = <3300000>;
> +		regulator-always-on;
> +		regulator-boot-on;
> +		vin-supply = <&vcc_io>;
> +	};
> +
> +	vcc_sys: vcc-sys-regulator {
> +		compatible = "regulator-fixed";
> +		regulator-name = "vcc_sys";
> +		regulator-min-microvolt = <5000000>;
> +		regulator-max-microvolt = <5000000>;
> +		regulator-always-on;
> +		regulator-boot-on;
> +	};
> +
> +	vccio_wl: vccio-wl-regulator {
> +		compatible = "regulator-fixed";
> +		regulator-name = "vccio_wl";
> +		regulator-min-microvolt = <3300000>;
> +		regulator-max-microvolt = <3300000>;
> +		regulator-always-on;
> +		regulator-boot-on;
> +		vin-supply = <&vcc_io>;
> +	};
> +
> +	vdd_10: vdd-10-regulator {
> +		compatible = "regulator-fixed";
> +		regulator-name = "vdd_10";
> +		regulator-min-microvolt = <1000000>;
> +		regulator-max-microvolt = <1000000>;
> +		regulator-always-on;
> +		regulator-boot-on;
> +		vin-supply = <&vcc_sys>;
> +	};
> +};
> +
> +&emmc {
> +	broken-cd;
> +	bus-width = <8>;
> +	cap-mmc-highspeed;
> +	disable-wp;
> +	mmc-pwrseq = <&emmc_pwrseq>;
> +	non-removable;
> +	num-slots = <1>;
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&emmc_clk &emmc_cmd &emmc_bus8>;
> +	status = "okay";
> +};
> +
> +&gmac {
> +	assigned-clocks = <&cru SCLK_MAC>;
> +	assigned-clock-parents = <&ext_gmac>;
> +	phy-supply = <&vcc_lan>;
> +	phy-mode = "rgmii";
> +	clock_in_out = "input";
> +	snps,reset-gpio = <&gpio3 12 0>;
> +	snps,reset-active-low;
> +	snps,reset-delays-us = <0 10000 1000000>;
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&rgmii_pins>;
> +	tx_delay = <0x30>;
> +	rx_delay = <0x10>;
> +	status = "ok";
> +};
> +
> +&i2c0 {
> +	status = "okay";
> +
> +	vdd_cpu: syr827 at 40 {
> +		compatible = "silergy,syr827";
> +		reg = <0x40>;
> +		fcs,suspend-voltage-selector = <1>;
> +		regulator-name = "vdd_cpu";
> +		regulator-enable-ramp-delay = <300>;
> +		regulator-min-microvolt = <712500>;
> +		regulator-max-microvolt = <1500000>;
> +		regulator-ramp-delay = <8000>;
> +		regulator-always-on;
> +		regulator-boot-on;
> +		vin-supply = <&vcc_sys>;
> +	};
> +
> +	hym8563: hym8563 at 51 {
> +		compatible = "haoyu,hym8563";
> +		reg = <0x51>;
> +		#clock-cells = <0>;
> +		clock-frequency = <32768>;
> +		clock-output-names = "xin32k";
> +		/* rtc_int is not connected */
> +	};
> +};
> +
> +&pinctrl {
> +	pcfg_pull_none_drv_8ma: pcfg-pull-none-drv-8ma {
> +		bias-disable;
> +		drive-strength = <8>;
> +	};
> +
> +	pcfg_pull_up_drv_8ma: pcfg-pull-up-drv-8ma {
> +		bias-pull-up;
> +		drive-strength = <8>;
> +	};
> +
> +	emmc {
> +		emmc_bus8: emmc-bus8 {
> +			rockchip,pins = <1 18 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
> +					<1 19 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
> +					<1 20 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
> +					<1 21 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
> +					<1 22 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
> +					<1 23 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
> +					<1 24 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
> +					<1 25 RK_FUNC_2 &pcfg_pull_up_drv_8ma>;
> +		};
> +
> +		emmc-clk {
> +			rockchip,pins = <2 4 RK_FUNC_2 &pcfg_pull_none_drv_8ma>;
> +		};
> +
> +		emmc-cmd {
> +			rockchip,pins = <1 26 RK_FUNC_2 &pcfg_pull_up_drv_8ma>;
> +		};
> +
> +		emmc_reset: emmc-reset {
> +			rockchip,pins = <2 3 RK_FUNC_GPIO &pcfg_pull_none>;
> +		};
> +	};
> +
> +	keys {
> +		pwr_key: pwr-key {
> +			rockchip,pins = <0 5 RK_FUNC_GPIO &pcfg_pull_down>;
> +		};
> +	};
> +
> +	leds {
> +		stby_pwren: stby-pwren {
> +			rockchip,pins = <0 12 RK_FUNC_GPIO &pcfg_pull_none>;
> +		};
> +
> +		led_ctl: led-ctl {
> +			rockchip,pins = <3 29 RK_FUNC_GPIO &pcfg_pull_none>;
> +		};
> +	};
> +
> +	sdmmc {
> +		sdmmc_clk: sdmmc-clk {
> +			rockchip,pins = <2 9 RK_FUNC_1 &pcfg_pull_none_drv_8ma>;
> +		};
> +
> +		sdmmc_cmd: sdmmc-cmd {
> +			rockchip,pins = <2 10 RK_FUNC_1 &pcfg_pull_up_drv_8ma>;
> +		};
> +
> +		sdmmc_cd: sdmmc-cd {
> +			rockchip,pins = <2 11 RK_FUNC_1 &pcfg_pull_up_drv_8ma>;
> +		};
> +
> +		sdmmc_bus1: sdmmc-bus1 {
> +			rockchip,pins = <2 5 RK_FUNC_1 &pcfg_pull_up_drv_8ma>;
> +		};
> +
> +		sdmmc_bus4: sdmmc-bus4 {
> +			rockchip,pins = <2 5 RK_FUNC_1 &pcfg_pull_up_drv_8ma>,
> +					<2 6 RK_FUNC_1 &pcfg_pull_up_drv_8ma>,
> +					<2 7 RK_FUNC_1 &pcfg_pull_up_drv_8ma>,
> +					<2 8 RK_FUNC_1 &pcfg_pull_up_drv_8ma>;
> +		};
> +	};
> +
> +	usb {
> +		host_vbus_drv: host-vbus-drv {
> +			rockchip,pins = <0 4 RK_FUNC_GPIO &pcfg_pull_none>;
> +		};
> +	};
> +};
> +
> +&saradc {
> +	vref-supply = <&vcc_18>;
> +	status = "okay";
> +};
> +
> +&sdmmc {
> +	clock-frequency = <50000000>;
> +	clock-freq-min-max = <400000 50000000>;
> +	cap-sd-highspeed;
> +	supports-sd;
> +	broken-cd;
> +	card-detect-delay = <200>;
> +
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&sdmmc_clk &sdmmc_cmd &sdmmc_cd &sdmmc_bus4>;
> +	bus-width = <4>;
> +	num-slots = <1>;
> +	ignore-pm-notify;
> +	keep-power-in-suspend;
> +
> +	vmmc-supply = <&vcc_sd>;
> +	vqmmc-supply = <&vccio_sd>;
> +	status = "okay";
> +};
> +
> +&uart2 {
> +	status = "okay";
> +};
> +
> +&usb_host0_ehci {
> +	status = "okay";
> +};
> +
> +&usb_otg {
> +	dr_mode = "host";
> +	status = "okay";
> +};
> +
> +&wdt {
> +	status = "okay";
> +};
> -- 
> 2.6.2
> 




From javier at dowhile0.org  Fri Jan 15 06:22:52 2016
From: javier at dowhile0.org (Javier Martinez Canillas)
Date: Fri, 15 Jan 2016 11:22:52 -0300
Subject: [PATCH 2/2] arm64: dts: rockchip: Add basic support for orion-r68
In-Reply-To: <2174023.jkjdoEECkq@phil>
References: <1452863192-29231-1-git-send-email-matthias.bgg@gmail.com>
 <1452863192-29231-2-git-send-email-matthias.bgg@gmail.com>
 <CABxcv=m+hK3xS8_fYNns-xE5DaLHD0D_Yj8McjM72Y_5V=YmSw@mail.gmail.com>
 <2174023.jkjdoEECkq@phil>
Message-ID: <CABxcv=nkry9PUg5pp+6jKjYb_G=iEfMa7+evZOrUkifDS8h5BA@mail.gmail.com>

Hello Heiko,

On Fri, Jan 15, 2016 at 11:03 AM, Heiko Stuebner <heiko at sntech.de> wrote:
> Hi Javier,
>
> Am Freitag, 15. Januar 2016, 10:28:44 schrieb Javier Martinez Canillas:
>> This is not a complete review but I just wanted to comment on two
>> things that I noticed:
>
> same for me for now :-)
>
>
>> On Fri, Jan 15, 2016 at 10:06 AM, Matthias Brugger
>> <matthias.bgg at gmail.com> wrote:
>>
>> [snip]
>>
>> > +       };
>> > +
>> > +       vcc_18: vcc18-regulator {
>> > +               compatible = "regulator-fixed";
>> > +               regulator-name = "vcc_18";
>> > +               regulator-min-microvolt = <1800000>;
>> > +               regulator-max-microvolt = <1800000>;
>> > +               regulator-always-on;
>> > +               regulator-boot-on;
>> > +               vin-supply = <&vcc_sys>;
>> > +       };
>> > +
>> > +       /* supplies both host and otg */
>> > +       vcc_host: vcc-host-regulator {
>> > +               compatible = "regulator-fixed";
>> > +               gpio = <&gpio0 4 GPIO_ACTIVE_LOW>;
>> > +               pinctrl-names = "default";
>> > +               pinctrl-0 = <&host_vbus_drv>;
>> > +               regulator-name = "vcc_host";
>> > +               regulator-always-on;
>> > +               regulator-boot-on;
>> > +               vin-supply = <&vcc_sys>;
>> > +       };
>> > +
>> > +       vccio_sd: vcc-io-sd-regulator {
>> > +               regulator-name= "vccio_sd";
>> > +               gpio = <&gpio0 9 GPIO_ACTIVE_LOW>;
>> > +               regulator-min-microvolt = <3300000>;
>> > +               regulator-max-microvolt = <3300000>;
>> > +       };
>> > +
>> > +       vcc_sd: vcc-sd-regulator {
>> > +               compatible = "regulator-fixed";
>> > +               regulator-name = "vcc_sd";
>> > +               gpio = <&gpio3 11 GPIO_ACTIVE_LOW>;
>> > +               regulator-min-microvolt = <3300000>;
>> > +               regulator-max-microvolt = <3300000>;
>> > +               regulator-always-on;
>> > +               regulator-boot-on;
>> > +               vin-supply = <&vcc_io>;
>> > +       };
>> > +
>> > +       vcc_io: vcc-io-regulator {
>> > +               compatible = "regulator-fixed";
>> > +               regulator-name = "vcc_io";
>> > +               regulator-min-microvolt = <3300000>;
>> > +               regulator-max-microvolt = <3300000>;
>> > +               regulator-always-on;
>> > +               regulator-boot-on;
>> > +               vin-supply = <&vcc_sys>;
>> > +       };
>> > +
>> > +       vcc_lan: vcc-lan-regulator {
>> > +               compatible = "regulator-fixed";
>> > +               regulator-name = "vcc_lan";
>> > +               regulator-min-microvolt = <3300000>;
>> > +               regulator-max-microvolt = <3300000>;
>> > +               regulator-always-on;
>> > +               regulator-boot-on;
>> > +               vin-supply = <&vcc_io>;
>> > +       };
>> > +
>> > +       vcc_sys: vcc-sys-regulator {
>> > +               compatible = "regulator-fixed";
>> > +               regulator-name = "vcc_sys";
>> > +               regulator-min-microvolt = <5000000>;
>> > +               regulator-max-microvolt = <5000000>;
>> > +               regulator-always-on;
>> > +               regulator-boot-on;
>> > +       };
>> > +
>> > +       vccio_wl: vccio-wl-regulator {
>> > +               compatible = "regulator-fixed";
>> > +               regulator-name = "vccio_wl";
>> > +               regulator-min-microvolt = <3300000>;
>> > +               regulator-max-microvolt = <3300000>;
>> > +               regulator-always-on;
>> > +               regulator-boot-on;
>> > +               vin-supply = <&vcc_io>;
>> > +       };
>> > +
>> > +       vdd_10: vdd-10-regulator {
>> > +               compatible = "regulator-fixed";
>> > +               regulator-name = "vdd_10";
>> > +               regulator-min-microvolt = <1000000>;
>> > +               regulator-max-microvolt = <1000000>;
>> > +               regulator-always-on;
>> > +               regulator-boot-on;
>> > +               vin-supply = <&vcc_sys>;
>> > +       };
>> > +};
>>
>> There is only one regulator that is not marked as always-on. This will
>> prevent the regulator subsystem to disable unused regulators. Do you
>> really need all of them to be always-on?
>
> I do believe the regulators should be more or less correct. Sadly there are
> no real schematics for that specific device available, but the reference
> design for rk3368-based tv-boxes uses a hirarchy of hard-wired individual
> regulators without any ability to control for most of them [aka always on].
>
> And due to the r68 not sporting any different real pmic, I'd believe the
> Orion will most likely also follow that scheme.
>

I see, thanks a lot for the clarification. But then in that case I
wonder what's the point of defining the gpio property in the fixed
regulator device nodes?

>
> Heiko

Best regards,
Javier


From heiko at sntech.de  Fri Jan 15 06:26:24 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Fri, 15 Jan 2016 15:26:24 +0100
Subject: [PATCH 2/2] arm64: dts: rockchip: Add basic support for orion-r68
In-Reply-To: <CABxcv=nkry9PUg5pp+6jKjYb_G=iEfMa7+evZOrUkifDS8h5BA@mail.gmail.com>
References: <1452863192-29231-1-git-send-email-matthias.bgg@gmail.com>
 <2174023.jkjdoEECkq@phil>
 <CABxcv=nkry9PUg5pp+6jKjYb_G=iEfMa7+evZOrUkifDS8h5BA@mail.gmail.com>
Message-ID: <1804889.MQWMfNjko2@phil>

Am Freitag, 15. Januar 2016, 11:22:52 schrieb Javier Martinez Canillas:
> Hello Heiko,
> 
> On Fri, Jan 15, 2016 at 11:03 AM, Heiko Stuebner <heiko at sntech.de> wrote:
> > Hi Javier,
> > 
> > Am Freitag, 15. Januar 2016, 10:28:44 schrieb Javier Martinez Canillas:
> >> This is not a complete review but I just wanted to comment on two
> > 
> >> things that I noticed:
> > same for me for now :-)
> > 
> >> On Fri, Jan 15, 2016 at 10:06 AM, Matthias Brugger
> >> <matthias.bgg at gmail.com> wrote:
> >> 
> >> [snip]
> >> 
> >> > +       };
> >> > +
> >> > +       vcc_18: vcc18-regulator {
> >> > +               compatible = "regulator-fixed";
> >> > +               regulator-name = "vcc_18";
> >> > +               regulator-min-microvolt = <1800000>;
> >> > +               regulator-max-microvolt = <1800000>;
> >> > +               regulator-always-on;
> >> > +               regulator-boot-on;
> >> > +               vin-supply = <&vcc_sys>;
> >> > +       };
> >> > +
> >> > +       /* supplies both host and otg */
> >> > +       vcc_host: vcc-host-regulator {
> >> > +               compatible = "regulator-fixed";
> >> > +               gpio = <&gpio0 4 GPIO_ACTIVE_LOW>;
> >> > +               pinctrl-names = "default";
> >> > +               pinctrl-0 = <&host_vbus_drv>;
> >> > +               regulator-name = "vcc_host";
> >> > +               regulator-always-on;
> >> > +               regulator-boot-on;
> >> > +               vin-supply = <&vcc_sys>;
> >> > +       };
> >> > +
> >> > +       vccio_sd: vcc-io-sd-regulator {
> >> > +               regulator-name= "vccio_sd";
> >> > +               gpio = <&gpio0 9 GPIO_ACTIVE_LOW>;
> >> > +               regulator-min-microvolt = <3300000>;
> >> > +               regulator-max-microvolt = <3300000>;

that seems to be missing essential regulator properties
like a compatible.

> >> > +       };
> >> > +
> >> > +       vcc_sd: vcc-sd-regulator {
> >> > +               compatible = "regulator-fixed";
> >> > +               regulator-name = "vcc_sd";
> >> > +               gpio = <&gpio3 11 GPIO_ACTIVE_LOW>;
> >> > +               regulator-min-microvolt = <3300000>;
> >> > +               regulator-max-microvolt = <3300000>;
> >> > +               regulator-always-on;
> >> > +               regulator-boot-on;
> >> > +               vin-supply = <&vcc_io>;
> >> > +       };
> >> > +
> >> > +       vcc_io: vcc-io-regulator {
> >> > +               compatible = "regulator-fixed";
> >> > +               regulator-name = "vcc_io";
> >> > +               regulator-min-microvolt = <3300000>;
> >> > +               regulator-max-microvolt = <3300000>;
> >> > +               regulator-always-on;
> >> > +               regulator-boot-on;
> >> > +               vin-supply = <&vcc_sys>;
> >> > +       };
> >> > +
> >> > +       vcc_lan: vcc-lan-regulator {
> >> > +               compatible = "regulator-fixed";
> >> > +               regulator-name = "vcc_lan";
> >> > +               regulator-min-microvolt = <3300000>;
> >> > +               regulator-max-microvolt = <3300000>;
> >> > +               regulator-always-on;
> >> > +               regulator-boot-on;
> >> > +               vin-supply = <&vcc_io>;
> >> > +       };
> >> > +
> >> > +       vcc_sys: vcc-sys-regulator {
> >> > +               compatible = "regulator-fixed";
> >> > +               regulator-name = "vcc_sys";
> >> > +               regulator-min-microvolt = <5000000>;
> >> > +               regulator-max-microvolt = <5000000>;
> >> > +               regulator-always-on;
> >> > +               regulator-boot-on;
> >> > +       };
> >> > +
> >> > +       vccio_wl: vccio-wl-regulator {
> >> > +               compatible = "regulator-fixed";
> >> > +               regulator-name = "vccio_wl";
> >> > +               regulator-min-microvolt = <3300000>;
> >> > +               regulator-max-microvolt = <3300000>;
> >> > +               regulator-always-on;
> >> > +               regulator-boot-on;
> >> > +               vin-supply = <&vcc_io>;
> >> > +       };
> >> > +
> >> > +       vdd_10: vdd-10-regulator {
> >> > +               compatible = "regulator-fixed";
> >> > +               regulator-name = "vdd_10";
> >> > +               regulator-min-microvolt = <1000000>;
> >> > +               regulator-max-microvolt = <1000000>;
> >> > +               regulator-always-on;
> >> > +               regulator-boot-on;
> >> > +               vin-supply = <&vcc_sys>;
> >> > +       };
> >> > +};
> >> 
> >> There is only one regulator that is not marked as always-on. This will
> >> prevent the regulator subsystem to disable unused regulators. Do you
> >> really need all of them to be always-on?
> > 
> > I do believe the regulators should be more or less correct. Sadly there
> > are no real schematics for that specific device available, but the
> > reference design for rk3368-based tv-boxes uses a hirarchy of
> > hard-wired individual regulators without any ability to control for
> > most of them [aka always on].
> > 
> > And due to the r68 not sporting any different real pmic, I'd believe the
> > Orion will most likely also follow that scheme.
> 
> I see, thanks a lot for the clarification. But then in that case I
> wonder what's the point of defining the gpio property in the fixed
> regulator device nodes?

the ones with gpio properties actually can be turned off - here they are the 
different supplies for usb and sd-card only, so leaf-regulators without 
further children.


Heiko


From trini at konsulko.com  Fri Jan 15 06:42:24 2016
From: trini at konsulko.com (Tom Rini)
Date: Fri, 15 Jan 2016 09:42:24 -0500
Subject: [U-Boot] [PATCH v4 5/6] rockchip: kylin: Enable boot with
 android boot image
In-Reply-To: <5698577B.8040209@rock-chips.com>
References: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
 <1452675200-15941-6-git-send-email-jeffy.chen@rock-chips.com>
 <20160113152811.GC3359@bill-the-cat>
 <56970886.2040009@rock-chips.com>
 <20160114162227.GY3359@bill-the-cat>
 <569842F2.4080808@rock-chips.com>
 <20160115005950.GC3359@bill-the-cat>
 <5698577B.8040209@rock-chips.com>
Message-ID: <20160115144224.GR3359@bill-the-cat>

On Fri, Jan 15, 2016 at 10:20:43AM +0800, Jeffy Chen wrote:
> Hi Tom,
> 
> On 2016-1-15 8:59, Tom Rini wrote:
> >On Fri, Jan 15, 2016 at 08:53:06AM +0800, Jeffy Chen wrote:
> >>Hi Tom,
> >>
> >>On 2016-1-15 0:22, Tom Rini wrote:
> >>>On Thu, Jan 14, 2016 at 10:31:34AM +0800, Jeffy Chen wrote:
> >>>>Hi Tom,
> >>>>
> >>>>On 2016-1-13 23:28, Tom Rini wrote:
> >>>>>On Wed, Jan 13, 2016 at 04:53:19PM +0800, Jeffy Chen wrote:
> >>>>>
> >>>>>>The android kernel is using appended dtb by default, and store
> >>>>>>ramdisk right after kernel & dtb.
> >>>>>>So we needs to relocate ramdisk, and use atags to pass params.
> >>>>>>
> >>>>>>Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
> >>>>>>Acked-by: Simon Glass <sjg at chromium.org>
> >>>>>>---
> >>>>>>
> >>>>>>Changes in v4: None
> >>>>>>Changes in v3: None
> >>>>>>Changes in v2: None
> >>>>>>
> >>>>>>  include/configs/kylin_rk3036.h | 23 +++++++++++++++++++++++
> >>>>>>  1 file changed, 23 insertions(+)
> >>>>>>
> >>>>>>diff --git a/include/configs/kylin_rk3036.h b/include/configs/kylin_rk3036.h
> >>>>>>index b750b26..49997ec 100644
> >>>>>>--- a/include/configs/kylin_rk3036.h
> >>>>>>+++ b/include/configs/kylin_rk3036.h
> >>>>>>@@ -35,6 +35,29 @@
> >>>>>>  #undef CONFIG_EXTRA_ENV_SETTINGS
> >>>>>>  #define CONFIG_EXTRA_ENV_SETTINGS \
> >>>>>>  	"partitions=" PARTS_DEFAULT \
> >>>>>>+	"mmcdev=0\0" \
> >>>>>>+	"mmcpart=5\0" \
> >>>>>>+	"loadaddr=" __stringify(CONFIG_SYS_LOAD_ADDR) "\0" \
> >>>>>>+
> >>>>>>+#define CONFIG_ANDROID_BOOT_IMAGE
> >>>>>>+#define CONFIG_SYS_BOOT_RAMDISK_HIGH
> >>>>>This should already be set.
> >>>>Right, i'll remove it...
> >>>>>>+#define CONFIG_SYS_HUSH_PARSER
> >>>>>>+
> >>>>>>+#undef CONFIG_BOOTCOMMAND
> >>>>>>+#define CONFIG_BOOTCOMMAND \
> >>>>>>+	"mmc dev ${mmcdev}; if mmc rescan; then " \
> >>>>>>+		"part start mmc ${mmcdev} ${mmcpart} boot_start;" \
> >>>>>>+		"part size mmc ${mmcdev} ${mmcpart} boot_size;" \
> >>>>>>+		"mmc read ${loadaddr} ${boot_start} ${boot_size};" \
> >>>>>>+		"bootm start ${loadaddr}; bootm ramdisk;" \
> >>>>>>+		"bootm prep; bootm go;" \
> >>>>>>+	"fi;" \
> >>>>>>+
> >>>>>>+/* Enable atags */
> >>>>>>+#define CONFIG_SYS_BOOTPARAMS_LEN	(64*1024)
> >>>>>>+#define CONFIG_INITRD_TAG
> >>>>>>+#define CONFIG_SETUP_MEMORY_TAGS
> >>>>>>+#define CONFIG_CMDLINE_TAG
> >>>>>But I'm confused as to what exactly is going on here.  Appended dtb is
> >>>>>not the same as ATAGS.  And you shouldn't need to split up bootm like
> >>>>>that.  Can you please explain a bit more?  Thanks!
> >>>>The u-boot will pass atags to kernel, and kernel will merge those
> >>>>atags into the appended dtb(fdt).
> >>>>
> >>>>The default bootm flow would not pass ramdisk state, but we need it,
> >>>>so we should add this state into default flow, or just use split
> >>>>bootm cmds :)
> >>>That seems very strange.  Is the ramdisk concatenated with the kernel
> >>>and dtb as well (and that's why bootm ramdisk somehow finds it but
> >>>normal bootm doesn't as you aren't passing in a ramdisk address) ?
> >>Yes, the ramdisk concatenated with the kernel and dtb as
> >>well(u-boot/include/android_image.h: struct andr_img_hdr).
> >>
> >>And the normal bootm cmd would find it by parsing andr_img_hdr struct.
> >>But we still need bootm ramdisk state, because it will call
> >>boot_ramdisk_high to relocate ramdisk area :)
> >>
> >>I found if not relocate it to somewhere else, it would be corrupted
> >>after kernel's decompressing(during update fdt area).
> >So 'bootm $loadaddr' of an Android image sees, but does not relocate the
> >ramdisk that is included in the image, but bootm ramdisk does?  That
> >sounds like a bug in the regular bootm handling.
> Yep, the default bootm flow would not contain ramdisk relocate state:
> 
> vi common/cmd_bootm.c
>         return do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START |
>                 BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER |
>                 BOOTM_STATE_LOADOS |
> #if defined(CONFIG_PPC) || defined(CONFIG_MIPS)
>                 BOOTM_STATE_OS_CMDLINE |
> #endif
>                 BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |
>                 BOOTM_STATE_OS_GO, &images, 1);
> 
> But i'm not sure if it's ok to add it here...

Actually, maybe so.  This made me do some digging again back into
Simon's series that refactored the bootm code.  In the long long ago,
nearly every architecture would call bootm_ramdisk_high to relocate the
ramdisk and set the environment (and poke the DT).  But it wasn't always
clear when / why it would do this.  And it got consolidated.  And here's
where it's tricky.  It looks correct today, still, to make sure that we
relocate the ramdisk.  image_setup_linux() checks
IMAGE_ENABLE_RAMDISK_HIGH which is toggled by
CONFIG_SYS_BOOT_RAMDISK_HIGH.  But for example, MIPS whacked _back_ in a
local call to make sure the ramdisk was being relocated because it
wasn't back in 2014 into boot_prep_linux.  It may even be related to
some of the initrd rated things Masahiro has found recently.  So
something is wrong down in this core code.  Jeffy, can you try and debug
this a bit since you have a failing case in front of you?  Otherwise
I'll put it on my TODO list.  Thanks!

-- 
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160115/daec7b95/attachment.sig>

From javier at dowhile0.org  Fri Jan 15 07:31:22 2016
From: javier at dowhile0.org (Javier Martinez Canillas)
Date: Fri, 15 Jan 2016 12:31:22 -0300
Subject: [PATCH 2/2] arm64: dts: rockchip: Add basic support for orion-r68
In-Reply-To: <1804889.MQWMfNjko2@phil>
References: <1452863192-29231-1-git-send-email-matthias.bgg@gmail.com>
 <2174023.jkjdoEECkq@phil>
 <CABxcv=nkry9PUg5pp+6jKjYb_G=iEfMa7+evZOrUkifDS8h5BA@mail.gmail.com>
 <1804889.MQWMfNjko2@phil>
Message-ID: <CABxcv=nDs2jw0-zZK7jBmtLDEmvAPUNNMST0XBK75OyUGDXmRg@mail.gmail.com>

Hello Heiko,

On Fri, Jan 15, 2016 at 11:26 AM, Heiko Stuebner <heiko at sntech.de> wrote:
> Am Freitag, 15. Januar 2016, 11:22:52 schrieb Javier Martinez Canillas:
>> Hello Heiko,
>>
>> On Fri, Jan 15, 2016 at 11:03 AM, Heiko Stuebner <heiko at sntech.de> wrote:
>> > Hi Javier,
>> >
>> > Am Freitag, 15. Januar 2016, 10:28:44 schrieb Javier Martinez Canillas:
>> >> This is not a complete review but I just wanted to comment on two
>> >
>> >> things that I noticed:
>> > same for me for now :-)
>> >
>> >> On Fri, Jan 15, 2016 at 10:06 AM, Matthias Brugger
>> >> <matthias.bgg at gmail.com> wrote:
>> >>
>> >> [snip]
>> >>
>> >> > +       };
>> >> > +
>> >> > +       vcc_18: vcc18-regulator {
>> >> > +               compatible = "regulator-fixed";
>> >> > +               regulator-name = "vcc_18";
>> >> > +               regulator-min-microvolt = <1800000>;
>> >> > +               regulator-max-microvolt = <1800000>;
>> >> > +               regulator-always-on;
>> >> > +               regulator-boot-on;
>> >> > +               vin-supply = <&vcc_sys>;
>> >> > +       };
>> >> > +
>> >> > +       /* supplies both host and otg */
>> >> > +       vcc_host: vcc-host-regulator {
>> >> > +               compatible = "regulator-fixed";
>> >> > +               gpio = <&gpio0 4 GPIO_ACTIVE_LOW>;
>> >> > +               pinctrl-names = "default";
>> >> > +               pinctrl-0 = <&host_vbus_drv>;
>> >> > +               regulator-name = "vcc_host";
>> >> > +               regulator-always-on;
>> >> > +               regulator-boot-on;
>> >> > +               vin-supply = <&vcc_sys>;
>> >> > +       };
>> >> > +
>> >> > +       vccio_sd: vcc-io-sd-regulator {
>> >> > +               regulator-name= "vccio_sd";
>> >> > +               gpio = <&gpio0 9 GPIO_ACTIVE_LOW>;
>> >> > +               regulator-min-microvolt = <3300000>;
>> >> > +               regulator-max-microvolt = <3300000>;
>
> that seems to be missing essential regulator properties
> like a compatible.
>

Oh right, I missed that when first reading the patch.

>> >> > +       };
>> >> > +
>> >> > +       vcc_sd: vcc-sd-regulator {
>> >> > +               compatible = "regulator-fixed";
>> >> > +               regulator-name = "vcc_sd";
>> >> > +               gpio = <&gpio3 11 GPIO_ACTIVE_LOW>;
>> >> > +               regulator-min-microvolt = <3300000>;
>> >> > +               regulator-max-microvolt = <3300000>;
>> >> > +               regulator-always-on;
>> >> > +               regulator-boot-on;
>> >> > +               vin-supply = <&vcc_io>;
>> >> > +       };
>> >> > +
>> >> > +       vcc_io: vcc-io-regulator {
>> >> > +               compatible = "regulator-fixed";
>> >> > +               regulator-name = "vcc_io";
>> >> > +               regulator-min-microvolt = <3300000>;
>> >> > +               regulator-max-microvolt = <3300000>;
>> >> > +               regulator-always-on;
>> >> > +               regulator-boot-on;
>> >> > +               vin-supply = <&vcc_sys>;
>> >> > +       };
>> >> > +
>> >> > +       vcc_lan: vcc-lan-regulator {
>> >> > +               compatible = "regulator-fixed";
>> >> > +               regulator-name = "vcc_lan";
>> >> > +               regulator-min-microvolt = <3300000>;
>> >> > +               regulator-max-microvolt = <3300000>;
>> >> > +               regulator-always-on;
>> >> > +               regulator-boot-on;
>> >> > +               vin-supply = <&vcc_io>;
>> >> > +       };
>> >> > +
>> >> > +       vcc_sys: vcc-sys-regulator {
>> >> > +               compatible = "regulator-fixed";
>> >> > +               regulator-name = "vcc_sys";
>> >> > +               regulator-min-microvolt = <5000000>;
>> >> > +               regulator-max-microvolt = <5000000>;
>> >> > +               regulator-always-on;
>> >> > +               regulator-boot-on;
>> >> > +       };
>> >> > +
>> >> > +       vccio_wl: vccio-wl-regulator {
>> >> > +               compatible = "regulator-fixed";
>> >> > +               regulator-name = "vccio_wl";
>> >> > +               regulator-min-microvolt = <3300000>;
>> >> > +               regulator-max-microvolt = <3300000>;
>> >> > +               regulator-always-on;
>> >> > +               regulator-boot-on;
>> >> > +               vin-supply = <&vcc_io>;
>> >> > +       };
>> >> > +
>> >> > +       vdd_10: vdd-10-regulator {
>> >> > +               compatible = "regulator-fixed";
>> >> > +               regulator-name = "vdd_10";
>> >> > +               regulator-min-microvolt = <1000000>;
>> >> > +               regulator-max-microvolt = <1000000>;
>> >> > +               regulator-always-on;
>> >> > +               regulator-boot-on;
>> >> > +               vin-supply = <&vcc_sys>;
>> >> > +       };
>> >> > +};
>> >>
>> >> There is only one regulator that is not marked as always-on. This will
>> >> prevent the regulator subsystem to disable unused regulators. Do you
>> >> really need all of them to be always-on?
>> >
>> > I do believe the regulators should be more or less correct. Sadly there
>> > are no real schematics for that specific device available, but the
>> > reference design for rk3368-based tv-boxes uses a hirarchy of
>> > hard-wired individual regulators without any ability to control for
>> > most of them [aka always on].
>> >
>> > And due to the r68 not sporting any different real pmic, I'd believe the
>> > Orion will most likely also follow that scheme.
>>
>> I see, thanks a lot for the clarification. But then in that case I
>> wonder what's the point of defining the gpio property in the fixed
>> regulator device nodes?
>
> the ones with gpio properties actually can be turned off - here they are the
> different supplies for usb and sd-card only, so leaf-regulators without
> further children.
>

Ok, sorry I misunderstood and thought you were referring to all
regulators. I of course agree that regulators that can't be turned of
should be marked as always-on. I just asked this to be revisit and
only mark as always-on the ones that really should be.

>
> Heiko

Best regards,
Javier


From daniel.schwierzeck at gmail.com  Fri Jan 15 07:42:21 2016
From: daniel.schwierzeck at gmail.com (Daniel Schwierzeck)
Date: Fri, 15 Jan 2016 16:42:21 +0100
Subject: [U-Boot] [PATCH v4 5/6] rockchip: kylin: Enable boot with
 android boot image
In-Reply-To: <20160115144224.GR3359@bill-the-cat>
References: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
 <1452675200-15941-6-git-send-email-jeffy.chen@rock-chips.com>
 <20160113152811.GC3359@bill-the-cat> <56970886.2040009@rock-chips.com>
 <20160114162227.GY3359@bill-the-cat> <569842F2.4080808@rock-chips.com>
 <20160115005950.GC3359@bill-the-cat> <5698577B.8040209@rock-chips.com>
 <20160115144224.GR3359@bill-the-cat>
Message-ID: <1452872541.3905.21.camel@gmail.com>

Am Freitag, den 15.01.2016, 09:42 -0500 schrieb Tom Rini:
> On Fri, Jan 15, 2016 at 10:20:43AM +0800, Jeffy Chen wrote:
> > Hi Tom,
> > 
> > On 2016-1-15 8:59, Tom Rini wrote:
> > > On Fri, Jan 15, 2016 at 08:53:06AM +0800, Jeffy Chen wrote:
> > > > Hi Tom,
> > > > 
> > > > On 2016-1-15 0:22, Tom Rini wrote:
> > > > > On Thu, Jan 14, 2016 at 10:31:34AM +0800, Jeffy Chen wrote:
> > > > > > Hi Tom,
> > > > > > 
> > > > > > On 2016-1-13 23:28, Tom Rini wrote:
> > > > > > > On Wed, Jan 13, 2016 at 04:53:19PM +0800, Jeffy Chen
> > > > > > > wrote:
> > > > > > > 
> > > > > > > > The android kernel is using appended dtb by default,
> > > > > > > > and store
> > > > > > > > ramdisk right after kernel & dtb.
> > > > > > > > So we needs to relocate ramdisk, and use atags to pass
> > > > > > > > params.
> > > > > > > > 
> > > > > > > > Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
> > > > > > > > Acked-by: Simon Glass <sjg at chromium.org>
> > > > > > > > ---
> > > > > > > > 
> > > > > > > > Changes in v4: None
> > > > > > > > Changes in v3: None
> > > > > > > > Changes in v2: None
> > > > > > > > 
> > > > > > > >  include/configs/kylin_rk3036.h | 23
> > > > > > > > +++++++++++++++++++++++
> > > > > > > >  1 file changed, 23 insertions(+)
> > > > > > > > 
> > > > > > > > diff --git a/include/configs/kylin_rk3036.h
> > > > > > > > b/include/configs/kylin_rk3036.h
> > > > > > > > index b750b26..49997ec 100644
> > > > > > > > --- a/include/configs/kylin_rk3036.h
> > > > > > > > +++ b/include/configs/kylin_rk3036.h
> > > > > > > > @@ -35,6 +35,29 @@
> > > > > > > >  #undef CONFIG_EXTRA_ENV_SETTINGS
> > > > > > > >  #define CONFIG_EXTRA_ENV_SETTINGS \
> > > > > > > >  	"partitions=" PARTS_DEFAULT \
> > > > > > > > +	"mmcdev=0\0" \
> > > > > > > > +	"mmcpart=5\0" \
> > > > > > > > +	"loadaddr=" __stringify(CONFIG_SYS_LOAD_ADDR)
> > > > > > > > "\0" \
> > > > > > > > +
> > > > > > > > +#define CONFIG_ANDROID_BOOT_IMAGE
> > > > > > > > +#define CONFIG_SYS_BOOT_RAMDISK_HIGH
> > > > > > > This should already be set.
> > > > > > Right, i'll remove it...
> > > > > > > > +#define CONFIG_SYS_HUSH_PARSER
> > > > > > > > +
> > > > > > > > +#undef CONFIG_BOOTCOMMAND
> > > > > > > > +#define CONFIG_BOOTCOMMAND \
> > > > > > > > +	"mmc dev ${mmcdev}; if mmc rescan; then " \
> > > > > > > > +		"part start mmc ${mmcdev} ${mmcpart}
> > > > > > > > boot_start;" \
> > > > > > > > +		"part size mmc ${mmcdev} ${mmcpart}
> > > > > > > > boot_size;" \
> > > > > > > > +		"mmc read ${loadaddr} ${boot_start}
> > > > > > > > ${boot_size};" \
> > > > > > > > +		"bootm start ${loadaddr}; bootm
> > > > > > > > ramdisk;" \
> > > > > > > > +		"bootm prep; bootm go;" \
> > > > > > > > +	"fi;" \
> > > > > > > > +
> > > > > > > > +/* Enable atags */
> > > > > > > > +#define CONFIG_SYS_BOOTPARAMS_LEN	(64*1024)
> > > > > > > > +#define CONFIG_INITRD_TAG
> > > > > > > > +#define CONFIG_SETUP_MEMORY_TAGS
> > > > > > > > +#define CONFIG_CMDLINE_TAG
> > > > > > > But I'm confused as to what exactly is going on here. 
> > > > > > >  Appended dtb is
> > > > > > > not the same as ATAGS.  And you shouldn't need to split
> > > > > > > up bootm like
> > > > > > > that.  Can you please explain a bit more?  Thanks!
> > > > > > The u-boot will pass atags to kernel, and kernel will merge
> > > > > > those
> > > > > > atags into the appended dtb(fdt).
> > > > > > 
> > > > > > The default bootm flow would not pass ramdisk state, but we
> > > > > > need it,
> > > > > > so we should add this state into default flow, or just use
> > > > > > split
> > > > > > bootm cmds :)
> > > > > That seems very strange.  Is the ramdisk concatenated with
> > > > > the kernel
> > > > > and dtb as well (and that's why bootm ramdisk somehow finds
> > > > > it but
> > > > > normal bootm doesn't as you aren't passing in a ramdisk
> > > > > address) ?
> > > > Yes, the ramdisk concatenated with the kernel and dtb as
> > > > well(u-boot/include/android_image.h: struct andr_img_hdr).
> > > > 
> > > > And the normal bootm cmd would find it by parsing andr_img_hdr
> > > > struct.
> > > > But we still need bootm ramdisk state, because it will call
> > > > boot_ramdisk_high to relocate ramdisk area :)
> > > > 
> > > > I found if not relocate it to somewhere else, it would be
> > > > corrupted
> > > > after kernel's decompressing(during update fdt area).
> > > So 'bootm $loadaddr' of an Android image sees, but does not
> > > relocate the
> > > ramdisk that is included in the image, but bootm ramdisk does? 
> > >  That
> > > sounds like a bug in the regular bootm handling.
> > Yep, the default bootm flow would not contain ramdisk relocate
> > state:
> > 
> > vi common/cmd_bootm.c
> >         return do_bootm_states(cmdtp, flag, argc, argv,
> > BOOTM_STATE_START |
> >                 BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER |
> >                 BOOTM_STATE_LOADOS |
> > #if defined(CONFIG_PPC) || defined(CONFIG_MIPS)
> >                 BOOTM_STATE_OS_CMDLINE |
> > #endif
> >                 BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |
> >                 BOOTM_STATE_OS_GO, &images, 1);
> > 
> > But i'm not sure if it's ok to add it here...
> 
> Actually, maybe so.  This made me do some digging again back into
> Simon's series that refactored the bootm code.  In the long long ago,
> nearly every architecture would call bootm_ramdisk_high to relocate
> the
> ramdisk and set the environment (and poke the DT).  But it wasn't
> always
> clear when / why it would do this.  And it got consolidated.  And
> here's
> where it's tricky.  It looks correct today, still, to make sure that
> we
> relocate the ramdisk.  image_setup_linux() checks
> IMAGE_ENABLE_RAMDISK_HIGH which is toggled by
> CONFIG_SYS_BOOT_RAMDISK_HIGH.  But for example, MIPS whacked _back_
> in a
> local call to make sure the ramdisk was being relocated because it
> wasn't back in 2014 into boot_prep_linux.  It may even be related to
> some of the initrd rated things Masahiro has found recently.  So
> something is wrong down in this core code.  

one problem is the different behaviour of bootm between legacy uImages
and FIT uImages. In case of legacy uImages, the core code automatically
relocates initramfs and DTB. In case of FIT uImages, the arch-specific
do_bootm_linux() still has to do that. This was only somehow addressed
by adding the helper function image_setup_linux() and calling it from
do_bootm_linux(). But you can't use that function with legacy uImages,
because initramfs and DTB would be relocated twice because the states
BOOTM_STATE_RAMDISK and BOOTM_STATE_FDT are not checked. Because of
that image_setup_linux() also leads to a different behaviour when
calling bootm as a single command and calling bootm with sub-steps.

So the current bootm implementation on MIPS is the only way for me to
make bootm working with all possible combinations of legacy/FIT uImage,
with/without initramfs, with/without DTB and single/multiple bootm
calls. I suspect that the current implementation on ARM does not work
with all possible combinations.

> Jeffy, can you try and debug
> this a bit since you have a failing case in front of you?  Otherwise
> I'll put it on my TODO list.  Thanks!
> 

Some ideas for a possible refactoring of the bootm core code:
- remove arch-specific do_bootm_linux() and image_setup_linux()
- rename functions like boot_prep_linux() and boot_jump_linux() to
arch_boot_prep_linux() and arch_boot_jump_linux() and declare them as
__weak and add a default implementation, the arch code can overwrite
them
- refactor the core code to call those functions at the appropriate
locations

-- 
- Daniel



From broonie at kernel.org  Fri Jan 15 09:46:23 2016
From: broonie at kernel.org (Mark Brown)
Date: Fri, 15 Jan 2016 17:46:23 +0000
Subject: [PATCH v3 3/9] ASoC: rockchip: i2s: add support for grabbing
 output clock to codec
In-Reply-To: <1452865796-23527-4-git-send-email-wxt@rock-chips.com>
References: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
 <1452865796-23527-4-git-send-email-wxt@rock-chips.com>
Message-ID: <20160115174623.GZ6588@sirena.org.uk>

On Fri, Jan 15, 2016 at 09:49:50PM +0800, Caesar Wang wrote:

> We need to claim the clock which is driving the codec so that when we
> enable clock gating, we continue to clock the codec when needed.
> I make this an optional clock since there might be some applications
> where we don't need it but can still use the I2S block.

> - As the perious discuss on https://patchwork.kernel.org/patch/5427131/,
>   I think Mark likes do it in codec driver, I have to say I agree this
>   patch in here since that's the i2s block output. I don't know if the
>   Mark has changed his mind.

If the I2S block is providing a clock to the CODEC then that's what the
software should do so that the CODEC can gate and ungate the clock as
required.  This patch has the I2S block using a clock, not providing
one.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160115/6c8b1525/attachment.sig>

From john.stultz at linaro.org  Fri Jan 15 12:27:57 2016
From: john.stultz at linaro.org (John Stultz)
Date: Fri, 15 Jan 2016 12:27:57 -0800
Subject: [PATCH v2 2/4] power: reset: add reboot mode driver
In-Reply-To: <1452598319-8324-1-git-send-email-andy.yan@rock-chips.com>
References: <1452598029-8222-1-git-send-email-andy.yan@rock-chips.com>
 <1452598319-8324-1-git-send-email-andy.yan@rock-chips.com>
Message-ID: <CALAqxLVNUG_cVbavXC34sNPv68jbMghti31+PrgQ7mpPngXh6g@mail.gmail.com>

On Tue, Jan 12, 2016 at 3:31 AM, Andy Yan <andy.yan at rock-chips.com> wrote:
> This driver parse the reboot commands like "reboot loader"
> and "reboot recovery" to get a boot mode described in the
> device tree , then call the write interfae to store the boot
> mode in some persistent storage  like special register or ram,
> which can be read by the bootloader after system reboot, then
> the bootloader can take different action according to the mode
> stored.
>
> This is commonly used on Android based devices, which in order
> to reboot the device into fastboot or recovery mode.
>
> Signed-off-by: Andy Yan <andy.yan at rock-chips.com>


Hey Andy!
  Thanks for keeping this work going! I've just successfully reworked
my Nexus7 tree to use your implementation (using the syscon version,
setting up a syscon for IMEM as Bjorn had requested earlier).

All is working well so far!

The one thing I was working on supporting with my own version that
seems to be missing here are for devices that use string based codes,
rather then magic numbers.

This was mostly a theoretical issue. I think the Galaxy Nexus used it,
and when I was looking at some of the HTC devices, they support a text
based reason along with the magic code, but at least in some
implementations the text mode isn't used, so I suspect there its just
for extra debugging.  So this may not be critical to solve until
someone tries to add support for such a device.

Anyway, I'm going to look at porting this to the HiKey board next
(which just uses reserved ram, not syscon), so I'll try to do a an
SRAM driver implementaiton to see how that goes.

thanks!
-john


From sonnyrao at chromium.org  Fri Jan 15 13:48:04 2016
From: sonnyrao at chromium.org (Sonny Rao)
Date: Fri, 15 Jan 2016 13:48:04 -0800
Subject: [PATCH v3 3/9] ASoC: rockchip: i2s: add support for grabbing
 output clock to codec
In-Reply-To: <20160115174623.GZ6588@sirena.org.uk>
References: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
 <1452865796-23527-4-git-send-email-wxt@rock-chips.com>
 <20160115174623.GZ6588@sirena.org.uk>
Message-ID: <CAPz6YkWt-iXS6P4QBBoq3v3i7ZAGGu6=_9AHw4qgevEwDwbdTA@mail.gmail.com>

On Fri, Jan 15, 2016 at 9:46 AM, Mark Brown <broonie at kernel.org> wrote:
> On Fri, Jan 15, 2016 at 09:49:50PM +0800, Caesar Wang wrote:
>
>> We need to claim the clock which is driving the codec so that when we
>> enable clock gating, we continue to clock the codec when needed.
>> I make this an optional clock since there might be some applications
>> where we don't need it but can still use the I2S block.
>
>> - As the perious discuss on https://patchwork.kernel.org/patch/5427131/,
>>   I think Mark likes do it in codec driver, I have to say I agree this
>>   patch in here since that's the i2s block output. I don't know if the
>>   Mark has changed his mind.
>
> If the I2S block is providing a clock to the CODEC then that's what the
> software should do so that the CODEC can gate and ungate the clock as
> required.  This patch has the I2S block using a clock, not providing
> one.

>From my read of the clock diagram for RK3288  there is a single clock
signal (labeled "clk_i2s0") that comes out of a fractional divider,
and it is split such that one path gets sent to the I2S block and the
second path is sent to a mux after which that signal is sent to an
external pin that goes to the codec.

There are separate clock gates for the two paths: one for the I2S
block and one after that mux before the external pin.

I'm not sure if it's being modeled that way in the Linux code or not,
but at least physically I don't think this clock signal actually goes
through the I2S block before being sent to the codec.

Does that help clarify?


From john.stultz at linaro.org  Fri Jan 15 14:41:55 2016
From: john.stultz at linaro.org (John Stultz)
Date: Fri, 15 Jan 2016 14:41:55 -0800
Subject: [PATCH v2 1/4] dt-bindings: power: reset: add document for
 reboot-mode driver
In-Reply-To: <1452598189-8272-1-git-send-email-andy.yan@rock-chips.com>
References: <1452598029-8222-1-git-send-email-andy.yan@rock-chips.com>
 <1452598189-8272-1-git-send-email-andy.yan@rock-chips.com>
Message-ID: <CALAqxLUxh3=LhoHxqiRm_5L4G6m1Vctp=aUg+9_uAtLkFwW9bw@mail.gmail.com>

On Tue, Jan 12, 2016 at 3:29 AM, Andy Yan <andy.yan at rock-chips.com> wrote:
> add device tree binding document for reboot-mode driver
>
> Signed-off-by: Andy Yan <andy.yan at rock-chips.com>
>
> ---
>
> Changes in v2: None
> Changes in v1: None
>
>  .../bindings/power/reset/reboot-mode.txt           | 41 +++++++++++++++++
>  .../bindings/power/reset/syscon-reboot-mode.txt    | 52 ++++++++++++++++++++++
>  2 files changed, 93 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/power/reset/reboot-mode.txt
>  create mode 100644 Documentation/devicetree/bindings/power/reset/syscon-reboot-mode.txt
>
> diff --git a/Documentation/devicetree/bindings/power/reset/reboot-mode.txt b/Documentation/devicetree/bindings/power/reset/reboot-mode.txt
> new file mode 100644
> index 0000000..81d9f66
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/power/reset/reboot-mode.txt
> @@ -0,0 +1,41 @@
> +Generic reboot mode core map driver
> +
> +This driver get reboot mode arguments and call the write
> +interface to stores the magic value in special register
> +or ram . Then the bootloader can read it and take different
> +action according to the argument stored.
> +
> +Required properties:
> +- compatible: only support "syscon-reboot-mode" now.
> +
> +Each mode is represented as a sub-node of reboot_mode:
> +
> +Subnode required properties:
> +- linux,mode: reboot mode command,such as "loader", "recovery", "fastboot".
> +- loader,magic: magic number for the mode, this is vendor specific.
> +
> +Example:
> +       reboot_mode {
> +               compatible = "syscon-reboot-mode";
> +               offset = <0x40>;
> +
> +               loader {
> +                       linux,mode = "loader";
> +                       loader,magic = <BOOT_LOADER>;
> +               };
> +
> +               maskrom {
> +                       linux,mode = "maskrom";
> +                       loader,magic = <BOOT_MASKROM>;
> +               };
> +
> +               recovery {
> +                       linux,mode = "recovery";
> +                       loader,magic = <BOOT_RECOVERY>;
> +               };
> +
> +               fastboot {
> +                       linux,mode = "fastboot";
> +                       loader,magic = <BOOT_FASTBOOT>;
> +               };
> +        };


So one minor thought here. While the commands are somewhat vendor
specific, would it be a good idea for the example commands to match
the common commands on Android devices?  For example, usually
"bootloader" is what gets you into fastboot mode on nexus devices.

thanks
-john


From sjg at chromium.org  Fri Jan 15 17:18:17 2016
From: sjg at chromium.org (Simon Glass)
Date: Fri, 15 Jan 2016 18:18:17 -0700
Subject: [U-Boot] [PATCH v4 5/6] rockchip: kylin: Enable boot with android
 boot image
In-Reply-To: <1452872541.3905.21.camel@gmail.com>
References: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
 <1452675200-15941-6-git-send-email-jeffy.chen@rock-chips.com>
 <20160113152811.GC3359@bill-the-cat> <56970886.2040009@rock-chips.com>
 <20160114162227.GY3359@bill-the-cat> <569842F2.4080808@rock-chips.com>
 <20160115005950.GC3359@bill-the-cat> <5698577B.8040209@rock-chips.com>
 <20160115144224.GR3359@bill-the-cat> <1452872541.3905.21.camel@gmail.com>
Message-ID: <CAPnjgZ1SUJDVPFVXgJjEYgom7tEdaORUAjQ8QfQ8jCpw=NtcJQ@mail.gmail.com>

Hi,

On 15 January 2016 at 08:42, Daniel Schwierzeck
<daniel.schwierzeck at gmail.com> wrote:
> Am Freitag, den 15.01.2016, 09:42 -0500 schrieb Tom Rini:
>> On Fri, Jan 15, 2016 at 10:20:43AM +0800, Jeffy Chen wrote:
>> > Hi Tom,
>> >
>> > On 2016-1-15 8:59, Tom Rini wrote:
>> > > On Fri, Jan 15, 2016 at 08:53:06AM +0800, Jeffy Chen wrote:
>> > > > Hi Tom,
>> > > >
>> > > > On 2016-1-15 0:22, Tom Rini wrote:
>> > > > > On Thu, Jan 14, 2016 at 10:31:34AM +0800, Jeffy Chen wrote:
>> > > > > > Hi Tom,
>> > > > > >
>> > > > > > On 2016-1-13 23:28, Tom Rini wrote:
>> > > > > > > On Wed, Jan 13, 2016 at 04:53:19PM +0800, Jeffy Chen
>> > > > > > > wrote:
>> > > > > > >
>> > > > > > > > The android kernel is using appended dtb by default,
>> > > > > > > > and store
>> > > > > > > > ramdisk right after kernel & dtb.
>> > > > > > > > So we needs to relocate ramdisk, and use atags to pass
>> > > > > > > > params.
>> > > > > > > >
>> > > > > > > > Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
>> > > > > > > > Acked-by: Simon Glass <sjg at chromium.org>
>> > > > > > > > ---
>> > > > > > > >
>> > > > > > > > Changes in v4: None
>> > > > > > > > Changes in v3: None
>> > > > > > > > Changes in v2: None
>> > > > > > > >
>> > > > > > > >  include/configs/kylin_rk3036.h | 23
>> > > > > > > > +++++++++++++++++++++++
>> > > > > > > >  1 file changed, 23 insertions(+)
>> > > > > > > >
>> > > > > > > > diff --git a/include/configs/kylin_rk3036.h
>> > > > > > > > b/include/configs/kylin_rk3036.h
>> > > > > > > > index b750b26..49997ec 100644
>> > > > > > > > --- a/include/configs/kylin_rk3036.h
>> > > > > > > > +++ b/include/configs/kylin_rk3036.h
>> > > > > > > > @@ -35,6 +35,29 @@
>> > > > > > > >  #undef CONFIG_EXTRA_ENV_SETTINGS
>> > > > > > > >  #define CONFIG_EXTRA_ENV_SETTINGS \
>> > > > > > > >         "partitions=" PARTS_DEFAULT \
>> > > > > > > > +       "mmcdev=0\0" \
>> > > > > > > > +       "mmcpart=5\0" \
>> > > > > > > > +       "loadaddr=" __stringify(CONFIG_SYS_LOAD_ADDR)
>> > > > > > > > "\0" \
>> > > > > > > > +
>> > > > > > > > +#define CONFIG_ANDROID_BOOT_IMAGE
>> > > > > > > > +#define CONFIG_SYS_BOOT_RAMDISK_HIGH
>> > > > > > > This should already be set.
>> > > > > > Right, i'll remove it...
>> > > > > > > > +#define CONFIG_SYS_HUSH_PARSER
>> > > > > > > > +
>> > > > > > > > +#undef CONFIG_BOOTCOMMAND
>> > > > > > > > +#define CONFIG_BOOTCOMMAND \
>> > > > > > > > +       "mmc dev ${mmcdev}; if mmc rescan; then " \
>> > > > > > > > +               "part start mmc ${mmcdev} ${mmcpart}
>> > > > > > > > boot_start;" \
>> > > > > > > > +               "part size mmc ${mmcdev} ${mmcpart}
>> > > > > > > > boot_size;" \
>> > > > > > > > +               "mmc read ${loadaddr} ${boot_start}
>> > > > > > > > ${boot_size};" \
>> > > > > > > > +               "bootm start ${loadaddr}; bootm
>> > > > > > > > ramdisk;" \
>> > > > > > > > +               "bootm prep; bootm go;" \
>> > > > > > > > +       "fi;" \
>> > > > > > > > +
>> > > > > > > > +/* Enable atags */
>> > > > > > > > +#define CONFIG_SYS_BOOTPARAMS_LEN      (64*1024)
>> > > > > > > > +#define CONFIG_INITRD_TAG
>> > > > > > > > +#define CONFIG_SETUP_MEMORY_TAGS
>> > > > > > > > +#define CONFIG_CMDLINE_TAG
>> > > > > > > But I'm confused as to what exactly is going on here.
>> > > > > > >  Appended dtb is
>> > > > > > > not the same as ATAGS.  And you shouldn't need to split
>> > > > > > > up bootm like
>> > > > > > > that.  Can you please explain a bit more?  Thanks!
>> > > > > > The u-boot will pass atags to kernel, and kernel will merge
>> > > > > > those
>> > > > > > atags into the appended dtb(fdt).
>> > > > > >
>> > > > > > The default bootm flow would not pass ramdisk state, but we
>> > > > > > need it,
>> > > > > > so we should add this state into default flow, or just use
>> > > > > > split
>> > > > > > bootm cmds :)
>> > > > > That seems very strange.  Is the ramdisk concatenated with
>> > > > > the kernel
>> > > > > and dtb as well (and that's why bootm ramdisk somehow finds
>> > > > > it but
>> > > > > normal bootm doesn't as you aren't passing in a ramdisk
>> > > > > address) ?
>> > > > Yes, the ramdisk concatenated with the kernel and dtb as
>> > > > well(u-boot/include/android_image.h: struct andr_img_hdr).
>> > > >
>> > > > And the normal bootm cmd would find it by parsing andr_img_hdr
>> > > > struct.
>> > > > But we still need bootm ramdisk state, because it will call
>> > > > boot_ramdisk_high to relocate ramdisk area :)
>> > > >
>> > > > I found if not relocate it to somewhere else, it would be
>> > > > corrupted
>> > > > after kernel's decompressing(during update fdt area).
>> > > So 'bootm $loadaddr' of an Android image sees, but does not
>> > > relocate the
>> > > ramdisk that is included in the image, but bootm ramdisk does?
>> > >  That
>> > > sounds like a bug in the regular bootm handling.
>> > Yep, the default bootm flow would not contain ramdisk relocate
>> > state:
>> >
>> > vi common/cmd_bootm.c
>> >         return do_bootm_states(cmdtp, flag, argc, argv,
>> > BOOTM_STATE_START |
>> >                 BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER |
>> >                 BOOTM_STATE_LOADOS |
>> > #if defined(CONFIG_PPC) || defined(CONFIG_MIPS)
>> >                 BOOTM_STATE_OS_CMDLINE |
>> > #endif
>> >                 BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |
>> >                 BOOTM_STATE_OS_GO, &images, 1);
>> >
>> > But i'm not sure if it's ok to add it here...
>>
>> Actually, maybe so.  This made me do some digging again back into
>> Simon's series that refactored the bootm code.  In the long long ago,
>> nearly every architecture would call bootm_ramdisk_high to relocate
>> the
>> ramdisk and set the environment (and poke the DT).  But it wasn't
>> always
>> clear when / why it would do this.  And it got consolidated.  And
>> here's
>> where it's tricky.  It looks correct today, still, to make sure that
>> we
>> relocate the ramdisk.  image_setup_linux() checks
>> IMAGE_ENABLE_RAMDISK_HIGH which is toggled by
>> CONFIG_SYS_BOOT_RAMDISK_HIGH.  But for example, MIPS whacked _back_
>> in a
>> local call to make sure the ramdisk was being relocated because it
>> wasn't back in 2014 into boot_prep_linux.  It may even be related to
>> some of the initrd rated things Masahiro has found recently.  So
>> something is wrong down in this core code.
>
> one problem is the different behaviour of bootm between legacy uImages
> and FIT uImages. In case of legacy uImages, the core code automatically
> relocates initramfs and DTB. In case of FIT uImages, the arch-specific
> do_bootm_linux() still has to do that. This was only somehow addressed
> by adding the helper function image_setup_linux() and calling it from
> do_bootm_linux(). But you can't use that function with legacy uImages,
> because initramfs and DTB would be relocated twice because the states
> BOOTM_STATE_RAMDISK and BOOTM_STATE_FDT are not checked. Because of
> that image_setup_linux() also leads to a different behaviour when
> calling bootm as a single command and calling bootm with sub-steps.
>
> So the current bootm implementation on MIPS is the only way for me to
> make bootm working with all possible combinations of legacy/FIT uImage,
> with/without initramfs, with/without DTB and single/multiple bootm
> calls. I suspect that the current implementation on ARM does not work
> with all possible combinations.
>
>> Jeffy, can you try and debug
>> this a bit since you have a failing case in front of you?  Otherwise
>> I'll put it on my TODO list.  Thanks!
>>
>
> Some ideas for a possible refactoring of the bootm core code:
> - remove arch-specific do_bootm_linux() and image_setup_linux()
> - rename functions like boot_prep_linux() and boot_jump_linux() to
> arch_boot_prep_linux() and arch_boot_jump_linux() and declare them as
> __weak and add a default implementation, the arch code can overwrite
> them
> - refactor the core code to call those functions at the appropriate
> locations

Perhaps we need a more formal API between bootm and the arch-specific
implementation of the various pieces.

Also it would be great to add more tests for bootm. I made a start
based on what I could figure out about the behaviour
(tests/image/test-fit.py), but we should do more.

Regards,
Simon


From naobsd at gmail.com  Fri Jan 15 17:22:40 2016
From: naobsd at gmail.com (Naoki FUKAUMI)
Date: Sat, 16 Jan 2016 10:22:40 +0900
Subject: [PATCH 2/2] arm64: dts: rockchip: Add basic support for orion-r68
In-Reply-To: <1452863192-29231-2-git-send-email-matthias.bgg@gmail.com>
References: <1452863192-29231-1-git-send-email-matthias.bgg@gmail.com>
 <1452863192-29231-2-git-send-email-matthias.bgg@gmail.com>
Message-ID: <CAFnsNoQ0Y11kQaTiQL-xEoTxAbZW4vzZDFrZC8ccS4egBx4NEg@mail.gmail.com>

Hi,

this is not full review.

On Fri, Jan 15, 2016 at 10:06 PM, Matthias Brugger
<matthias.bgg at gmail.com> wrote:

> diff --git a/Documentation/devicetree/bindings/arm/rockchip.txt b/Documentation/devicetree/bindings/arm/rockchip.txt
> index 8e985dd..fa97d93 100644
> --- a/Documentation/devicetree/bindings/arm/rockchip.txt
> +++ b/Documentation/devicetree/bindings/arm/rockchip.txt
> @@ -72,3 +72,7 @@ Rockchip platforms device tree bindings
>  - Rockchip R88 board:
>      Required root node properties:
>        - compatible = "rockchip,r88", "rockchip,rk3368";
> +
> +- Tronsmart Orion R68
> +    Required root node properties:
> +      - compatible = "tronsmart,orion-r68", "rockchip,rk3368";

> diff --git a/arch/arm64/boot/dts/rockchip/rk3368-orion-r68.dts b/arch/arm64/boot/dts/rockchip/rk3368-orion-r68.dts
> new file mode 100644
> index 0000000..3f71d506
> --- /dev/null
> +++ b/arch/arm64/boot/dts/rockchip/rk3368-orion-r68.dts
> @@ -0,0 +1,370 @@
> +/*
> + * Copyright (c) 2015 Matthias Brugger <mbrugger at suse.com>
> + *
> + * This file is dual-licensed: you can use it either under the terms
> + * of the GPL or the X11 license, at your option. Note that this dual
> + * licensing only applies to this file, and not this project as a
> + * whole.
> + *
> + *  a) This file is free software; you can redistribute it and/or
> + *     modify it under the terms of the GNU General Public License as
> + *     published by the Free Software Foundation; either version 2 of the
> + *     License, or (at your option) any later version.
> + *
> + *     This file is distributed in the hope that it will be useful,
> + *     but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *     GNU General Public License for more details.
> + *
> + * Or, alternatively,
> + *
> + *  b) Permission is hereby granted, free of charge, to any person
> + *     obtaining a copy of this software and associated documentation
> + *     files (the "Software"), to deal in the Software without
> + *     restriction, including without limitation the rights to use,
> + *     copy, modify, merge, publish, distribute, sublicense, and/or
> + *     sell copies of the Software, and to permit persons to whom the
> + *     Software is furnished to do so, subject to the following
> + *     conditions:
> + *
> + *     The above copyright notice and this permission notice shall be
> + *     included in all copies or substantial portions of the Software.
> + *
> + *     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.
> + */
> +
> +/dts-v1/;
> +#include "rk3368.dtsi"
> +
> +/ {
> +       model = "Rockchip Orion R68";
> +       compatible = "tronsmart,orion-r68", "rockchip,rk3368";
> +
> +       chosen {
> +               stdout-path = "serial2:115200n8";
> +       };
> +
> +       memory {
> +               device_type = "memory";
> +               reg = <0x0 0x0 0x0 0x40000000>;
> +       };

there are (at least) 4 models/revisions in Tronsmart Orion R68, Meta
or Pro, 100M or 1000M ethernet. probably there are more variants from
other vendors based on same board.
I don't say all variants need to be supported here now, but it may
need few more description.

I have Orion R68 Meta 1000M version. there is a label on the board,
"Netxeon RK68G V2.0 2G/16G/AP6335". it might be called
"netxeon,rk68g-v2.0" or something like that.

> +       leds: gpio-leds {
> +               compatible = "gpio-leds";
> +
> +               work {
> +                       gpios = <&gpio3 29 GPIO_ACTIVE_HIGH>;
> +                       label = "orion:purple:led";
> +                       pinctrl-names = "default";
> +                       pinctrl-0 = <&led_ctl>;
> +                       default-state = "on";
> +               };
> +       };

> +       leds {
> +               stby_pwren: stby-pwren {
> +                       rockchip,pins = <0 12 RK_FUNC_GPIO &pcfg_pull_none>;
> +               };
> +
> +               led_ctl: led-ctl {
> +                       rockchip,pins = <3 29 RK_FUNC_GPIO &pcfg_pull_none>;
> +               };
> +       };

on my board, there is no "purple" LED, <&gpio3 29 GPIO_ACTIVE_HIGH> is
red LED, <&gpio0 12 GPIO_ACTIVE_HIGH> is blue LED.

did you already confirm all GPIO connection written in your dts?

> +&uart2 {
> +       status = "okay";
> +};

uart4 is working for me. (as a console)

Regards,

--
FUKAUMI Naoki


From peter at hurleysoftware.com  Sat Jan 16 07:49:23 2016
From: peter at hurleysoftware.com (Peter Hurley)
Date: Sat, 16 Jan 2016 07:49:23 -0800
Subject: [PATCH v2] serial: core: remove baud_rates when serial console
 setup
In-Reply-To: <1451894086-20435-1-git-send-email-jeffy.chen@rock-chips.com>
References: <1451894086-20435-1-git-send-email-jeffy.chen@rock-chips.com>
Message-ID: <569A6683.3080702@hurleysoftware.com>

On 01/03/2016 11:54 PM, Jeffy Chen wrote:
> Currently, when tring to set up a serial console with a higher
> baud rate, it would fallback to 921600.

Reviewed-by: Peter Hurley <peter at hurleysoftware.com>


From robh at kernel.org  Sat Jan 16 16:17:39 2016
From: robh at kernel.org (Rob Herring)
Date: Sat, 16 Jan 2016 18:17:39 -0600
Subject: [PATCH 1/2] devicetree: bindings: Add vendor prefix for Tronsmart
In-Reply-To: <1452863192-29231-1-git-send-email-matthias.bgg@gmail.com>
References: <1452863192-29231-1-git-send-email-matthias.bgg@gmail.com>
Message-ID: <20160117001739.GA11246@rob-hp-laptop>

On Fri, Jan 15, 2016 at 02:06:31PM +0100, Matthias Brugger wrote:
> Tronsmart is a China based company building consumer electronic
> devices.
> 
> Signed-off-by: Matthias Brugger <mbrugger at suse.com>
> ---
>  Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
>  1 file changed, 1 insertion(+)

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


From heiko at sntech.de  Sun Jan 17 06:25:14 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sun, 17 Jan 2016 15:25:14 +0100
Subject: [PATCH v12 0/18] Add Analogix Core Display Port Driver
In-Reply-To: <2322926.s1viZ6tSS3@phil>
References: <1450873538-18304-1-git-send-email-ykk@rock-chips.com>
 <2322926.s1viZ6tSS3@phil>
Message-ID: <1670568.VBNHbJuGkf@phil>

Hi Yakir,

Am Mittwoch, 13. Januar 2016, 15:59:51 schrieb Heiko Stuebner:
> Am Mittwoch, 23. Dezember 2015, 20:25:38 schrieb Yakir Yang:
> >    The Samsung Exynos eDP controller and Rockchip RK3288 eDP controller
> > 
> > share the same IP, so a lot of parts can be re-used. I split the common
> > code into bridge directory, then rk3288 and exynos only need to keep
> > some platform code. Cause I can't find the exact IP name of exynos dp
> > controller, so I decide to name dp core driver with "analogix" which I
> > find in rk3288 eDP TRM
> 
> could you rebase your patches on top of Dave's drm-next branch [0] please?
> The exynos part got some const attributes for the *_func_ops structs and
> drm_encoder_init got an additional parameter.
> I'm still hoping that we can get this finally committed once 4.5-rc1 is
> released in 1.5 weeks :-) .
> 
> I did try to merge things together [1], the system at least comes up and
> detects the panel (EDID is read correctly and it turns the backlight on)
> but I don't see any output on the display - hdmi works fine though and
> X11 is claiming everything to be fine in both cases.
> 
> So I don't know yet if I made a mistake when putting this together or
> there is an issue on the driver-side.

I was able to debug this a bit more, and it seems the encoder_helper_funcs 
don't like the prepare/commit callbacks and like enable better.

So when I do something similar to Marks patch a8eef71d38da ("drm/rockchip: 
dw_hdmi: use encoder enable function"), like in the attached patch (to be 
folded into the original addition of those functions), I get display output 
again.


Heiko

> [0] http://cgit.freedesktop.org/~airlied/linux/log/?h=drm-next
> [1] https://github.com/mmind/linux-rockchip/tree/tmp/analogixdp_v12-veyron
> I left out patch16, per your talk with Jingoo and me not seeing any AUX
> CH errors without it.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-analogix_dp-rockchip-use-.enable-instead-of-preprare.patch
Type: text/x-patch
Size: 1406 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160117/40b29442/attachment.bin>

From heiko at sntech.de  Sun Jan 17 07:21:08 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sun, 17 Jan 2016 16:21:08 +0100
Subject: [RFC/PATCH] drm/rockchip: don't wait for vblank if fb hasn't
 changed
In-Reply-To: <1452689614-6570-1-git-send-email-john@metanate.com>
References: <1452689614-6570-1-git-send-email-john@metanate.com>
Message-ID: <5745581.hZKD3MVpjF@phil>

Am Mittwoch, 13. Januar 2016, 12:53:34 schrieb John Keeping:
> As commented in drm_atomic_helper_wait_for_vblanks(), userspace relies
> on cursor ioctls being unsynced.  Converting the rockchip driver to
> atomic has significantly impacted cursor performance by making every
> cursor update wait for vblank.
> 
> By skipping the vblank sync when the framebuffer has not changed (as is
> done in drm_atomic_helper_wait_for_vblanks()) we can avoid this for the
> common case of moving the cursor and only need to delay the cursor ioctl
> when the cursor icon changes.
> 
> I originally inserted a check on legacy_cursor_update as well, but that
> caused a storm of iommu page faults.  I didn't investigate the cause of
> those since this change gives enough of a performance improvement for my
> use case.
> 
> This is RFC because of that and because the framebuffer_changed()
> function is copied from drm_atomic_helper.c as a quick way to test the
> result.
> 
> Signed-off-by: John Keeping <john at metanate.com>

I've seen the effects now as well after making the atomic parts work on in 
my devtree - i.e. sluggish cursor movements.

This patch fixes that issue, so at least:
Tested-by: Heiko Stuebner <heiko at sntech.de>


Right now I still see flickering on animated cursors though (like ones used 
by KDE), that wasn't present before.


Heiko


From heiko at sntech.de  Sun Jan 17 11:07:01 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sun, 17 Jan 2016 20:07:01 +0100
Subject: [GIT PULL] clk fixes for 4.5
Message-ID: <1578308.XWWZ6XYns5@phil>

Hi Mike, Stephen,

based on the clk-for-linus-4.5 tag that got merged into mainline
some asorted fixes for Rockchip clocks.

So, please pull


Thanks
Heiko

The following changes since commit 3da834e3e5a4a5d26882955298b55a9ed37a00bc:

  clk: remove duplicated COMMON_CLK_NXP record from clk/Kconfig (2016-01-13 18:09:52 -0800)

are available in the git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git tags/v4.5-rockchip-clkfixes1

for you to fetch changes up to 8931f8e02979e4180566907019d432527c80abf7:

  clk: rockchip: rk3368: fix some clock gates (2016-01-16 16:01:21 +0100)

----------------------------------------------------------------
Fixes for wrong register offsets in both rk3036 and rk3368.
Also rename the external input for the emac on rk3036, which
should still be ok to do, as that binding was only introduced
during this merge-window.

----------------------------------------------------------------
Jianqun xu (1):
      clk: rockchip: rk3368: fix some clock gates

Xing Zheng (4):
      clk: rockchip: rk3036: fix the FLAGs for clock mux
      clk: rockchip: rk3036: fix uarts clock error
      clk: rockchip: rk3036: fix the div offset for emac clock
      clk: rockchip: rk3036: rename emac ext source clock

 .../bindings/clock/rockchip,rk3036-cru.txt         |  2 +-
 drivers/clk/rockchip/clk-rk3036.c                  | 26 +++++++++++-----------
 drivers/clk/rockchip/clk-rk3368.c                  | 26 +++++++++++-----------
 3 files changed, 27 insertions(+), 27 deletions(-)



From heiko at sntech.de  Sun Jan 17 11:14:07 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sun, 17 Jan 2016 20:14:07 +0100
Subject: [PATCH] clk: rockchip: rk3036: enable the CLK_IGNORE_UNUSED flag
 for hclk_vio_bus
In-Reply-To: <1452858854-841-1-git-send-email-ykk@rock-chips.com>
References: <1452858854-841-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1816378.OnNVlMiORS@phil>

Am Freitag, 15. Januar 2016, 19:54:14 schrieb Yakir Yang:
> HCLK_VIO_BUS is the noc bus controller clock for display module,
> due to it shouldn't belong to any driver, but we need it enabled,
> so just mark it as the CLK_IGNORE_UNUSED flag.
> 
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>

applied to my clk-branch for 4.6

Thanks
Heiko


From heiko at sntech.de  Sun Jan 17 11:18:46 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sun, 17 Jan 2016 20:18:46 +0100
Subject: [PATCH v3 8/9] ARM: dts: rockchip: add reboot-mode node for
 rk3036 SoCs
In-Reply-To: <1452865796-23527-9-git-send-email-wxt@rock-chips.com>
References: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
 <1452865796-23527-9-git-send-email-wxt@rock-chips.com>
Message-ID: <3866840.JFJvXxmQit@phil>

Hi Caesar, Andy,

Am Freitag, 15. Januar 2016, 21:49:55 schrieb Caesar Wang:
> Add reboot mode driver DT node for rk3036 platform.
> This patch adds the needed reboot mode node for rk3036.
> 
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>

this obviously depends on Andy's reboot series.

Andy, could you include this patch into further submissions of your series 
please? That way it won't keep floating unconnected on the lists :-)


Thanks
Heiko

> ---
> 
> Changes in v3: None
> 
>  arch/arm/boot/dts/rk3036.dtsi | 28 +++++++++++++++++++++++++++-
>  1 file changed, 27 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
> index cf0968c..f5ecfff 100644
> --- a/arch/arm/boot/dts/rk3036.dtsi
> +++ b/arch/arm/boot/dts/rk3036.dtsi
> @@ -43,6 +43,7 @@
>  #include <dt-bindings/interrupt-controller/arm-gic.h>
>  #include <dt-bindings/pinctrl/rockchip.h>
>  #include <dt-bindings/clock/rk3036-cru.h>
> +#include <dt-bindings/soc/rockchip_boot-mode.h>
>  #include "skeleton.dtsi"
> 
>  / {
> @@ -297,8 +298,33 @@
>  	};
> 
>  	grf: syscon at 20008000 {
> -		compatible = "rockchip,rk3036-grf", "syscon";
> +		compatible = "rockchip,rk3036-grf", "syscon", "simple-mfd";
>  		reg = <0x20008000 0x1000>;
> +
> +		reboot-mode {
> +			compatible = "syscon-reboot-mode";
> +			offset = <0x1d8>;
> +
> +			normal {
> +				linux,mode = "normal";
> +				loader,magic = <BOOT_NORMAL>;
> +			};
> +
> +			loader {
> +				linux,mode = "loader";
> +				loader,magic = <BOOT_LOADER>;
> +			};
> +
> +			recovery {
> +				linux,mode = "recovery";
> +				loader,magic = <BOOT_RECOVERY>;
> +			};
> +
> +			fastboot {
> +				linux,mode = "fastboot";
> +				loader,magic = <BOOT_FASTBOOT>;
> +			};
> +		};
>  	};
> 
>  	acodec: acodec-ana at 20030000 {



From heiko at sntech.de  Sun Jan 17 11:20:48 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sun, 17 Jan 2016 20:20:48 +0100
Subject: [PATCH v3 9/9] ARM: dts: rockchip: add pl330-broken-no-flushp
 quirk for rk3036 SoCs
In-Reply-To: <1452865796-23527-10-git-send-email-wxt@rock-chips.com>
References: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
 <1452865796-23527-10-git-send-email-wxt@rock-chips.com>
Message-ID: <2986016.zV0HQZEBmn@phil>

Hi Caesar,

Am Freitag, 15. Januar 2016, 21:49:56 schrieb Caesar Wang:
> Pl330 integrated in rk3036 platform that doesn't support
> DMAFLUSHP function. So we add 'arm,pl330-broken-no-flushp' quirk
> for rk3036.
> 
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>

can you try to find out if Shawn plans on respinning his work on the pl330 
quirk that is needed for this. His last submission seems to be from october.


Thanks
Heiko


From heiko at sntech.de  Sun Jan 17 11:23:37 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sun, 17 Jan 2016 20:23:37 +0100
Subject: [PATCH v3 4/9] ARM: dts: rockchip: enable the uart0 for kylin
 board
In-Reply-To: <1452865796-23527-5-git-send-email-wxt@rock-chips.com>
References: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
 <1452865796-23527-5-git-send-email-wxt@rock-chips.com>
Message-ID: <3453769.45ZRnt7eYG@phil>

Am Freitag, 15. Januar 2016, 21:49:51 schrieb Caesar Wang:
> This patch is enabling the uart0 for bluetooth module.
> 
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>

applied to my dts32 branch for 4.6,

after moving your uart0 node _above_ the already existing uart2 node.


Heiko


From heiko at sntech.de  Sun Jan 17 11:26:37 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sun, 17 Jan 2016 20:26:37 +0100
Subject: [PATCH v3 5/9] ARM: dts: rockchip: enable the high speed on sdio
 for kylin board
In-Reply-To: <1452865796-23527-6-git-send-email-wxt@rock-chips.com>
References: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
 <1452865796-23527-6-git-send-email-wxt@rock-chips.com>
Message-ID: <4961569.46y83h10I6@phil>

Am Freitag, 15. Januar 2016, 21:49:52 schrieb Caesar Wang:
> We want to the higher speed for wifi module working.
> 
> Bootup kernel log:
> ...
> mmc_host mmc0: Bus speed (slot 0) = 37125000Hz (slot req 37500000Hz,
> actual 37125000HZ div = 0)
> 
> or run 'cat /sys/kernel/debug/clk/clk_summary |grep phase -C 1' to check
> Otherwise, the mmc0 will run 400khz defalult value to work.
> 
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>

applied to my dts32 branch for 4.6



From heiko at sntech.de  Sun Jan 17 11:31:01 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sun, 17 Jan 2016 20:31:01 +0100
Subject: [PATCH v3 6/9] ARM: dts: rockchip: add the sdio power sequence
 for kylin board
In-Reply-To: <1452865796-23527-7-git-send-email-wxt@rock-chips.com>
References: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
 <1452865796-23527-7-git-send-email-wxt@rock-chips.com>
Message-ID: <10023420.T8auvzlYRp@phil>

Am Freitag, 15. Januar 2016, 21:49:53 schrieb Caesar Wang:
> This patch adds the sdio power sequence for kylin board.
> The WLAN attached to a SDIO interface, wifi/bluetooth have
> reset and power been needed to enable.
> 
> AFAIK, the simple power sequence provider sets a value for multiple GPIOs.
> So the reset and power of WlAN chip can be handled in mmc power sequence.
> On the module itself this is one of these, that should can be handled by
> reset GPIOs in simple mmc power sequence.
> 
> The Bluetooth host wake is high active from bootup, this patch is also
> set pinctrl bias as the default to enable the pull up in soc internal.
> 
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>

applied to my dts32 branch for 4.6,

after moving the sdio-pwrseq node to its correct alphabetical position. 
Please try to keep to the sorting :-)


Thanks
Heiko


From heiko at sntech.de  Sun Jan 17 11:37:17 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sun, 17 Jan 2016 20:37:17 +0100
Subject: [PATCH v3 7/9] ARM: dts: rockchip: add the sdmmc for kylin board
In-Reply-To: <1452865796-23527-8-git-send-email-wxt@rock-chips.com>
References: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
 <1452865796-23527-8-git-send-email-wxt@rock-chips.com>
Message-ID: <2230942.JkKsuulFEe@phil>

Am Freitag, 15. Januar 2016, 21:49:54 schrieb Caesar Wang:
> Although We can add the sdmmc node, shouldn't enable it.
> Since the sdmmc is reusing the same pin with uart2.
> Unfortunately, the uart2 is used by the debug port, so that will cause
> the debug information can't display on console if enabling the sdmmc.
> 
> As we have supported the sdmmc (sd card) on hardware for kylin board.
> So, maybe we can have the sdmmc node in kylin dts, not to enable it.
> 
> Anyway, you only need add the okay status if someone want to enable the
> sdmmc.
> 
> e.g.
> if you use the adb to debug with android os.
> You can add the
> status = "okay" to enable the sdmmc for sd card working.
> The defauly status is disabling it.
> 
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>

applied to my dts32 branch for 4.6


Thanks
Heiko


From heiko at sntech.de  Sun Jan 17 15:16:50 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Mon, 18 Jan 2016 00:16:50 +0100
Subject: [PATCH v2 0/2] fix rk3066a based boards hang when cpufreq changes
In-Reply-To: <1452864223-423-1-git-send-email-andy.yan@rock-chips.com>
References: <1452864223-423-1-git-send-email-andy.yan@rock-chips.com>
Message-ID: <1825501.4ZfleL6hBQ@phil>

Hi Andy,

Am Freitag, 15. Januar 2016, 21:23:43 schrieb Andy Yan:
> I found the rayeager board always hang in the system boot
> process or when cpufreq change. When check the BSP code[0]
> from ChipSpark, I found the current vdd_arm voltage is too
> low and so is the vdd_logic voltage, which should be modulated
> by PWM3.
> 
> So I increase the vdd_arm voltage and enabled pwm3 as a pwm
> regulator here.

applied both to my dts32 branch for 4.6

Thanks
Heiko


From andy.yan at rock-chips.com  Sun Jan 17 17:11:58 2016
From: andy.yan at rock-chips.com (Andy Yan)
Date: Mon, 18 Jan 2016 09:11:58 +0800
Subject: [PATCH v3 8/9] ARM: dts: rockchip: add reboot-mode node for
 rk3036 SoCs
In-Reply-To: <3866840.JFJvXxmQit@phil>
References: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
 <1452865796-23527-9-git-send-email-wxt@rock-chips.com>
 <3866840.JFJvXxmQit@phil>
Message-ID: <569C3BDE.1040108@rock-chips.com>

Hi Heiko, Caesar:

On 2016?01?18? 03:18, Heiko Stuebner wrote:
> Hi Caesar, Andy,
>
> Am Freitag, 15. Januar 2016, 21:49:55 schrieb Caesar Wang:
>> Add reboot mode driver DT node for rk3036 platform.
>> This patch adds the needed reboot mode node for rk3036.
>>
>> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
> this obviously depends on Andy's reboot series.
>
> Andy, could you include this patch into further submissions of your series
> please? That way it won't keep floating unconnected on the lists :-)
>
>
> Thanks
> Heiko
>


     Okay, I will add it in my next series.
>> ---
>>
>> Changes in v3: None
>>
>>   arch/arm/boot/dts/rk3036.dtsi | 28 +++++++++++++++++++++++++++-
>>   1 file changed, 27 insertions(+), 1 deletion(-)
>>
>> diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
>> index cf0968c..f5ecfff 100644
>> --- a/arch/arm/boot/dts/rk3036.dtsi
>> +++ b/arch/arm/boot/dts/rk3036.dtsi
>> @@ -43,6 +43,7 @@
>>   #include <dt-bindings/interrupt-controller/arm-gic.h>
>>   #include <dt-bindings/pinctrl/rockchip.h>
>>   #include <dt-bindings/clock/rk3036-cru.h>
>> +#include <dt-bindings/soc/rockchip_boot-mode.h>
>>   #include "skeleton.dtsi"
>>
>>   / {
>> @@ -297,8 +298,33 @@
>>   	};
>>
>>   	grf: syscon at 20008000 {
>> -		compatible = "rockchip,rk3036-grf", "syscon";
>> +		compatible = "rockchip,rk3036-grf", "syscon", "simple-mfd";
>>   		reg = <0x20008000 0x1000>;
>> +
>> +		reboot-mode {
>> +			compatible = "syscon-reboot-mode";
>> +			offset = <0x1d8>;
>> +
>> +			normal {
>> +				linux,mode = "normal";
>> +				loader,magic = <BOOT_NORMAL>;
>> +			};
>> +
>> +			loader {
>> +				linux,mode = "loader";
>> +				loader,magic = <BOOT_LOADER>;
>> +			};
>> +
>> +			recovery {
>> +				linux,mode = "recovery";
>> +				loader,magic = <BOOT_RECOVERY>;
>> +			};
>> +
>> +			fastboot {
>> +				linux,mode = "fastboot";
>> +				loader,magic = <BOOT_FASTBOOT>;
>> +			};
>> +		};
>>   	};
>>
>>   	acodec: acodec-ana at 20030000 {
>
>
>
>





From mark.yao at rock-chips.com  Sun Jan 17 17:21:58 2016
From: mark.yao at rock-chips.com (Mark yao)
Date: Mon, 18 Jan 2016 09:21:58 +0800
Subject: [PATCH v3 1/2] drm: rockchip/hdmi: add Innosilicon HDMI support
In-Reply-To: <1452850690-31760-1-git-send-email-ykk@rock-chips.com>
References: <1452850598-30859-1-git-send-email-ykk@rock-chips.com>
 <1452850690-31760-1-git-send-email-ykk@rock-chips.com>
Message-ID: <569C3E36.10103@rock-chips.com>

Hi Yakir

I'd like you can change your patch title into "drm/rockchip/hdmi", so 
when I search patches use "drm/rockchip" can find your patch.

and I have some advices mail inline.

Thanks:-)

On 2016?01?15? 17:38, Yakir Yang wrote:
> The Innosilicon HDMI is a low power HDMI 1.4 transmitter
> IP, and it have been integrated on some rockchip CPUs
> (like RK3036, RK312x).
>
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
> ---
> Changes in v3:
> - Use encoder enable/disable function, and remove the encoder DPMS function
> - Keep HDMI PLL power on in standby mode
>
> Changes in v2:
> - Using DRM atomic helper functions for connector init (Mark)
> - Remove "hdmi->connector.encoder = encoder;" (Mark)
>
>   drivers/gpu/drm/rockchip/Kconfig     |   8 +
>   drivers/gpu/drm/rockchip/Makefile    |   1 +
>   drivers/gpu/drm/rockchip/inno_hdmi.c | 999 +++++++++++++++++++++++++++++++++++
>   drivers/gpu/drm/rockchip/inno_hdmi.h | 364 +++++++++++++
>   4 files changed, 1372 insertions(+)
>   create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.c
>   create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.h
>
> diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
> index 35215f6..a5014e0 100644
> --- a/drivers/gpu/drm/rockchip/Kconfig
> +++ b/drivers/gpu/drm/rockchip/Kconfig
> @@ -25,3 +25,11 @@ config ROCKCHIP_DW_HDMI
>   	  for the Synopsys DesignWare HDMI driver. If you want to
>   	  enable HDMI on RK3288 based SoC, you should selet this
>   	  option.
> +
> +config ROCKCHIP_INNO_HDMI
> +	tristate "Rockchip specific extensions for Innosilicon HDMI"
> +        depends on DRM_ROCKCHIP
> +        help
> +	  This selects support for Rockchip SoC specific extensions
> +	  for the Innosilicon HDMI driver. If you want to enable
> +	  HDMI on RK3036 based SoC, you should selet this option.
> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> index a9d380f..da2bf76 100644
> --- a/drivers/gpu/drm/rockchip/Makefile
> +++ b/drivers/gpu/drm/rockchip/Makefile
> @@ -6,6 +6,7 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
>   		rockchip_drm_gem.o
>   
>   obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
> +obj-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
>   
>   obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_drm_vop.o \
>   				rockchip_vop_reg.o
> diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
> new file mode 100644
> index 0000000..dc98179
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
> @@ -0,0 +1,999 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + *    Zheng Yang <zhengyang at rock-chips.com>
> + *    Yakir Yang <ykk at rock-chips.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/irq.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/hdmi.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of_device.h>
> +
> +#include <drm/drm_of.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_encoder_slave.h>
> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_vop.h"
> +
> +#include "inno_hdmi.h"
> +
> +#define to_inno_hdmi(x)	container_of(x, struct inno_hdmi, x)
> +
> +struct hdmi_data_info {
> +	int vic;
> +	bool sink_is_hdmi;
> +	bool sink_has_audio;
> +	unsigned int enc_in_format;
> +	unsigned int enc_out_format;
> +	unsigned int colorimetry;
> +};
> +
> +struct inno_hdmi_i2c {
> +	struct i2c_adapter adap;
> +
> +	u8 ddc_addr;
> +	u8 segment_addr;
> +
> +	struct mutex lock;
> +	struct completion cmp;
> +};
> +
> +struct inno_hdmi {
> +	struct device *dev;
> +	struct drm_device *drm_dev;
> +
> +	int irq;
> +	struct clk *pclk;
> +	void __iomem *regs;
> +
> +	struct drm_connector	connector;
> +	struct drm_encoder	encoder;
> +
> +	struct inno_hdmi_i2c *i2c;
> +	struct i2c_adapter *ddc;
> +
> +	unsigned int tmds_rate;
> +
> +	struct hdmi_data_info	hdmi_data;
> +	struct drm_display_mode previous_mode;
> +};
> +
> +enum {
> +	CSC_ITU601_16_235_TO_RGB_0_255_8BIT,
> +	CSC_ITU601_0_255_TO_RGB_0_255_8BIT,
> +	CSC_ITU709_16_235_TO_RGB_0_255_8BIT,
> +	CSC_RGB_0_255_TO_ITU601_16_235_8BIT,
> +	CSC_RGB_0_255_TO_ITU709_16_235_8BIT,
> +	CSC_RGB_0_255_TO_RGB_16_235_8BIT,
> +};
> +
> +static const char coeff_csc[][24] = {
> +	/*
> +	 * YUV2RGB:601 SD mode(Y[16:235], UV[16:240], RGB[0:255]):
> +	 *   R = 1.164*Y + 1.596*V - 204
> +	 *   G = 1.164*Y - 0.391*U - 0.813*V + 154
> +	 *   B = 1.164*Y + 2.018*U - 258
> +	 */
> +	{
> +		0x04, 0xa7, 0x00, 0x00, 0x06, 0x62, 0x02, 0xcc,
> +		0x04, 0xa7, 0x11, 0x90, 0x13, 0x40, 0x00, 0x9a,
> +		0x04, 0xa7, 0x08, 0x12, 0x00, 0x00, 0x03, 0x02
> +	},
> +	/*
> +	 * YUV2RGB:601 SD mode(YUV[0:255],RGB[0:255]):
> +	 *   R = Y + 1.402*V - 248
> +	 *   G = Y - 0.344*U - 0.714*V + 135
> +	 *   B = Y + 1.772*U - 227
> +	 */
> +	{
> +		0x04, 0x00, 0x00, 0x00, 0x05, 0x9b, 0x02, 0xf8,
> +		0x04, 0x00, 0x11, 0x60, 0x12, 0xdb, 0x00, 0x87,
> +		0x04, 0x00, 0x07, 0x16, 0x00, 0x00, 0x02, 0xe3
> +	},
> +	/*
> +	 * YUV2RGB:709 HD mode(Y[16:235],UV[16:240],RGB[0:255]):
> +	 *   R = 1.164*Y + 1.793*V - 248
> +	 *   G = 1.164*Y - 0.213*U - 0.534*V + 77
> +	 *   B = 1.164*Y + 2.115*U - 289
> +	 */
> +	{
> +		0x04, 0xa7, 0x00, 0x00, 0x07, 0x2c, 0x02, 0xf8,
> +		0x04, 0xa7, 0x10, 0xda, 0x12, 0x22, 0x00, 0x4d,
> +		0x04, 0xa7, 0x08, 0x74, 0x00, 0x00, 0x03, 0x21
> +	},
> +
> +	/*
> +	 * RGB2YUV:601 SD mode:
> +	 *   Cb = -0.291G - 0.148R + 0.439B + 128
> +	 *   Y  = 0.504G  + 0.257R + 0.098B + 16
> +	 *   Cr = -0.368G + 0.439R - 0.071B + 128
> +	 */
> +	{
> +		0x11, 0x5f, 0x01, 0x82, 0x10, 0x23, 0x00, 0x80,
> +		0x02, 0x1c, 0x00, 0xa1, 0x00, 0x36, 0x00, 0x1e,
> +		0x11, 0x29, 0x10, 0x59, 0x01, 0x82, 0x00, 0x80
> +	},
> +	/*
> +	 * RGB2YUV:709 HD mode:
> +	 *   Cb = - 0.338G - 0.101R + 0.439B + 128
> +	 *   Y  = 0.614G   + 0.183R + 0.062B + 16
> +	 *   Cr = - 0.399G + 0.439R - 0.040B + 128
> +	 */
> +	{
> +		0x11, 0x98, 0x01, 0xc1, 0x10, 0x28, 0x00, 0x80,
> +		0x02, 0x74, 0x00, 0xbb, 0x00, 0x3f, 0x00, 0x10,
> +		0x11, 0x5a, 0x10, 0x67, 0x01, 0xc1, 0x00, 0x80
> +	},
> +	/*
> +	 * RGB[0:255]2RGB[16:235]:
> +	 *   R' = R x (235-16)/255 + 16;
> +	 *   G' = G x (235-16)/255 + 16;
> +	 *   B' = B x (235-16)/255 + 16;
> +	 */
> +	{
> +		0x00, 0x00, 0x03, 0x6F, 0x00, 0x00, 0x00, 0x10,
> +		0x03, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
> +		0x00, 0x00, 0x00, 0x00, 0x03, 0x6F, 0x00, 0x10
> +	},
> +};
> +
> +static inline u8 hdmi_readb(struct inno_hdmi *hdmi, u16 offset)
> +{
> +	return readl_relaxed(hdmi->regs + (offset) * 0x04);
> +}
> +
> +static inline void hdmi_writeb(struct inno_hdmi *hdmi, u16 offset, u32 val)
> +{
> +	writel_relaxed(val, hdmi->regs + (offset) * 0x04);
> +}
> +
> +static inline void hdmi_modb(struct inno_hdmi *hdmi, u16 offset,
> +			     u32 msk, u32 val)
> +{
> +	u8 temp = hdmi_readb(hdmi, offset) & ~msk;
> +
> +	temp |= val & msk;
> +	hdmi_writeb(hdmi, offset, temp);
> +}
> +
> +static void inno_hdmi_i2c_init(struct inno_hdmi *hdmi)
> +{
> +	int ddc_bus_freq;
> +
> +	ddc_bus_freq = (hdmi->tmds_rate >> 2) / HDMI_SCL_RATE;
> +
> +	hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
> +	hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
> +
> +	/* Clear the EDID interrupt flag and mute the interrupt */
> +	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
> +	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
> +}
> +
> +static void inno_hdmi_sys_power(struct inno_hdmi *hdmi, bool enable)
> +{
> +	if (enable)
> +		hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON);
> +	else
> +		hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
> +}
> +
> +static void inno_hdmi_set_pwr_mode(struct inno_hdmi *hdmi, int mode)
> +{
> +	switch (mode) {
> +	case NORMAL:
> +		inno_hdmi_sys_power(hdmi, false);
> +
> +		hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x6f);
> +		hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0xbb);
> +
> +		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
> +		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x14);
> +		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x10);
> +		hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x0f);
> +		hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x00);
> +		hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x01);
> +
> +		inno_hdmi_sys_power(hdmi, true);
> +		break;
> +
> +	case LOWER_PWR:
> +		inno_hdmi_sys_power(hdmi, false);
> +		hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0x00);
> +		hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x00);
> +		hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x00);
> +		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
> +
> +		break;
> +
> +	default:
> +		dev_err(hdmi->dev, "Unknown power mode %d\n", mode);
> +	}
> +}
> +
> +static void inno_hdmi_reset(struct inno_hdmi *hdmi)
> +{
> +	u32 val;
> +	u32 msk;
> +
> +	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_DIGITAL, v_NOT_RST_DIGITAL);
> +	udelay(100);
> +
> +	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_ANALOG, v_NOT_RST_ANALOG);
> +	udelay(100);
> +
> +	msk = m_REG_CLK_INV | m_REG_CLK_SOURCE | m_POWER | m_INT_POL;
> +	val = v_REG_CLK_INV | v_REG_CLK_SOURCE_SYS | v_PWR_ON | v_INT_POL_HIGH;
> +	hdmi_modb(hdmi, HDMI_SYS_CTRL, msk, val);
> +
> +	inno_hdmi_set_pwr_mode(hdmi, NORMAL);
> +}
> +
> +static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi)
> +{
> +	char info[HDMI_SIZE_AVI_INFOFRAME] = {0};
> +	int avi_color_mode;
> +	int i;
> +
> +	hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_AVI);
> +
> +	info[0] = 0x82;
> +	info[1] = 0x02;
> +	info[2] = 0x0D;
> +	info[3] = info[0] + info[1] + info[2];
> +
> +	if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_RGB)
> +		avi_color_mode = AVI_COLOR_MODE_RGB;
> +	else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV444)
> +		avi_color_mode = AVI_COLOR_MODE_YCBCR444;
> +	else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV422)
> +		avi_color_mode = AVI_COLOR_MODE_YCBCR422;
> +	else
> +		avi_color_mode = AVI_COLOR_MODE_RGB;
> +
> +	info[4] = (avi_color_mode << 5);
> +	info[5] = (AVI_COLORIMETRY_NO_DATA << 6) |
> +		  (AVI_CODED_FRAME_ASPECT_NO_DATA << 4) |
> +		  ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME;
> +
> +	info[6] = 0;
> +	info[7] = hdmi->hdmi_data.vic;
> +
> +	if (hdmi->hdmi_data.vic == 6 || hdmi->hdmi_data.vic == 7 ||
> +	    hdmi->hdmi_data.vic == 21 || hdmi->hdmi_data.vic == 22)
> +		info[8] = 1;
> +	else
> +		info[8] = 0;
> +
> +	/* Calculate avi info frame checKsum */
> +	for (i = 4; i < HDMI_SIZE_AVI_INFOFRAME; i++)
> +		info[3] += info[i];
> +	info[3] = 0x100 - info[3];
> +
> +	for (i = 0; i < HDMI_SIZE_AVI_INFOFRAME; i++)
> +		hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i, info[i]);
> +
> +	return 0;
> +}
> +
> +static int inno_hdmi_config_video_vsi(struct inno_hdmi *hdmi)
> +{
> +	char info[HDMI_SIZE_VSI_INFOFRAME] = {0};
> +	int i;
> +
> +	hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, m_PACKET_VSI_EN,
> +		  v_PACKET_VSI_EN(0));
> +
> +	hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_VSI);
> +
> +	/* Header Bytes */
> +	info[0] = 0x81;
> +	info[1] = 0x01;
> +
> +	/* PB1 - PB3 contain the 24bit IEEE Registration Identifier */
> +	info[4] = 0x03;
> +	info[5] = 0x0c;
> +	info[6] = 0x00;
> +
> +	/* PB4 - HDMI_Video_Format into bits 7:5 */
> +	info[7] = 0;
> +
> +	/*
> +	 * PB5 - Depending on the video format, this byte will contain
> +	 * either the HDMI_VIC code in buts 7:0, OR the 3D_Structure in
> +	 * bits 7:4.
> +	 */
> +	info[2] = 0x06 - 2;
> +	info[8] = 0;
> +	info[9] = 0;
> +
> +	info[3] = info[0] + info[1] + info[2];
> +
> +	/* Calculate info frame checKsum */
> +	for (i = 4; i < HDMI_SIZE_VSI_INFOFRAME; i++)
> +		info[3] += info[i];
> +	info[3] = 0x100 - info[3];
> +
> +	for (i = 0; i < HDMI_SIZE_VSI_INFOFRAME; i++)
> +		hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i, info[i]);
> +
> +	hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, m_PACKET_VSI_EN,
> +		  v_PACKET_VSI_EN(1));
> +
> +	return 0;
> +}
> +
> +static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
> +{
> +	struct hdmi_data_info *data = &hdmi->hdmi_data;
> +	int c0_c2_change = 0;
> +	int csc_enable = 0;
> +	int csc_mode = 0;
> +	int auto_csc = 0;
> +	int value;
> +	int i;
> +
> +	/* Input video mode is SDR RGB24bit, data enable signal from external */
> +	hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL1, v_DE_EXTERNAL |
> +		    v_VIDEO_INPUT_FORMAT(VIDEO_INPUT_SDR_RGB444));
> +
> +	/* Input color hardcode to RGB, and output color hardcode to RGB888 */
> +	value = v_VIDEO_INPUT_BITS(VIDEO_INPUT_8BITS) |
> +		v_VIDEO_OUTPUT_COLOR(0) |
> +		v_VIDEO_INPUT_CSP(0);
> +	hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL2, value);
> +
> +	if (data->enc_out_format == data->enc_out_format) {
> +		if ((data->enc_in_format == HDMI_COLORSPACE_RGB) ||
> +		    (data->enc_in_format >= HDMI_COLORSPACE_YUV444)) {
> +			value = v_SOF_DISABLE | v_COLOR_DEPTH_NOT_INDICATED(1);
> +			hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
> +
> +			hdmi_modb(hdmi, HDMI_VIDEO_CONTRL,
> +				  m_VIDEO_AUTO_CSC | m_VIDEO_C0_C2_SWAP,
> +				  v_VIDEO_AUTO_CSC(AUTO_CSC_DISABLE) |
> +				  v_VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE));
> +			return 0;
> +		}
> +	}
> +
> +	if (data->colorimetry == HDMI_COLORIMETRY_ITU_601) {
> +		if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
> +		    (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
> +			csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT;
> +			auto_csc = AUTO_CSC_DISABLE;
> +			c0_c2_change = C0_C2_CHANGE_DISABLE;
> +			csc_enable = v_CSC_ENABLE;
> +		} else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
> +			   (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
> +			csc_mode = CSC_ITU601_16_235_TO_RGB_0_255_8BIT;
> +			auto_csc = AUTO_CSC_ENABLE;
> +			c0_c2_change = C0_C2_CHANGE_DISABLE;
> +			csc_enable = v_CSC_DISABLE;
> +		}
> +	} else {
> +		if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
> +		    (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
> +			csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT;
> +			auto_csc = AUTO_CSC_DISABLE;
> +			c0_c2_change = C0_C2_CHANGE_DISABLE;
> +			csc_enable = v_CSC_ENABLE;
> +		} else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
> +			   (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
> +			csc_mode = CSC_ITU709_16_235_TO_RGB_0_255_8BIT;
> +			auto_csc = AUTO_CSC_ENABLE;
> +			c0_c2_change = C0_C2_CHANGE_DISABLE;
> +			csc_enable = v_CSC_DISABLE;
> +		}
> +	}
> +
> +	for (i = 0; i < 24; i++)
> +		hdmi_writeb(hdmi, HDMI_VIDEO_CSC_COEF + i,
> +			    coeff_csc[csc_mode][i]);
> +
> +	value = v_SOF_DISABLE | csc_enable | v_COLOR_DEPTH_NOT_INDICATED(1);
> +	hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
> +	hdmi_modb(hdmi, HDMI_VIDEO_CONTRL, m_VIDEO_AUTO_CSC |
> +		  m_VIDEO_C0_C2_SWAP, v_VIDEO_AUTO_CSC(auto_csc) |
> +		  v_VIDEO_C0_C2_SWAP(c0_c2_change));
> +
> +	return 0;
> +}
> +
> +static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi,
> +					 struct drm_display_mode *mode)
> +{
> +	int value;
> +
> +	/* Set detail external video timing polarity and interlace mode */
> +	value = v_EXTERANL_VIDEO(1);
> +	value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
> +		 v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
> +	value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
> +		 v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
> +	value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
> +		 v_INETLACE(1) : v_INETLACE(0);
> +	hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
> +
> +	/* Set detail external video timing */
> +	value = mode->htotal;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
> +
> +	value = mode->htotal - mode->hdisplay;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
> +
> +	value = mode->hsync_start - mode->hdisplay;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
> +
> +	value = mode->hsync_end - mode->hsync_start;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
> +
> +	value = mode->vtotal;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
> +
> +	value = mode->vtotal - mode->vdisplay;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
> +
> +	value = mode->vsync_start - mode->vdisplay;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
> +
> +	value = mode->vsync_end - mode->vsync_start;
> +	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
> +
> +	hdmi_writeb(hdmi, HDMI_PHY_PRE_DIV_RATIO, 0x1e);
> +	hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_LOW, 0x2c);
> +	hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH, 0x01);
> +
> +	return 0;
> +}
> +
> +static int inno_hdmi_setup(struct inno_hdmi *hdmi,
> +			   struct drm_display_mode *mode)
> +{
> +	int value;
> +
> +	hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
> +
> +	hdmi->hdmi_data.enc_in_format = HDMI_COLORSPACE_RGB;
> +	hdmi->hdmi_data.enc_out_format = HDMI_COLORSPACE_RGB;
> +
> +	if ((hdmi->hdmi_data.vic == 6) || (hdmi->hdmi_data.vic == 7) ||
> +	    (hdmi->hdmi_data.vic == 21) || (hdmi->hdmi_data.vic == 22) ||
> +	    (hdmi->hdmi_data.vic == 2) || (hdmi->hdmi_data.vic == 3) ||
> +	    (hdmi->hdmi_data.vic == 17) || (hdmi->hdmi_data.vic == 18))
> +		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601;
> +	else
> +		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
> +
> +	/* Mute video and audio output */
> +	hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
> +		  v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1));
> +
> +	/* Set HDMI Mode */
> +	hdmi_writeb(hdmi, HDMI_HDCP_CTRL,
> +		    v_HDMI_DVI(hdmi->hdmi_data.sink_is_hdmi));
> +
> +	inno_hdmi_config_video_timing(hdmi, mode);
> +
> +	inno_hdmi_config_video_csc(hdmi);
> +
> +	if (hdmi->hdmi_data.sink_is_hdmi) {
> +		inno_hdmi_config_video_avi(hdmi);
> +		inno_hdmi_config_video_vsi(hdmi);
> +	}
> +
> +	/*
> +	 * When IP controller have configured to an accurate video
> +	 * timing, then the TMDS clock source would be switched to
> +	 * DCLK_LCDC, so we need to init the TMDS rate to mode pixel
> +	 * clock rate, and reconfigure the DDC clock.
> +	 */
> +	hdmi->tmds_rate = mode->clock * 1000;
> +	inno_hdmi_i2c_init(hdmi);
> +
> +	/* Unmute video and audio output */
> +	hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
> +		  v_AUDIO_MUTE(0) | v_VIDEO_MUTE(0));
> +
> +	return 0;
> +}
> +
> +static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder,
> +				       struct drm_display_mode *mode,
> +				       struct drm_display_mode *adj_mode)
> +{
> +	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
> +
> +	inno_hdmi_setup(hdmi, adj_mode);
> +
> +	/* Store the display mode for plugin/DKMS poweron events */

I think DKMS should be DPMS.

> +	memcpy(&hdmi->previous_mode, adj_mode, sizeof(hdmi->previous_mode));
> +}
> +
> +static void inno_hdmi_encoder_enable(struct drm_encoder *encoder)
> +{
> +	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
> +
> +	inno_hdmi_set_pwr_mode(hdmi, NORMAL);
> +}
> +
> +static void inno_hdmi_encoder_disable(struct drm_encoder *encoder)
> +{
> +	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
> +
> +	inno_hdmi_set_pwr_mode(hdmi, LOWER_PWR);
> +}
> +
> +static void inno_hdmi_encoder_commit(struct drm_encoder *encoder)
> +{
> +}
> +
> +static void inno_hdmi_encoder_prepare(struct drm_encoder *encoder)
> +{
> +	rockchip_drm_crtc_mode_config(encoder->crtc, DRM_MODE_CONNECTOR_HDMIA,
> +				      ROCKCHIP_OUT_MODE_P888);

Can you move mode_config into inno_hdmi_encoder_enable, and remove 
.prepare and .commit?

> +}
> +
> +static bool inno_hdmi_encoder_mode_fixup(struct drm_encoder *encoder,
> +					 const struct drm_display_mode *mode,
> +					 struct drm_display_mode *adj_mode)
> +{
> +	return true;
> +}
> +
> +static struct drm_encoder_helper_funcs inno_hdmi_encoder_helper_funcs = {
> +	.enable     = inno_hdmi_encoder_enable,
> +	.disable    = inno_hdmi_encoder_disable,
> +	.mode_fixup = inno_hdmi_encoder_mode_fixup,
> +	.mode_set   = inno_hdmi_encoder_mode_set,
> +	.prepare    = inno_hdmi_encoder_prepare,
> +	.commit     = inno_hdmi_encoder_commit,

On drm atomic, I think if support .enable and .disable callbacks, then 
.prepare and .commit is not needed.

> +};
> +
> +static struct drm_encoder_funcs inno_hdmi_encoder_funcs = {
> +	.destroy = drm_encoder_cleanup,
> +};
> +
> +static enum drm_connector_status
> +inno_hdmi_connector_detect(struct drm_connector *connector, bool force)
> +{
> +	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
> +
> +	return (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
> +		connector_status_connected : connector_status_disconnected;
> +}
> +
> +static int inno_hdmi_connector_get_modes(struct drm_connector *connector)
> +{
> +	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
> +	struct edid *edid;
> +	int ret = 0;
> +
> +	if (!hdmi->ddc)
> +		return 0;
> +
> +	edid = drm_get_edid(connector, hdmi->ddc);
> +	if (edid) {
> +		hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
> +		hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
> +		drm_mode_connector_update_edid_property(connector, edid);
> +		ret = drm_add_edid_modes(connector, edid);
> +		kfree(edid);
> +	}
> +
> +	return ret;
> +}
> +
> +static enum drm_mode_status
> +inno_hdmi_connector_mode_valid(struct drm_connector *connector,
> +			       struct drm_display_mode *mode)
> +{
> +	return MODE_OK;
> +}
> +
> +static struct drm_encoder *
> +inno_hdmi_connector_best_encoder(struct drm_connector *connector)
> +{
> +	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
> +
> +	return &hdmi->encoder;
> +}
> +
> +static int
> +inno_hdmi_probe_single_connector_modes(struct drm_connector *connector,
> +				       uint32_t maxX, uint32_t maxY)
> +{
> +	return drm_helper_probe_single_connector_modes(connector, 1920, 1080);
> +}
> +
> +static void inno_hdmi_connector_destroy(struct drm_connector *connector)
> +{
> +	drm_connector_unregister(connector);
> +	drm_connector_cleanup(connector);
> +}
> +
> +static struct drm_connector_funcs inno_hdmi_connector_funcs = {
> +	.dpms = drm_atomic_helper_connector_dpms,
> +	.fill_modes = inno_hdmi_probe_single_connector_modes,
> +	.detect = inno_hdmi_connector_detect,
> +	.destroy = inno_hdmi_connector_destroy,
> +	.reset = drm_atomic_helper_connector_reset,
> +	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static struct drm_connector_helper_funcs inno_hdmi_connector_helper_funcs = {
> +	.get_modes = inno_hdmi_connector_get_modes,
> +	.mode_valid = inno_hdmi_connector_mode_valid,
> +	.best_encoder = inno_hdmi_connector_best_encoder,
> +};
> +
> +static int inno_hdmi_register(struct drm_device *drm, struct inno_hdmi *hdmi)
> +{
> +	struct drm_encoder *encoder = &hdmi->encoder;
> +
> +	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, hdmi->dev->of_node);

Keep 80 characters :-) .

> +	/*
> +	 * If we failed to find the CRTC(s) which this encoder is
> +	 * supposed to be connected to, it's because the CRTC has
> +	 * not been registered yet.  Defer probing, and hope that
> +	 * the required CRTC is added later.
> +	 */
> +	if (encoder->possible_crtcs == 0)
> +		return -EPROBE_DEFER;
> +
> +	drm_encoder_helper_add(encoder, &inno_hdmi_encoder_helper_funcs);
> +	drm_encoder_init(drm, encoder, &inno_hdmi_encoder_funcs,
> +			 DRM_MODE_ENCODER_TMDS, NULL);
> +
> +	hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
> +
> +	drm_connector_helper_add(&hdmi->connector,
> +				 &inno_hdmi_connector_helper_funcs);
> +	drm_connector_init(drm, &hdmi->connector, &inno_hdmi_connector_funcs,
> +			   DRM_MODE_CONNECTOR_HDMIA);
> +
> +	drm_mode_connector_attach_encoder(&hdmi->connector, encoder);
> +
> +	return 0;
> +}
> +
> +static irqreturn_t inno_hdmi_i2c_irq(struct inno_hdmi *hdmi)
> +{
> +	struct inno_hdmi_i2c *i2c = hdmi->i2c;
> +	u8 stat;
> +
> +	stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
> +	if (!(stat & m_INT_EDID_READY))
> +		return IRQ_NONE;
> +
> +	/* Clear HDMI EDID interrupt flag */
> +	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
> +
> +	complete(&i2c->cmp);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t inno_hdmi_hardirq(int irq, void *dev_id)
> +{
> +	struct inno_hdmi *hdmi = dev_id;
> +	irqreturn_t ret = IRQ_NONE;
> +	u8 interrupt;
> +
> +	if (hdmi->i2c)
> +		ret = inno_hdmi_i2c_irq(hdmi);
> +
> +	interrupt = hdmi_readb(hdmi, HDMI_STATUS);
> +	if (interrupt & m_INT_HOTPLUG) {
> +		hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG);
> +		ret = IRQ_WAKE_THREAD;
> +	}
> +
> +	return ret;
> +}
> +
> +static irqreturn_t inno_hdmi_irq(int irq, void *dev_id)
> +{
> +	struct inno_hdmi *hdmi = dev_id;
> +
> +	drm_helper_hpd_irq_event(hdmi->connector.dev);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int inno_hdmi_i2c_wait(struct inno_hdmi *hdmi)
> +{
> +	struct inno_hdmi_i2c *i2c = hdmi->i2c;
> +	int stat;
> +
> +	stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
> +	if (!stat) {
> +		stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
> +		if (!stat)
> +			return -EAGAIN;
> +	}
> +
> +	return 0;
> +}
> +
> +static int inno_hdmi_i2c_read(struct inno_hdmi *hdmi, struct i2c_msg *msgs)
> +{
> +	int length = msgs->len;
> +	u8 *buf = msgs->buf;
> +	int ret;
> +
> +	ret = inno_hdmi_i2c_wait(hdmi);
> +	if (ret)
> +		return ret;
> +
> +	while (length--)
> +		*buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
> +
> +	return 0;
> +}
> +
> +static int inno_hdmi_i2c_write(struct inno_hdmi *hdmi, struct i2c_msg *msgs)
> +{
> +	struct inno_hdmi_i2c *i2c = hdmi->i2c;
> +
> +	/*
> +	 * The DDC module only support read EDID message, so
> +	 * we assume that each word write to this i2c adapter
> +	 * should be the offset of EDID word address.
> +	 */
> +	if ((msgs->len != 1) ||
> +	    ((msgs->addr != DDC_ADDR) && (msgs->addr != DDC_SEGMENT_ADDR)))
> +		return -EINVAL;
> +
> +	reinit_completion(&i2c->cmp);
> +
> +	if (msgs->addr == DDC_SEGMENT_ADDR)
> +		hdmi->i2c->segment_addr = msgs->buf[0];
> +	if (msgs->addr == DDC_ADDR)
> +		hdmi->i2c->ddc_addr = msgs->buf[0];
> +
> +	/* Set edid fifo first addr */
> +	hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
> +
> +	/* Set edid word address 0x00/0x80 */
> +	hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
> +
> +	/* Set edid segment pointer */
> +	hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr);
> +
> +	return 0;
> +}
> +
> +static int inno_hdmi_i2c_xfer(struct i2c_adapter *adap,
> +			      struct i2c_msg *msgs, int num)
> +{
> +	struct inno_hdmi *hdmi = i2c_get_adapdata(adap);
> +	struct inno_hdmi_i2c *i2c = hdmi->i2c;
> +	int i, ret = 0;
> +
> +	mutex_lock(&i2c->lock);
> +
> +	/* Clear the EDID interrupt flag and unmute the interrupt */
> +	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY);
> +	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
> +
> +	for (i = 0; i < num; i++) {
> +		dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n",
> +			i + 1, num, msgs[i].len, msgs[i].flags);
> +
> +		if (msgs[i].flags & I2C_M_RD)
> +			ret = inno_hdmi_i2c_read(hdmi, &msgs[i]);
> +		else
> +			ret = inno_hdmi_i2c_write(hdmi, &msgs[i]);
> +
> +		if (ret < 0)
> +			break;
> +	}
> +
> +	if (!ret)
> +		ret = num;
> +
> +	/* Mute HDMI EDID interrupt */
> +	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
> +
> +	mutex_unlock(&i2c->lock);
> +
> +	return ret;
> +}
> +
> +static u32 inno_hdmi_i2c_func(struct i2c_adapter *adapter)
> +{
> +	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> +}
> +
> +static const struct i2c_algorithm inno_hdmi_algorithm = {
> +	.master_xfer	= inno_hdmi_i2c_xfer,
> +	.functionality	= inno_hdmi_i2c_func,
> +};
> +
> +static struct i2c_adapter *inno_hdmi_i2c_adapter(struct inno_hdmi *hdmi)
> +{
> +	struct i2c_adapter *adap;
> +	struct inno_hdmi_i2c *i2c;
> +	int ret;
> +
> +	i2c = devm_kzalloc(hdmi->dev, sizeof(*i2c), GFP_KERNEL);
> +	if (!i2c)
> +		return ERR_PTR(-ENOMEM);
> +
> +	mutex_init(&i2c->lock);
> +	init_completion(&i2c->cmp);
> +
> +	adap = &i2c->adap;
> +	adap->class = I2C_CLASS_DDC;
> +	adap->owner = THIS_MODULE;
> +	adap->dev.parent = hdmi->dev;
> +	adap->dev.of_node = hdmi->dev->of_node;
> +	adap->algo = &inno_hdmi_algorithm;
> +	strlcpy(adap->name, "Inno HDMI", sizeof(adap->name));
> +	i2c_set_adapdata(adap, hdmi);
> +
> +	ret = i2c_add_adapter(adap);
> +	if (ret) {
> +		dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name);
> +		devm_kfree(hdmi->dev, i2c);
> +		return ERR_PTR(ret);
> +	}
> +
> +	hdmi->i2c = i2c;
> +
> +	dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name);
> +
> +	return adap;
> +}
> +
> +static int inno_hdmi_bind(struct device *dev, struct device *master,
> +				 void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct drm_device *drm = data;
> +	struct inno_hdmi *hdmi;
> +	struct resource *iores;
> +	int irq;
> +	int ret;
> +
> +	hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
> +	if (!hdmi)
> +		return -ENOMEM;
> +
> +	hdmi->dev = dev;
> +	hdmi->drm_dev = drm;
> +
> +	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!iores)
> +		return -ENXIO;
> +
> +	hdmi->regs = devm_ioremap_resource(dev, iores);
> +	if (IS_ERR(hdmi->regs))
> +		return PTR_ERR(hdmi->regs);
> +
> +	hdmi->pclk = devm_clk_get(hdmi->dev, "pclk");
> +	if (IS_ERR(hdmi->pclk)) {
> +		dev_err(hdmi->dev, "Unable to get HDMI pclk clk\n");
> +		return PTR_ERR(hdmi->pclk);
> +	}
> +
> +	ret = clk_prepare_enable(hdmi->pclk);
> +	if (ret) {
> +		dev_err(hdmi->dev, "Cannot enable HDMI pclk clock: %d\n", ret);
> +		return ret;
> +	}
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0)
> +		return irq;
> +
> +	inno_hdmi_reset(hdmi);
> +
> +	hdmi->ddc = inno_hdmi_i2c_adapter(hdmi);
> +	if (IS_ERR(hdmi->ddc)) {
> +		hdmi->ddc = NULL;
> +		return PTR_ERR(hdmi->ddc);
> +	}
> +
> +	/*
> +	 * When IP controller haven't configured to an accurate video
> +	 * timing, then the TMDS clock source would be switched to
> +	 * PCLK_HDMI, so we need to init the TMDS rate to PCLK rate,
> +	 * and reconfigure the DDC clock.
> +	 */
> +	hdmi->tmds_rate = clk_get_rate(hdmi->pclk);
> +	inno_hdmi_i2c_init(hdmi);
> +
> +	ret = inno_hdmi_register(drm, hdmi);
> +	if (ret)
> +		return ret;
> +
> +	dev_set_drvdata(dev, hdmi);
> +
> +	/* Unmute hotplug interrupt */
> +	hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
> +
> +	ret = devm_request_threaded_irq(dev, irq, inno_hdmi_hardirq,
> +					inno_hdmi_irq, IRQF_SHARED,
> +					dev_name(dev), hdmi);
> +
> +	return ret;
> +}
> +
> +static void inno_hdmi_unbind(struct device *dev, struct device *master,
> +			     void *data)
> +{
> +	struct inno_hdmi *hdmi = dev_get_drvdata(dev);
> +
> +	hdmi->connector.funcs->destroy(&hdmi->connector);
> +	hdmi->encoder.funcs->destroy(&hdmi->encoder);
> +
> +	clk_disable_unprepare(hdmi->pclk);
> +	i2c_put_adapter(hdmi->ddc);
> +}
> +
> +static const struct component_ops inno_hdmi_ops = {
> +	.bind	= inno_hdmi_bind,
> +	.unbind	= inno_hdmi_unbind,
> +};
> +
> +static int inno_hdmi_probe(struct platform_device *pdev)
> +{
> +	return component_add(&pdev->dev, &inno_hdmi_ops);
> +}
> +
> +static int inno_hdmi_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &inno_hdmi_ops);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id inno_hdmi_dt_ids[] = {
> +	{ .compatible = "rockchip,rk3036-inno-hdmi",
> +	},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, inno_hdmi_dt_ids);
> +
> +static struct platform_driver inno_hdmi_driver = {
> +	.probe  = inno_hdmi_probe,
> +	.remove = inno_hdmi_remove,
> +	.driver = {
> +		.name = "innohdmi-rockchip",
> +		.of_match_table = inno_hdmi_dt_ids,
> +	},
> +};
> +
> +module_platform_driver(inno_hdmi_driver);
> +
> +MODULE_AUTHOR("Zheng Yang <zhengyang at rock-chips.com>");
> +MODULE_AUTHOR("Yakir Yang <ykk at rock-chips.com>");
> +MODULE_DESCRIPTION("Rockchip Specific INNO-HDMI Driver");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:innohdmi-rockchip");
> diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.h b/drivers/gpu/drm/rockchip/inno_hdmi.h
> new file mode 100644
> index 0000000..4ff17ad
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/inno_hdmi.h
> @@ -0,0 +1,364 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + *    Zheng Yang <zhengyang at rock-chips.com>
> + *    Yakir Yang <ykk at rock-chips.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef __INNO_HDMI_H__
> +#define __INNO_HDMI_H__
> +
> +#define DDC_SEGMENT_ADDR		0x30
> +
> +enum PWR_MODE {
> +	NORMAL,
> +	LOWER_PWR,
> +};
> +
> +#define HDMI_SCL_RATE			(100*1000)
> +#define DDC_BUS_FREQ_L			0x4b
> +#define DDC_BUS_FREQ_H			0x4c
> +
> +#define HDMI_SYS_CTRL			0x00
> +#define m_RST_ANALOG			(1 << 6)
> +#define v_RST_ANALOG			(0 << 6)
> +#define v_NOT_RST_ANALOG		(1 << 6)
> +#define m_RST_DIGITAL			(1 << 5)
> +#define v_RST_DIGITAL			(0 << 5)
> +#define v_NOT_RST_DIGITAL		(1 << 5)
> +#define m_REG_CLK_INV			(1 << 4)
> +#define v_REG_CLK_NOT_INV		(0 << 4)
> +#define v_REG_CLK_INV			(1 << 4)
> +#define m_VCLK_INV			(1 << 3)
> +#define v_VCLK_NOT_INV			(0 << 3)
> +#define v_VCLK_INV			(1 << 3)
> +#define m_REG_CLK_SOURCE		(1 << 2)
> +#define v_REG_CLK_SOURCE_TMDS		(0 << 2)
> +#define v_REG_CLK_SOURCE_SYS		(1 << 2)
> +#define m_POWER				(1 << 1)
> +#define v_PWR_ON			(0 << 1)
> +#define v_PWR_OFF			(1 << 1)
> +#define m_INT_POL			(1 << 0)
> +#define v_INT_POL_HIGH			1
> +#define v_INT_POL_LOW			0
> +
> +#define HDMI_VIDEO_CONTRL1		0x01
> +#define m_VIDEO_INPUT_FORMAT		(7 << 1)
> +#define m_DE_SOURCE			(1 << 0)
> +#define v_VIDEO_INPUT_FORMAT(n)		(n << 1)
> +#define v_DE_EXTERNAL			1
> +#define v_DE_INTERNAL			0
> +enum {
> +	VIDEO_INPUT_SDR_RGB444 = 0,
> +	VIDEO_INPUT_DDR_RGB444 = 5,
> +	VIDEO_INPUT_DDR_YCBCR422 = 6
> +};
> +
> +#define HDMI_VIDEO_CONTRL2		0x02
> +#define m_VIDEO_OUTPUT_COLOR		(3 << 6)
> +#define m_VIDEO_INPUT_BITS		(3 << 4)
> +#define m_VIDEO_INPUT_CSP		(1 << 0)
> +#define v_VIDEO_OUTPUT_COLOR(n)		(((n) & 0x3) << 6)
> +#define v_VIDEO_INPUT_BITS(n)		(n << 4)
> +#define v_VIDEO_INPUT_CSP(n)		(n << 0)
> +enum {
> +	VIDEO_INPUT_12BITS = 0,
> +	VIDEO_INPUT_10BITS = 1,
> +	VIDEO_INPUT_REVERT = 2,
> +	VIDEO_INPUT_8BITS = 3,
> +};
> +
> +#define HDMI_VIDEO_CONTRL		0x03
> +#define m_VIDEO_AUTO_CSC		(1 << 7)
> +#define v_VIDEO_AUTO_CSC(n)		(n << 7)
> +#define m_VIDEO_C0_C2_SWAP		(1 << 0)
> +#define v_VIDEO_C0_C2_SWAP(n)		(n << 0)
> +enum {
> +	C0_C2_CHANGE_ENABLE = 0,
> +	C0_C2_CHANGE_DISABLE = 1,
> +	AUTO_CSC_DISABLE = 0,
> +	AUTO_CSC_ENABLE = 1,
> +};
> +
> +#define HDMI_VIDEO_CONTRL3		0x04
> +#define m_COLOR_DEPTH_NOT_INDICATED	(1 << 4)
> +#define m_SOF				(1 << 3)
> +#define m_COLOR_RANGE			(1 << 2)
> +#define m_CSC				(1 << 0)
> +#define v_COLOR_DEPTH_NOT_INDICATED(n)	((n) << 4)
> +#define v_SOF_ENABLE			(0 << 3)
> +#define v_SOF_DISABLE			(1 << 3)
> +#define v_COLOR_RANGE_FULL		(1 << 2)
> +#define v_COLOR_RANGE_LIMITED		(0 << 2)
> +#define v_CSC_ENABLE			1
> +#define v_CSC_DISABLE			0
> +
> +#define HDMI_AV_MUTE			0x05
> +#define m_AVMUTE_CLEAR			(1 << 7)
> +#define m_AVMUTE_ENABLE			(1 << 6)
> +#define m_AUDIO_MUTE			(1 << 1)
> +#define m_VIDEO_BLACK			(1 << 0)
> +#define v_AVMUTE_CLEAR(n)		(n << 7)
> +#define v_AVMUTE_ENABLE(n)		(n << 6)
> +#define v_AUDIO_MUTE(n)			(n << 1)
> +#define v_VIDEO_MUTE(n)			(n << 0)
> +
> +#define HDMI_VIDEO_TIMING_CTL		0x08
> +#define v_HSYNC_POLARITY(n)		(n << 3)
> +#define v_VSYNC_POLARITY(n)		(n << 2)
> +#define v_INETLACE(n)			(n << 1)
> +#define v_EXTERANL_VIDEO(n)		(n << 0)
> +
> +#define HDMI_VIDEO_EXT_HTOTAL_L		0x09
> +#define HDMI_VIDEO_EXT_HTOTAL_H		0x0a
> +#define HDMI_VIDEO_EXT_HBLANK_L		0x0b
> +#define HDMI_VIDEO_EXT_HBLANK_H		0x0c
> +#define HDMI_VIDEO_EXT_HDELAY_L		0x0d
> +#define HDMI_VIDEO_EXT_HDELAY_H		0x0e
> +#define HDMI_VIDEO_EXT_HDURATION_L	0x0f
> +#define HDMI_VIDEO_EXT_HDURATION_H	0x10
> +#define HDMI_VIDEO_EXT_VTOTAL_L		0x11
> +#define HDMI_VIDEO_EXT_VTOTAL_H		0x12
> +#define HDMI_VIDEO_EXT_VBLANK		0x13
> +#define HDMI_VIDEO_EXT_VDELAY		0x14
> +#define HDMI_VIDEO_EXT_VDURATION	0x15
> +
> +#define HDMI_VIDEO_CSC_COEF		0x18
> +
> +#define HDMI_AUDIO_CTRL1		0x35
> +enum {
> +	CTS_SOURCE_INTERNAL = 0,
> +	CTS_SOURCE_EXTERNAL = 1,
> +};
> +#define v_CTS_SOURCE(n)			(n << 7)
> +
> +enum {
> +	DOWNSAMPLE_DISABLE = 0,
> +	DOWNSAMPLE_1_2 = 1,
> +	DOWNSAMPLE_1_4 = 2,
> +};
> +#define v_DOWN_SAMPLE(n)		(n << 5)
> +
> +enum {
> +	AUDIO_SOURCE_IIS = 0,
> +	AUDIO_SOURCE_SPDIF = 1,
> +};
> +#define v_AUDIO_SOURCE(n)		(n << 3)
> +
> +#define v_MCLK_ENABLE(n)		(n << 2)
> +enum {
> +	MCLK_128FS = 0,
> +	MCLK_256FS = 1,
> +	MCLK_384FS = 2,
> +	MCLK_512FS = 3,
> +};
> +#define v_MCLK_RATIO(n)			(n)
> +
> +#define AUDIO_SAMPLE_RATE		0x37
> +enum {
> +	AUDIO_32K = 0x3,
> +	AUDIO_441K = 0x0,
> +	AUDIO_48K = 0x2,
> +	AUDIO_882K = 0x8,
> +	AUDIO_96K = 0xa,
> +	AUDIO_1764K = 0xc,
> +	AUDIO_192K = 0xe,
> +};
> +
> +#define AUDIO_I2S_MODE			0x38
> +enum {
> +	I2S_CHANNEL_1_2 = 1,
> +	I2S_CHANNEL_3_4 = 3,
> +	I2S_CHANNEL_5_6 = 7,
> +	I2S_CHANNEL_7_8 = 0xf
> +};
> +#define v_I2S_CHANNEL(n)		((n) << 2)
> +enum {
> +	I2S_STANDARD = 0,
> +	I2S_LEFT_JUSTIFIED = 1,
> +	I2S_RIGHT_JUSTIFIED = 2,
> +};
> +#define v_I2S_MODE(n)			(n)
> +
> +#define AUDIO_I2S_MAP			0x39
> +#define AUDIO_I2S_SWAPS_SPDIF		0x3a
> +#define v_SPIDF_FREQ(n)			(n)
> +
> +#define N_32K				0x1000
> +#define N_441K				0x1880
> +#define N_882K				0x3100
> +#define N_1764K				0x6200
> +#define N_48K				0x1800
> +#define N_96K				0x3000
> +#define N_192K				0x6000
> +
> +#define HDMI_AUDIO_CHANNEL_STATUS	0x3e
> +#define m_AUDIO_STATUS_NLPCM		(1 << 7)
> +#define m_AUDIO_STATUS_USE		(1 << 6)
> +#define m_AUDIO_STATUS_COPYRIGHT	(1 << 5)
> +#define m_AUDIO_STATUS_ADDITION		(3 << 2)
> +#define m_AUDIO_STATUS_CLK_ACCURACY	(2 << 0)
> +#define v_AUDIO_STATUS_NLPCM(n)		((n & 1) << 7)
> +#define AUDIO_N_H			0x3f
> +#define AUDIO_N_M			0x40
> +#define AUDIO_N_L			0x41
> +
> +#define HDMI_AUDIO_CTS_H		0x45
> +#define HDMI_AUDIO_CTS_M		0x46
> +#define HDMI_AUDIO_CTS_L		0x47
> +
> +#define HDMI_DDC_CLK_L			0x4b
> +#define HDMI_DDC_CLK_H			0x4c
> +
> +#define HDMI_EDID_SEGMENT_POINTER	0x4d
> +#define HDMI_EDID_WORD_ADDR		0x4e
> +#define HDMI_EDID_FIFO_OFFSET		0x4f
> +#define HDMI_EDID_FIFO_ADDR		0x50
> +
> +#define HDMI_PACKET_SEND_MANUAL		0x9c
> +#define HDMI_PACKET_SEND_AUTO		0x9d
> +#define m_PACKET_GCP_EN			(1 << 7)
> +#define m_PACKET_MSI_EN			(1 << 6)
> +#define m_PACKET_SDI_EN			(1 << 5)
> +#define m_PACKET_VSI_EN			(1 << 4)
> +#define v_PACKET_GCP_EN(n)		((n & 1) << 7)
> +#define v_PACKET_MSI_EN(n)		((n & 1) << 6)
> +#define v_PACKET_SDI_EN(n)		((n & 1) << 5)
> +#define v_PACKET_VSI_EN(n)		((n & 1) << 4)
> +
> +#define HDMI_CONTROL_PACKET_BUF_INDEX	0x9f
> +enum {
> +	INFOFRAME_VSI = 0x05,
> +	INFOFRAME_AVI = 0x06,
> +	INFOFRAME_AAI = 0x08,
> +};
> +
> +#define HDMI_CONTROL_PACKET_ADDR	0xa0
> +#define HDMI_SIZE_VSI_INFOFRAME		0x0A
> +#define HDMI_SIZE_AVI_INFOFRAME		0x11
> +#define HDMI_SIZE_AUDIO_INFOFRAME	0x0F
> +enum {
> +	AVI_COLOR_MODE_RGB = 0,
> +	AVI_COLOR_MODE_YCBCR422 = 1,
> +	AVI_COLOR_MODE_YCBCR444 = 2,
> +	AVI_COLORIMETRY_NO_DATA = 0,
> +
> +	AVI_COLORIMETRY_SMPTE_170M = 1,
> +	AVI_COLORIMETRY_ITU709 = 2,
> +	AVI_COLORIMETRY_EXTENDED = 3,
> +
> +	AVI_CODED_FRAME_ASPECT_NO_DATA = 0,
> +	AVI_CODED_FRAME_ASPECT_4_3 = 1,
> +	AVI_CODED_FRAME_ASPECT_16_9 = 2,
> +
> +	ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME = 0x08,
> +	ACTIVE_ASPECT_RATE_4_3 = 0x09,
> +	ACTIVE_ASPECT_RATE_16_9 = 0x0A,
> +	ACTIVE_ASPECT_RATE_14_9 = 0x0B,
> +};
> +
> +#define HDMI_HDCP_CTRL			0x52
> +#define m_HDMI_DVI			(1 << 1)
> +#define v_HDMI_DVI(n)			(n << 1)
> +
> +#define HDMI_INTERRUPT_MASK1		0xc0
> +#define HDMI_INTERRUPT_STATUS1		0xc1
> +#define	m_INT_ACTIVE_VSYNC		(1 << 5)
> +#define m_INT_EDID_READY		(1 << 2)
> +
> +#define HDMI_INTERRUPT_MASK2		0xc2
> +#define HDMI_INTERRUPT_STATUS2		0xc3
> +#define m_INT_HDCP_ERR			(1 << 7)
> +#define m_INT_BKSV_FLAG			(1 << 6)
> +#define m_INT_HDCP_OK			(1 << 4)
> +
> +#define HDMI_STATUS			0xc8
> +#define m_HOTPLUG			(1 << 7)
> +#define m_MASK_INT_HOTPLUG		(1 << 5)
> +#define m_INT_HOTPLUG			(1 << 1)
> +#define v_MASK_INT_HOTPLUG(n)		((n & 0x1) << 5)
> +
> +#define HDMI_COLORBAR                   0xc9
> +
> +#define HDMI_PHY_SYNC			0xce
> +#define HDMI_PHY_SYS_CTL		0xe0
> +#define m_TMDS_CLK_SOURCE		(1 << 5)
> +#define v_TMDS_FROM_PLL			(0 << 5)
> +#define v_TMDS_FROM_GEN			(1 << 5)
> +#define m_PHASE_CLK			(1 << 4)
> +#define v_DEFAULT_PHASE			(0 << 4)
> +#define v_SYNC_PHASE			(1 << 4)
> +#define m_TMDS_CURRENT_PWR		(1 << 3)
> +#define v_TURN_ON_CURRENT		(0 << 3)
> +#define v_CAT_OFF_CURRENT		(1 << 3)
> +#define m_BANDGAP_PWR			(1 << 2)
> +#define v_BANDGAP_PWR_UP		(0 << 2)
> +#define v_BANDGAP_PWR_DOWN		(1 << 2)
> +#define m_PLL_PWR			(1 << 1)
> +#define v_PLL_PWR_UP			(0 << 1)
> +#define v_PLL_PWR_DOWN			(1 << 1)
> +#define m_TMDS_CHG_PWR			(1 << 0)
> +#define v_TMDS_CHG_PWR_UP		(0 << 0)
> +#define v_TMDS_CHG_PWR_DOWN		(1 << 0)
> +
> +#define HDMI_PHY_CHG_PWR		0xe1
> +#define v_CLK_CHG_PWR(n)		((n & 1) << 3)
> +#define v_DATA_CHG_PWR(n)		((n & 7) << 0)
> +
> +#define HDMI_PHY_DRIVER			0xe2
> +#define v_CLK_MAIN_DRIVER(n)		(n << 4)
> +#define v_DATA_MAIN_DRIVER(n)		(n << 0)
> +
> +#define HDMI_PHY_PRE_EMPHASIS		0xe3
> +#define v_PRE_EMPHASIS(n)		((n & 7) << 4)
> +#define v_CLK_PRE_DRIVER(n)		((n & 3) << 2)
> +#define v_DATA_PRE_DRIVER(n)		((n & 3) << 0)
> +
> +#define HDMI_PHY_FEEDBACK_DIV_RATIO_LOW		0xe7
> +#define v_FEEDBACK_DIV_LOW(n)			(n & 0xff)
> +#define HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH	0xe8
> +#define v_FEEDBACK_DIV_HIGH(n)			(n & 1)
> +
> +#define HDMI_PHY_PRE_DIV_RATIO		0xed
> +#define v_PRE_DIV_RATIO(n)		(n & 0x1f)
> +
> +#define HDMI_CEC_CTRL			0xd0
> +#define m_ADJUST_FOR_HISENSE		(1 << 6)
> +#define m_REJECT_RX_BROADCAST		(1 << 5)
> +#define m_BUSFREETIME_ENABLE		(1 << 2)
> +#define m_REJECT_RX			(1 << 1)
> +#define m_START_TX			(1 << 0)
> +
> +#define HDMI_CEC_DATA			0xd1
> +#define HDMI_CEC_TX_OFFSET		0xd2
> +#define HDMI_CEC_RX_OFFSET		0xd3
> +#define HDMI_CEC_CLK_H			0xd4
> +#define HDMI_CEC_CLK_L			0xd5
> +#define HDMI_CEC_TX_LENGTH		0xd6
> +#define HDMI_CEC_RX_LENGTH		0xd7
> +#define HDMI_CEC_TX_INT_MASK		0xd8
> +#define m_TX_DONE			(1 << 3)
> +#define m_TX_NOACK			(1 << 2)
> +#define m_TX_BROADCAST_REJ		(1 << 1)
> +#define m_TX_BUSNOTFREE			(1 << 0)
> +
> +#define HDMI_CEC_RX_INT_MASK		0xd9
> +#define m_RX_LA_ERR			(1 << 4)
> +#define m_RX_GLITCH			(1 << 3)
> +#define m_RX_DONE			(1 << 0)
> +
> +#define HDMI_CEC_TX_INT			0xda
> +#define HDMI_CEC_RX_INT			0xdb
> +#define HDMI_CEC_BUSFREETIME_L		0xdc
> +#define HDMI_CEC_BUSFREETIME_H		0xdd
> +#define HDMI_CEC_LOGICADDR		0xde
> +
> +#endif /* __INNO_HDMI_H__ */


-- 
?ark Yao




From mark.yao at rock-chips.com  Sun Jan 17 17:40:32 2016
From: mark.yao at rock-chips.com (Mark yao)
Date: Mon, 18 Jan 2016 09:40:32 +0800
Subject: [PATCH 3/3] drm/rockchip: explain why we can't wait_for_vblanks
In-Reply-To: <20160114162619.233ab39c.john@metanate.com>
References: <20160114142047.GD19130@phenom.ffwll.local>	<cover.1452782114.git.john@metanate.com>	<107bbc36a316ed0ddc7b5a8bcd9b6db6cbc71d4f.1452782114.git.john@metanate.com>	<20160114145705.GA24005@ulmo>
 <20160114162619.233ab39c.john@metanate.com>
Message-ID: <569C4290.1030902@rock-chips.com>

On 2016?01?15? 00:26, John Keeping wrote:
> On Thu, 14 Jan 2016 15:57:05 +0100, Thierry Reding wrote:
>
>> On Thu, Jan 14, 2016 at 02:39:42PM +0000, John Keeping wrote:
>>> Signed-off-by: John Keeping <john at metanate.com>
>>> ---
>>>   drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 6 ++++++
>>>   1 file changed, 6 insertions(+)
>>>
>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>>> index 679d23a..b267ce4 100644
>>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>>> @@ -177,6 +177,12 @@ static void rockchip_crtc_wait_for_update(struct drm_crtc *crtc)
>>>   		crtc_funcs->wait_for_update(crtc);
>>>   }
>>>   
>>> +/*
>>> + * We can't use drm_atomic_helper_wait_for_vblanks() because rk3288 and rk3066
>>> + * have hardware counters for neither vblanks nor scanlines.  This function is
>>> + * equivalent but uses rockchip_crtc_wait_for_update() instead of waiting for
>>> + * vblank_count to change.
>>> + */
>> This is kind of misleading. From reading earlier parts of the thread the
>> reason why drm_atomic_helper_wait_for_vblanks() won't work is because it
>> has a potential race condition that can't be detected unless you also
>> have a vblank counter. However, the above comment makes it work like
>> drm_atomic_helper_wait_for_vblanks() doesn't work in the absence of a
>> vblank counter, which isn't quite true.
> How about something like this (using the sequence from Mark's message):
>
> /*
>   * We can't use drm_atomic_helper_wait_for_vblanks() because rk3288 and rk3066
>   * have hardware counters for neither vblanks nor scanlines, which results in
>   * a race where:
>   *				| <-- HW vsync irq and reg take effect
>   *	       plane_commit --> |
>   *	get_vblank and wait --> |
>   *				| <-- handle_vblank, vblank->count + 1
>   *		 cleanup_fb --> |
>   *		iommu crash --> |
>   *				| <-- HW vsync irq and reg take effect
>   *
>   * This function is equivalent but uses rockchip_crtc_wait_for_update() instead
>   * of waiting for vblank_count to change.
>   */
>
>
>

Looks good for me, but maybe Thierry has some more advices. :-)

-- 
?ark Yao




From ykk at rock-chips.com  Mon Jan 18 02:43:23 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Mon, 18 Jan 2016 18:43:23 +0800
Subject: [PATCH v3 1/2] drm: rockchip/hdmi: add Innosilicon HDMI support
In-Reply-To: <569C3E36.10103@rock-chips.com>
References: <1452850598-30859-1-git-send-email-ykk@rock-chips.com>
 <1452850690-31760-1-git-send-email-ykk@rock-chips.com>
 <569C3E36.10103@rock-chips.com>
Message-ID: <569CC1CB.4090205@rock-chips.com>

Hi Mark,

On 01/18/2016 09:21 AM, Mark yao wrote:
> Hi Yakir
>
> I'd like you can change your patch title into "drm/rockchip/hdmi", so 
> when I search patches use "drm/rockchip" can find your patch.
>

Okay

> and I have some advices mail inline.
>
> Thanks:-)
>
> On 2016?01?15? 17:38, Yakir Yang wrote:
>> The Innosilicon HDMI is a low power HDMI 1.4 transmitter
>> IP, and it have been integrated on some rockchip CPUs
>> (like RK3036, RK312x).
>>
>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
>> ---
>> Changes in v3:
>> - Use encoder enable/disable function, and remove the encoder DPMS 
>> function
>> - Keep HDMI PLL power on in standby mode
>>
>> Changes in v2:
>> - Using DRM atomic helper functions for connector init (Mark)
>> - Remove "hdmi->connector.encoder = encoder;" (Mark)
>>
>>   drivers/gpu/drm/rockchip/Kconfig     |   8 +
>>   drivers/gpu/drm/rockchip/Makefile    |   1 +
>>   drivers/gpu/drm/rockchip/inno_hdmi.c | 999 
>> +++++++++++++++++++++++++++++++++++
>>   drivers/gpu/drm/rockchip/inno_hdmi.h | 364 +++++++++++++
>>   4 files changed, 1372 insertions(+)
>>   create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.c
>>   create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.h
>>
>> diff --git a/drivers/gpu/drm/rockchip/Kconfig 
>> b/drivers/gpu/drm/rockchip/Kconfig
>> index 35215f6..a5014e0 100644
>> --- a/drivers/gpu/drm/rockchip/Kconfig
>> +++ b/drivers/gpu/drm/rockchip/Kconfig
>> @@ -25,3 +25,11 @@ config ROCKCHIP_DW_HDMI
>>         for the Synopsys DesignWare HDMI driver. If you want to
>>         enable HDMI on RK3288 based SoC, you should selet this
>>         option.
>> +
>> +config ROCKCHIP_INNO_HDMI
>> +    tristate "Rockchip specific extensions for Innosilicon HDMI"
>> +        depends on DRM_ROCKCHIP
>> +        help
>> +      This selects support for Rockchip SoC specific extensions
>> +      for the Innosilicon HDMI driver. If you want to enable
>> +      HDMI on RK3036 based SoC, you should selet this option.
>> diff --git a/drivers/gpu/drm/rockchip/Makefile 
>> b/drivers/gpu/drm/rockchip/Makefile
>> index a9d380f..da2bf76 100644
>> --- a/drivers/gpu/drm/rockchip/Makefile
>> +++ b/drivers/gpu/drm/rockchip/Makefile
>> @@ -6,6 +6,7 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o 
>> rockchip_drm_fbdev.o \
>>           rockchip_drm_gem.o
>>     obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
>> +obj-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
>>     obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_drm_vop.o \
>>                   rockchip_vop_reg.o
>> diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c 
>> b/drivers/gpu/drm/rockchip/inno_hdmi.c
>> new file mode 100644
>> index 0000000..dc98179
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
>> @@ -0,0 +1,999 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + *    Zheng Yang <zhengyang at rock-chips.com>
>> + *    Yakir Yang <ykk at rock-chips.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/irq.h>
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/err.h>
>> +#include <linux/hdmi.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <linux/module.h>
>> +#include <linux/mutex.h>
>> +#include <linux/of_device.h>
>> +
>> +#include <drm/drm_of.h>
>> +#include <drm/drmP.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_crtc_helper.h>
>> +#include <drm/drm_edid.h>
>> +#include <drm/drm_encoder_slave.h>
>> +
>> +#include "rockchip_drm_drv.h"
>> +#include "rockchip_drm_vop.h"
>> +
>> +#include "inno_hdmi.h"
>> +
>> +#define to_inno_hdmi(x)    container_of(x, struct inno_hdmi, x)
>> +
>> +struct hdmi_data_info {
>> +    int vic;
>> +    bool sink_is_hdmi;
>> +    bool sink_has_audio;
>> +    unsigned int enc_in_format;
>> +    unsigned int enc_out_format;
>> +    unsigned int colorimetry;
>> +};
>> +
>> +struct inno_hdmi_i2c {
>> +    struct i2c_adapter adap;
>> +
>> +    u8 ddc_addr;
>> +    u8 segment_addr;
>> +
>> +    struct mutex lock;
>> +    struct completion cmp;
>> +};
>> +
>> +struct inno_hdmi {
>> +    struct device *dev;
>> +    struct drm_device *drm_dev;
>> +
>> +    int irq;
>> +    struct clk *pclk;
>> +    void __iomem *regs;
>> +
>> +    struct drm_connector    connector;
>> +    struct drm_encoder    encoder;
>> +
>> +    struct inno_hdmi_i2c *i2c;
>> +    struct i2c_adapter *ddc;
>> +
>> +    unsigned int tmds_rate;
>> +
>> +    struct hdmi_data_info    hdmi_data;
>> +    struct drm_display_mode previous_mode;
>> +};
>> +
>> +enum {
>> +    CSC_ITU601_16_235_TO_RGB_0_255_8BIT,
>> +    CSC_ITU601_0_255_TO_RGB_0_255_8BIT,
>> +    CSC_ITU709_16_235_TO_RGB_0_255_8BIT,
>> +    CSC_RGB_0_255_TO_ITU601_16_235_8BIT,
>> +    CSC_RGB_0_255_TO_ITU709_16_235_8BIT,
>> +    CSC_RGB_0_255_TO_RGB_16_235_8BIT,
>> +};
>> +
>> +static const char coeff_csc[][24] = {
>> +    /*
>> +     * YUV2RGB:601 SD mode(Y[16:235], UV[16:240], RGB[0:255]):
>> +     *   R = 1.164*Y + 1.596*V - 204
>> +     *   G = 1.164*Y - 0.391*U - 0.813*V + 154
>> +     *   B = 1.164*Y + 2.018*U - 258
>> +     */
>> +    {
>> +        0x04, 0xa7, 0x00, 0x00, 0x06, 0x62, 0x02, 0xcc,
>> +        0x04, 0xa7, 0x11, 0x90, 0x13, 0x40, 0x00, 0x9a,
>> +        0x04, 0xa7, 0x08, 0x12, 0x00, 0x00, 0x03, 0x02
>> +    },
>> +    /*
>> +     * YUV2RGB:601 SD mode(YUV[0:255],RGB[0:255]):
>> +     *   R = Y + 1.402*V - 248
>> +     *   G = Y - 0.344*U - 0.714*V + 135
>> +     *   B = Y + 1.772*U - 227
>> +     */
>> +    {
>> +        0x04, 0x00, 0x00, 0x00, 0x05, 0x9b, 0x02, 0xf8,
>> +        0x04, 0x00, 0x11, 0x60, 0x12, 0xdb, 0x00, 0x87,
>> +        0x04, 0x00, 0x07, 0x16, 0x00, 0x00, 0x02, 0xe3
>> +    },
>> +    /*
>> +     * YUV2RGB:709 HD mode(Y[16:235],UV[16:240],RGB[0:255]):
>> +     *   R = 1.164*Y + 1.793*V - 248
>> +     *   G = 1.164*Y - 0.213*U - 0.534*V + 77
>> +     *   B = 1.164*Y + 2.115*U - 289
>> +     */
>> +    {
>> +        0x04, 0xa7, 0x00, 0x00, 0x07, 0x2c, 0x02, 0xf8,
>> +        0x04, 0xa7, 0x10, 0xda, 0x12, 0x22, 0x00, 0x4d,
>> +        0x04, 0xa7, 0x08, 0x74, 0x00, 0x00, 0x03, 0x21
>> +    },
>> +
>> +    /*
>> +     * RGB2YUV:601 SD mode:
>> +     *   Cb = -0.291G - 0.148R + 0.439B + 128
>> +     *   Y  = 0.504G  + 0.257R + 0.098B + 16
>> +     *   Cr = -0.368G + 0.439R - 0.071B + 128
>> +     */
>> +    {
>> +        0x11, 0x5f, 0x01, 0x82, 0x10, 0x23, 0x00, 0x80,
>> +        0x02, 0x1c, 0x00, 0xa1, 0x00, 0x36, 0x00, 0x1e,
>> +        0x11, 0x29, 0x10, 0x59, 0x01, 0x82, 0x00, 0x80
>> +    },
>> +    /*
>> +     * RGB2YUV:709 HD mode:
>> +     *   Cb = - 0.338G - 0.101R + 0.439B + 128
>> +     *   Y  = 0.614G   + 0.183R + 0.062B + 16
>> +     *   Cr = - 0.399G + 0.439R - 0.040B + 128
>> +     */
>> +    {
>> +        0x11, 0x98, 0x01, 0xc1, 0x10, 0x28, 0x00, 0x80,
>> +        0x02, 0x74, 0x00, 0xbb, 0x00, 0x3f, 0x00, 0x10,
>> +        0x11, 0x5a, 0x10, 0x67, 0x01, 0xc1, 0x00, 0x80
>> +    },
>> +    /*
>> +     * RGB[0:255]2RGB[16:235]:
>> +     *   R' = R x (235-16)/255 + 16;
>> +     *   G' = G x (235-16)/255 + 16;
>> +     *   B' = B x (235-16)/255 + 16;
>> +     */
>> +    {
>> +        0x00, 0x00, 0x03, 0x6F, 0x00, 0x00, 0x00, 0x10,
>> +        0x03, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
>> +        0x00, 0x00, 0x00, 0x00, 0x03, 0x6F, 0x00, 0x10
>> +    },
>> +};
>> +
>> +static inline u8 hdmi_readb(struct inno_hdmi *hdmi, u16 offset)
>> +{
>> +    return readl_relaxed(hdmi->regs + (offset) * 0x04);
>> +}
>> +
>> +static inline void hdmi_writeb(struct inno_hdmi *hdmi, u16 offset, 
>> u32 val)
>> +{
>> +    writel_relaxed(val, hdmi->regs + (offset) * 0x04);
>> +}
>> +
>> +static inline void hdmi_modb(struct inno_hdmi *hdmi, u16 offset,
>> +                 u32 msk, u32 val)
>> +{
>> +    u8 temp = hdmi_readb(hdmi, offset) & ~msk;
>> +
>> +    temp |= val & msk;
>> +    hdmi_writeb(hdmi, offset, temp);
>> +}
>> +
>> +static void inno_hdmi_i2c_init(struct inno_hdmi *hdmi)
>> +{
>> +    int ddc_bus_freq;
>> +
>> +    ddc_bus_freq = (hdmi->tmds_rate >> 2) / HDMI_SCL_RATE;
>> +
>> +    hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
>> +    hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
>> +
>> +    /* Clear the EDID interrupt flag and mute the interrupt */
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
>> +}
>> +
>> +static void inno_hdmi_sys_power(struct inno_hdmi *hdmi, bool enable)
>> +{
>> +    if (enable)
>> +        hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON);
>> +    else
>> +        hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
>> +}
>> +
>> +static void inno_hdmi_set_pwr_mode(struct inno_hdmi *hdmi, int mode)
>> +{
>> +    switch (mode) {
>> +    case NORMAL:
>> +        inno_hdmi_sys_power(hdmi, false);
>> +
>> +        hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x6f);
>> +        hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0xbb);
>> +
>> +        hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
>> +        hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x14);
>> +        hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x10);
>> +        hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x0f);
>> +        hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x00);
>> +        hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x01);
>> +
>> +        inno_hdmi_sys_power(hdmi, true);
>> +        break;
>> +
>> +    case LOWER_PWR:
>> +        inno_hdmi_sys_power(hdmi, false);
>> +        hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0x00);
>> +        hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x00);
>> +        hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x00);
>> +        hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
>> +
>> +        break;
>> +
>> +    default:
>> +        dev_err(hdmi->dev, "Unknown power mode %d\n", mode);
>> +    }
>> +}
>> +
>> +static void inno_hdmi_reset(struct inno_hdmi *hdmi)
>> +{
>> +    u32 val;
>> +    u32 msk;
>> +
>> +    hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_DIGITAL, v_NOT_RST_DIGITAL);
>> +    udelay(100);
>> +
>> +    hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_ANALOG, v_NOT_RST_ANALOG);
>> +    udelay(100);
>> +
>> +    msk = m_REG_CLK_INV | m_REG_CLK_SOURCE | m_POWER | m_INT_POL;
>> +    val = v_REG_CLK_INV | v_REG_CLK_SOURCE_SYS | v_PWR_ON | 
>> v_INT_POL_HIGH;
>> +    hdmi_modb(hdmi, HDMI_SYS_CTRL, msk, val);
>> +
>> +    inno_hdmi_set_pwr_mode(hdmi, NORMAL);
>> +}
>> +
>> +static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi)
>> +{
>> +    char info[HDMI_SIZE_AVI_INFOFRAME] = {0};
>> +    int avi_color_mode;
>> +    int i;
>> +
>> +    hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_AVI);
>> +
>> +    info[0] = 0x82;
>> +    info[1] = 0x02;
>> +    info[2] = 0x0D;
>> +    info[3] = info[0] + info[1] + info[2];
>> +
>> +    if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_RGB)
>> +        avi_color_mode = AVI_COLOR_MODE_RGB;
>> +    else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV444)
>> +        avi_color_mode = AVI_COLOR_MODE_YCBCR444;
>> +    else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV422)
>> +        avi_color_mode = AVI_COLOR_MODE_YCBCR422;
>> +    else
>> +        avi_color_mode = AVI_COLOR_MODE_RGB;
>> +
>> +    info[4] = (avi_color_mode << 5);
>> +    info[5] = (AVI_COLORIMETRY_NO_DATA << 6) |
>> +          (AVI_CODED_FRAME_ASPECT_NO_DATA << 4) |
>> +          ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME;
>> +
>> +    info[6] = 0;
>> +    info[7] = hdmi->hdmi_data.vic;
>> +
>> +    if (hdmi->hdmi_data.vic == 6 || hdmi->hdmi_data.vic == 7 ||
>> +        hdmi->hdmi_data.vic == 21 || hdmi->hdmi_data.vic == 22)
>> +        info[8] = 1;
>> +    else
>> +        info[8] = 0;
>> +
>> +    /* Calculate avi info frame checKsum */
>> +    for (i = 4; i < HDMI_SIZE_AVI_INFOFRAME; i++)
>> +        info[3] += info[i];
>> +    info[3] = 0x100 - info[3];
>> +
>> +    for (i = 0; i < HDMI_SIZE_AVI_INFOFRAME; i++)
>> +        hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i, info[i]);
>> +
>> +    return 0;
>> +}
>> +
>> +static int inno_hdmi_config_video_vsi(struct inno_hdmi *hdmi)
>> +{
>> +    char info[HDMI_SIZE_VSI_INFOFRAME] = {0};
>> +    int i;
>> +
>> +    hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, m_PACKET_VSI_EN,
>> +          v_PACKET_VSI_EN(0));
>> +
>> +    hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_VSI);
>> +
>> +    /* Header Bytes */
>> +    info[0] = 0x81;
>> +    info[1] = 0x01;
>> +
>> +    /* PB1 - PB3 contain the 24bit IEEE Registration Identifier */
>> +    info[4] = 0x03;
>> +    info[5] = 0x0c;
>> +    info[6] = 0x00;
>> +
>> +    /* PB4 - HDMI_Video_Format into bits 7:5 */
>> +    info[7] = 0;
>> +
>> +    /*
>> +     * PB5 - Depending on the video format, this byte will contain
>> +     * either the HDMI_VIC code in buts 7:0, OR the 3D_Structure in
>> +     * bits 7:4.
>> +     */
>> +    info[2] = 0x06 - 2;
>> +    info[8] = 0;
>> +    info[9] = 0;
>> +
>> +    info[3] = info[0] + info[1] + info[2];
>> +
>> +    /* Calculate info frame checKsum */
>> +    for (i = 4; i < HDMI_SIZE_VSI_INFOFRAME; i++)
>> +        info[3] += info[i];
>> +    info[3] = 0x100 - info[3];
>> +
>> +    for (i = 0; i < HDMI_SIZE_VSI_INFOFRAME; i++)
>> +        hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i, info[i]);
>> +
>> +    hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, m_PACKET_VSI_EN,
>> +          v_PACKET_VSI_EN(1));
>> +
>> +    return 0;
>> +}
>> +
>> +static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
>> +{
>> +    struct hdmi_data_info *data = &hdmi->hdmi_data;
>> +    int c0_c2_change = 0;
>> +    int csc_enable = 0;
>> +    int csc_mode = 0;
>> +    int auto_csc = 0;
>> +    int value;
>> +    int i;
>> +
>> +    /* Input video mode is SDR RGB24bit, data enable signal from 
>> external */
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL1, v_DE_EXTERNAL |
>> +            v_VIDEO_INPUT_FORMAT(VIDEO_INPUT_SDR_RGB444));
>> +
>> +    /* Input color hardcode to RGB, and output color hardcode to 
>> RGB888 */
>> +    value = v_VIDEO_INPUT_BITS(VIDEO_INPUT_8BITS) |
>> +        v_VIDEO_OUTPUT_COLOR(0) |
>> +        v_VIDEO_INPUT_CSP(0);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL2, value);
>> +
>> +    if (data->enc_out_format == data->enc_out_format) {
>> +        if ((data->enc_in_format == HDMI_COLORSPACE_RGB) ||
>> +            (data->enc_in_format >= HDMI_COLORSPACE_YUV444)) {
>> +            value = v_SOF_DISABLE | v_COLOR_DEPTH_NOT_INDICATED(1);
>> +            hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
>> +
>> +            hdmi_modb(hdmi, HDMI_VIDEO_CONTRL,
>> +                  m_VIDEO_AUTO_CSC | m_VIDEO_C0_C2_SWAP,
>> +                  v_VIDEO_AUTO_CSC(AUTO_CSC_DISABLE) |
>> +                  v_VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE));
>> +            return 0;
>> +        }
>> +    }
>> +
>> +    if (data->colorimetry == HDMI_COLORIMETRY_ITU_601) {
>> +        if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
>> +            (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
>> +            csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT;
>> +            auto_csc = AUTO_CSC_DISABLE;
>> +            c0_c2_change = C0_C2_CHANGE_DISABLE;
>> +            csc_enable = v_CSC_ENABLE;
>> +        } else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
>> +               (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
>> +            csc_mode = CSC_ITU601_16_235_TO_RGB_0_255_8BIT;
>> +            auto_csc = AUTO_CSC_ENABLE;
>> +            c0_c2_change = C0_C2_CHANGE_DISABLE;
>> +            csc_enable = v_CSC_DISABLE;
>> +        }
>> +    } else {
>> +        if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
>> +            (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
>> +            csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT;
>> +            auto_csc = AUTO_CSC_DISABLE;
>> +            c0_c2_change = C0_C2_CHANGE_DISABLE;
>> +            csc_enable = v_CSC_ENABLE;
>> +        } else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
>> +               (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
>> +            csc_mode = CSC_ITU709_16_235_TO_RGB_0_255_8BIT;
>> +            auto_csc = AUTO_CSC_ENABLE;
>> +            c0_c2_change = C0_C2_CHANGE_DISABLE;
>> +            csc_enable = v_CSC_DISABLE;
>> +        }
>> +    }
>> +
>> +    for (i = 0; i < 24; i++)
>> +        hdmi_writeb(hdmi, HDMI_VIDEO_CSC_COEF + i,
>> +                coeff_csc[csc_mode][i]);
>> +
>> +    value = v_SOF_DISABLE | csc_enable | 
>> v_COLOR_DEPTH_NOT_INDICATED(1);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
>> +    hdmi_modb(hdmi, HDMI_VIDEO_CONTRL, m_VIDEO_AUTO_CSC |
>> +          m_VIDEO_C0_C2_SWAP, v_VIDEO_AUTO_CSC(auto_csc) |
>> +          v_VIDEO_C0_C2_SWAP(c0_c2_change));
>> +
>> +    return 0;
>> +}
>> +
>> +static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi,
>> +                     struct drm_display_mode *mode)
>> +{
>> +    int value;
>> +
>> +    /* Set detail external video timing polarity and interlace mode */
>> +    value = v_EXTERANL_VIDEO(1);
>> +    value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
>> +         v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
>> +    value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
>> +         v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
>> +    value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
>> +         v_INETLACE(1) : v_INETLACE(0);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
>> +
>> +    /* Set detail external video timing */
>> +    value = mode->htotal;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
>> +
>> +    value = mode->htotal - mode->hdisplay;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
>> +
>> +    value = mode->hsync_start - mode->hdisplay;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
>> +
>> +    value = mode->hsync_end - mode->hsync_start;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
>> +
>> +    value = mode->vtotal;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
>> +
>> +    value = mode->vtotal - mode->vdisplay;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
>> +
>> +    value = mode->vsync_start - mode->vdisplay;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
>> +
>> +    value = mode->vsync_end - mode->vsync_start;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
>> +
>> +    hdmi_writeb(hdmi, HDMI_PHY_PRE_DIV_RATIO, 0x1e);
>> +    hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_LOW, 0x2c);
>> +    hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH, 0x01);
>> +
>> +    return 0;
>> +}
>> +
>> +static int inno_hdmi_setup(struct inno_hdmi *hdmi,
>> +               struct drm_display_mode *mode)
>> +{
>> +    int value;
>> +
>> +    hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
>> +
>> +    hdmi->hdmi_data.enc_in_format = HDMI_COLORSPACE_RGB;
>> +    hdmi->hdmi_data.enc_out_format = HDMI_COLORSPACE_RGB;
>> +
>> +    if ((hdmi->hdmi_data.vic == 6) || (hdmi->hdmi_data.vic == 7) ||
>> +        (hdmi->hdmi_data.vic == 21) || (hdmi->hdmi_data.vic == 22) ||
>> +        (hdmi->hdmi_data.vic == 2) || (hdmi->hdmi_data.vic == 3) ||
>> +        (hdmi->hdmi_data.vic == 17) || (hdmi->hdmi_data.vic == 18))
>> +        hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601;
>> +    else
>> +        hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
>> +
>> +    /* Mute video and audio output */
>> +    hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
>> +          v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1));
>> +
>> +    /* Set HDMI Mode */
>> +    hdmi_writeb(hdmi, HDMI_HDCP_CTRL,
>> +            v_HDMI_DVI(hdmi->hdmi_data.sink_is_hdmi));
>> +
>> +    inno_hdmi_config_video_timing(hdmi, mode);
>> +
>> +    inno_hdmi_config_video_csc(hdmi);
>> +
>> +    if (hdmi->hdmi_data.sink_is_hdmi) {
>> +        inno_hdmi_config_video_avi(hdmi);
>> +        inno_hdmi_config_video_vsi(hdmi);
>> +    }
>> +
>> +    /*
>> +     * When IP controller have configured to an accurate video
>> +     * timing, then the TMDS clock source would be switched to
>> +     * DCLK_LCDC, so we need to init the TMDS rate to mode pixel
>> +     * clock rate, and reconfigure the DDC clock.
>> +     */
>> +    hdmi->tmds_rate = mode->clock * 1000;
>> +    inno_hdmi_i2c_init(hdmi);
>> +
>> +    /* Unmute video and audio output */
>> +    hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
>> +          v_AUDIO_MUTE(0) | v_VIDEO_MUTE(0));
>> +
>> +    return 0;
>> +}
>> +
>> +static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder,
>> +                       struct drm_display_mode *mode,
>> +                       struct drm_display_mode *adj_mode)
>> +{
>> +    struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
>> +
>> +    inno_hdmi_setup(hdmi, adj_mode);
>> +
>> +    /* Store the display mode for plugin/DKMS poweron events */
>
> I think DKMS should be DPMS.

Yep, done

>
>> +    memcpy(&hdmi->previous_mode, adj_mode, 
>> sizeof(hdmi->previous_mode));
>> +}
>> +
>> +static void inno_hdmi_encoder_enable(struct drm_encoder *encoder)
>> +{
>> +    struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
>> +
>> +    inno_hdmi_set_pwr_mode(hdmi, NORMAL);
>> +}
>> +
>> +static void inno_hdmi_encoder_disable(struct drm_encoder *encoder)
>> +{
>> +    struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
>> +
>> +    inno_hdmi_set_pwr_mode(hdmi, LOWER_PWR);
>> +}
>> +
>> +static void inno_hdmi_encoder_commit(struct drm_encoder *encoder)
>> +{
>> +}
>> +
>> +static void inno_hdmi_encoder_prepare(struct drm_encoder *encoder)
>> +{
>> +    rockchip_drm_crtc_mode_config(encoder->crtc, 
>> DRM_MODE_CONNECTOR_HDMIA,
>> +                      ROCKCHIP_OUT_MODE_P888);
>
> Can you move mode_config into inno_hdmi_encoder_enable, and remove 
> .prepare and .commit?
>

Yes, done

>> +}
>> +
>> +static bool inno_hdmi_encoder_mode_fixup(struct drm_encoder *encoder,
>> +                     const struct drm_display_mode *mode,
>> +                     struct drm_display_mode *adj_mode)
>> +{
>> +    return true;
>> +}
>> +
>> +static struct drm_encoder_helper_funcs 
>> inno_hdmi_encoder_helper_funcs = {
>> +    .enable     = inno_hdmi_encoder_enable,
>> +    .disable    = inno_hdmi_encoder_disable,
>> +    .mode_fixup = inno_hdmi_encoder_mode_fixup,
>> +    .mode_set   = inno_hdmi_encoder_mode_set,
>> +    .prepare    = inno_hdmi_encoder_prepare,
>> +    .commit     = inno_hdmi_encoder_commit,
>
> On drm atomic, I think if support .enable and .disable callbacks, then 
> .prepare and .commit is not needed.
>

Got it

>> +};
>> +
>> +static struct drm_encoder_funcs inno_hdmi_encoder_funcs = {
>> +    .destroy = drm_encoder_cleanup,
>> +};
>> +
>> +static enum drm_connector_status
>> +inno_hdmi_connector_detect(struct drm_connector *connector, bool force)
>> +{
>> +    struct inno_hdmi *hdmi = to_inno_hdmi(connector);
>> +
>> +    return (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
>> +        connector_status_connected : connector_status_disconnected;
>> +}
>> +
>> +static int inno_hdmi_connector_get_modes(struct drm_connector 
>> *connector)
>> +{
>> +    struct inno_hdmi *hdmi = to_inno_hdmi(connector);
>> +    struct edid *edid;
>> +    int ret = 0;
>> +
>> +    if (!hdmi->ddc)
>> +        return 0;
>> +
>> +    edid = drm_get_edid(connector, hdmi->ddc);
>> +    if (edid) {
>> +        hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
>> +        hdmi->hdmi_data.sink_has_audio = 
>> drm_detect_monitor_audio(edid);
>> +        drm_mode_connector_update_edid_property(connector, edid);
>> +        ret = drm_add_edid_modes(connector, edid);
>> +        kfree(edid);
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +static enum drm_mode_status
>> +inno_hdmi_connector_mode_valid(struct drm_connector *connector,
>> +                   struct drm_display_mode *mode)
>> +{
>> +    return MODE_OK;
>> +}
>> +
>> +static struct drm_encoder *
>> +inno_hdmi_connector_best_encoder(struct drm_connector *connector)
>> +{
>> +    struct inno_hdmi *hdmi = to_inno_hdmi(connector);
>> +
>> +    return &hdmi->encoder;
>> +}
>> +
>> +static int
>> +inno_hdmi_probe_single_connector_modes(struct drm_connector *connector,
>> +                       uint32_t maxX, uint32_t maxY)
>> +{
>> +    return drm_helper_probe_single_connector_modes(connector, 1920, 
>> 1080);
>> +}
>> +
>> +static void inno_hdmi_connector_destroy(struct drm_connector 
>> *connector)
>> +{
>> +    drm_connector_unregister(connector);
>> +    drm_connector_cleanup(connector);
>> +}
>> +
>> +static struct drm_connector_funcs inno_hdmi_connector_funcs = {
>> +    .dpms = drm_atomic_helper_connector_dpms,
>> +    .fill_modes = inno_hdmi_probe_single_connector_modes,
>> +    .detect = inno_hdmi_connector_detect,
>> +    .destroy = inno_hdmi_connector_destroy,
>> +    .reset = drm_atomic_helper_connector_reset,
>> +    .atomic_duplicate_state = 
>> drm_atomic_helper_connector_duplicate_state,
>> +    .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
>> +};
>> +
>> +static struct drm_connector_helper_funcs 
>> inno_hdmi_connector_helper_funcs = {
>> +    .get_modes = inno_hdmi_connector_get_modes,
>> +    .mode_valid = inno_hdmi_connector_mode_valid,
>> +    .best_encoder = inno_hdmi_connector_best_encoder,
>> +};
>> +
>> +static int inno_hdmi_register(struct drm_device *drm, struct 
>> inno_hdmi *hdmi)
>> +{
>> +    struct drm_encoder *encoder = &hdmi->encoder;
>> +
>> +    encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, 
>> hdmi->dev->of_node);
>
> Keep 80 characters :-) .
>
Done
>> +    /*
>> +     * If we failed to find the CRTC(s) which this encoder is
>> +     * supposed to be connected to, it's because the CRTC has
>> +     * not been registered yet.  Defer probing, and hope that
>> +     * the required CRTC is added later.
>> +     */
>> +    if (encoder->possible_crtcs == 0)
>> +        return -EPROBE_DEFER;
>> +
>> +    drm_encoder_helper_add(encoder, &inno_hdmi_encoder_helper_funcs);
>> +    drm_encoder_init(drm, encoder, &inno_hdmi_encoder_funcs,
>> +             DRM_MODE_ENCODER_TMDS, NULL);
>> +
>> +    hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
>> +
>> +    drm_connector_helper_add(&hdmi->connector,
>> +                 &inno_hdmi_connector_helper_funcs);
>> +    drm_connector_init(drm, &hdmi->connector, 
>> &inno_hdmi_connector_funcs,
>> +               DRM_MODE_CONNECTOR_HDMIA);
>> +
>> +    drm_mode_connector_attach_encoder(&hdmi->connector, encoder);
>> +
>> +    return 0;
>> +}
>> +
>> +static irqreturn_t inno_hdmi_i2c_irq(struct inno_hdmi *hdmi)
>> +{
>> +    struct inno_hdmi_i2c *i2c = hdmi->i2c;
>> +    u8 stat;
>> +
>> +    stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
>> +    if (!(stat & m_INT_EDID_READY))
>> +        return IRQ_NONE;
>> +
>> +    /* Clear HDMI EDID interrupt flag */
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
>> +
>> +    complete(&i2c->cmp);
>> +
>> +    return IRQ_HANDLED;
>> +}
>> +
>> +static irqreturn_t inno_hdmi_hardirq(int irq, void *dev_id)
>> +{
>> +    struct inno_hdmi *hdmi = dev_id;
>> +    irqreturn_t ret = IRQ_NONE;
>> +    u8 interrupt;
>> +
>> +    if (hdmi->i2c)
>> +        ret = inno_hdmi_i2c_irq(hdmi);
>> +
>> +    interrupt = hdmi_readb(hdmi, HDMI_STATUS);
>> +    if (interrupt & m_INT_HOTPLUG) {
>> +        hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG);
>> +        ret = IRQ_WAKE_THREAD;
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +static irqreturn_t inno_hdmi_irq(int irq, void *dev_id)
>> +{
>> +    struct inno_hdmi *hdmi = dev_id;
>> +
>> +    drm_helper_hpd_irq_event(hdmi->connector.dev);
>> +
>> +    return IRQ_HANDLED;
>> +}
>> +
>> +static int inno_hdmi_i2c_wait(struct inno_hdmi *hdmi)
>> +{
>> +    struct inno_hdmi_i2c *i2c = hdmi->i2c;
>> +    int stat;
>> +
>> +    stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
>> +    if (!stat) {
>> +        stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
>> +        if (!stat)
>> +            return -EAGAIN;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static int inno_hdmi_i2c_read(struct inno_hdmi *hdmi, struct i2c_msg 
>> *msgs)
>> +{
>> +    int length = msgs->len;
>> +    u8 *buf = msgs->buf;
>> +    int ret;
>> +
>> +    ret = inno_hdmi_i2c_wait(hdmi);
>> +    if (ret)
>> +        return ret;
>> +
>> +    while (length--)
>> +        *buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
>> +
>> +    return 0;
>> +}
>> +
>> +static int inno_hdmi_i2c_write(struct inno_hdmi *hdmi, struct 
>> i2c_msg *msgs)
>> +{
>> +    struct inno_hdmi_i2c *i2c = hdmi->i2c;
>> +
>> +    /*
>> +     * The DDC module only support read EDID message, so
>> +     * we assume that each word write to this i2c adapter
>> +     * should be the offset of EDID word address.
>> +     */
>> +    if ((msgs->len != 1) ||
>> +        ((msgs->addr != DDC_ADDR) && (msgs->addr != DDC_SEGMENT_ADDR)))
>> +        return -EINVAL;
>> +
>> +    reinit_completion(&i2c->cmp);
>> +
>> +    if (msgs->addr == DDC_SEGMENT_ADDR)
>> +        hdmi->i2c->segment_addr = msgs->buf[0];
>> +    if (msgs->addr == DDC_ADDR)
>> +        hdmi->i2c->ddc_addr = msgs->buf[0];
>> +
>> +    /* Set edid fifo first addr */
>> +    hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
>> +
>> +    /* Set edid word address 0x00/0x80 */
>> +    hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
>> +
>> +    /* Set edid segment pointer */
>> +    hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, 
>> hdmi->i2c->segment_addr);
>> +
>> +    return 0;
>> +}
>> +
>> +static int inno_hdmi_i2c_xfer(struct i2c_adapter *adap,
>> +                  struct i2c_msg *msgs, int num)
>> +{
>> +    struct inno_hdmi *hdmi = i2c_get_adapdata(adap);
>> +    struct inno_hdmi_i2c *i2c = hdmi->i2c;
>> +    int i, ret = 0;
>> +
>> +    mutex_lock(&i2c->lock);
>> +
>> +    /* Clear the EDID interrupt flag and unmute the interrupt */
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY);
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
>> +
>> +    for (i = 0; i < num; i++) {
>> +        dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n",
>> +            i + 1, num, msgs[i].len, msgs[i].flags);
>> +
>> +        if (msgs[i].flags & I2C_M_RD)
>> +            ret = inno_hdmi_i2c_read(hdmi, &msgs[i]);
>> +        else
>> +            ret = inno_hdmi_i2c_write(hdmi, &msgs[i]);
>> +
>> +        if (ret < 0)
>> +            break;
>> +    }
>> +
>> +    if (!ret)
>> +        ret = num;
>> +
>> +    /* Mute HDMI EDID interrupt */
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
>> +
>> +    mutex_unlock(&i2c->lock);
>> +
>> +    return ret;
>> +}
>> +
>> +static u32 inno_hdmi_i2c_func(struct i2c_adapter *adapter)
>> +{
>> +    return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
>> +}
>> +
>> +static const struct i2c_algorithm inno_hdmi_algorithm = {
>> +    .master_xfer    = inno_hdmi_i2c_xfer,
>> +    .functionality    = inno_hdmi_i2c_func,
>> +};
>> +
>> +static struct i2c_adapter *inno_hdmi_i2c_adapter(struct inno_hdmi 
>> *hdmi)
>> +{
>> +    struct i2c_adapter *adap;
>> +    struct inno_hdmi_i2c *i2c;
>> +    int ret;
>> +
>> +    i2c = devm_kzalloc(hdmi->dev, sizeof(*i2c), GFP_KERNEL);
>> +    if (!i2c)
>> +        return ERR_PTR(-ENOMEM);
>> +
>> +    mutex_init(&i2c->lock);
>> +    init_completion(&i2c->cmp);
>> +
>> +    adap = &i2c->adap;
>> +    adap->class = I2C_CLASS_DDC;
>> +    adap->owner = THIS_MODULE;
>> +    adap->dev.parent = hdmi->dev;
>> +    adap->dev.of_node = hdmi->dev->of_node;
>> +    adap->algo = &inno_hdmi_algorithm;
>> +    strlcpy(adap->name, "Inno HDMI", sizeof(adap->name));
>> +    i2c_set_adapdata(adap, hdmi);
>> +
>> +    ret = i2c_add_adapter(adap);
>> +    if (ret) {
>> +        dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name);
>> +        devm_kfree(hdmi->dev, i2c);
>> +        return ERR_PTR(ret);
>> +    }
>> +
>> +    hdmi->i2c = i2c;
>> +
>> +    dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name);
>> +
>> +    return adap;
>> +}
>> +
>> +static int inno_hdmi_bind(struct device *dev, struct device *master,
>> +                 void *data)
>> +{
>> +    struct platform_device *pdev = to_platform_device(dev);
>> +    struct drm_device *drm = data;
>> +    struct inno_hdmi *hdmi;
>> +    struct resource *iores;
>> +    int irq;
>> +    int ret;
>> +
>> +    hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
>> +    if (!hdmi)
>> +        return -ENOMEM;
>> +
>> +    hdmi->dev = dev;
>> +    hdmi->drm_dev = drm;
>> +
>> +    iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +    if (!iores)
>> +        return -ENXIO;
>> +
>> +    hdmi->regs = devm_ioremap_resource(dev, iores);
>> +    if (IS_ERR(hdmi->regs))
>> +        return PTR_ERR(hdmi->regs);
>> +
>> +    hdmi->pclk = devm_clk_get(hdmi->dev, "pclk");
>> +    if (IS_ERR(hdmi->pclk)) {
>> +        dev_err(hdmi->dev, "Unable to get HDMI pclk clk\n");
>> +        return PTR_ERR(hdmi->pclk);
>> +    }
>> +
>> +    ret = clk_prepare_enable(hdmi->pclk);
>> +    if (ret) {
>> +        dev_err(hdmi->dev, "Cannot enable HDMI pclk clock: %d\n", ret);
>> +        return ret;
>> +    }
>> +
>> +    irq = platform_get_irq(pdev, 0);
>> +    if (irq < 0)
>> +        return irq;
>> +
>> +    inno_hdmi_reset(hdmi);
>> +
>> +    hdmi->ddc = inno_hdmi_i2c_adapter(hdmi);
>> +    if (IS_ERR(hdmi->ddc)) {
>> +        hdmi->ddc = NULL;
>> +        return PTR_ERR(hdmi->ddc);
>> +    }
>> +
>> +    /*
>> +     * When IP controller haven't configured to an accurate video
>> +     * timing, then the TMDS clock source would be switched to
>> +     * PCLK_HDMI, so we need to init the TMDS rate to PCLK rate,
>> +     * and reconfigure the DDC clock.
>> +     */
>> +    hdmi->tmds_rate = clk_get_rate(hdmi->pclk);
>> +    inno_hdmi_i2c_init(hdmi);
>> +
>> +    ret = inno_hdmi_register(drm, hdmi);
>> +    if (ret)
>> +        return ret;
>> +
>> +    dev_set_drvdata(dev, hdmi);
>> +
>> +    /* Unmute hotplug interrupt */
>> +    hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, 
>> v_MASK_INT_HOTPLUG(1));
>> +
>> +    ret = devm_request_threaded_irq(dev, irq, inno_hdmi_hardirq,
>> +                    inno_hdmi_irq, IRQF_SHARED,
>> +                    dev_name(dev), hdmi);
>> +
>> +    return ret;
>> +}
>> +
>> +static void inno_hdmi_unbind(struct device *dev, struct device *master,
>> +                 void *data)
>> +{
>> +    struct inno_hdmi *hdmi = dev_get_drvdata(dev);
>> +
>> + hdmi->connector.funcs->destroy(&hdmi->connector);
>> +    hdmi->encoder.funcs->destroy(&hdmi->encoder);
>> +
>> +    clk_disable_unprepare(hdmi->pclk);
>> +    i2c_put_adapter(hdmi->ddc);
>> +}
>> +
>> +static const struct component_ops inno_hdmi_ops = {
>> +    .bind    = inno_hdmi_bind,
>> +    .unbind    = inno_hdmi_unbind,
>> +};
>> +
>> +static int inno_hdmi_probe(struct platform_device *pdev)
>> +{
>> +    return component_add(&pdev->dev, &inno_hdmi_ops);
>> +}
>> +
>> +static int inno_hdmi_remove(struct platform_device *pdev)
>> +{
>> +    component_del(&pdev->dev, &inno_hdmi_ops);
>> +
>> +    return 0;
>> +}
>> +
>> +static const struct of_device_id inno_hdmi_dt_ids[] = {
>> +    { .compatible = "rockchip,rk3036-inno-hdmi",
>> +    },
>> +    {},
>> +};
>> +MODULE_DEVICE_TABLE(of, inno_hdmi_dt_ids);
>> +
>> +static struct platform_driver inno_hdmi_driver = {
>> +    .probe  = inno_hdmi_probe,
>> +    .remove = inno_hdmi_remove,
>> +    .driver = {
>> +        .name = "innohdmi-rockchip",
>> +        .of_match_table = inno_hdmi_dt_ids,
>> +    },
>> +};
>> +
>> +module_platform_driver(inno_hdmi_driver);
>> +
>> +MODULE_AUTHOR("Zheng Yang <zhengyang at rock-chips.com>");
>> +MODULE_AUTHOR("Yakir Yang <ykk at rock-chips.com>");
>> +MODULE_DESCRIPTION("Rockchip Specific INNO-HDMI Driver");
>> +MODULE_LICENSE("GPL");
>> +MODULE_ALIAS("platform:innohdmi-rockchip");
>> diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.h 
>> b/drivers/gpu/drm/rockchip/inno_hdmi.h
>> new file mode 100644
>> index 0000000..4ff17ad
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/inno_hdmi.h
>> @@ -0,0 +1,364 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + *    Zheng Yang <zhengyang at rock-chips.com>
>> + *    Yakir Yang <ykk at rock-chips.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#ifndef __INNO_HDMI_H__
>> +#define __INNO_HDMI_H__
>> +
>> +#define DDC_SEGMENT_ADDR        0x30
>> +
>> +enum PWR_MODE {
>> +    NORMAL,
>> +    LOWER_PWR,
>> +};
>> +
>> +#define HDMI_SCL_RATE            (100*1000)
>> +#define DDC_BUS_FREQ_L            0x4b
>> +#define DDC_BUS_FREQ_H            0x4c
>> +
>> +#define HDMI_SYS_CTRL            0x00
>> +#define m_RST_ANALOG            (1 << 6)
>> +#define v_RST_ANALOG            (0 << 6)
>> +#define v_NOT_RST_ANALOG        (1 << 6)
>> +#define m_RST_DIGITAL            (1 << 5)
>> +#define v_RST_DIGITAL            (0 << 5)
>> +#define v_NOT_RST_DIGITAL        (1 << 5)
>> +#define m_REG_CLK_INV            (1 << 4)
>> +#define v_REG_CLK_NOT_INV        (0 << 4)
>> +#define v_REG_CLK_INV            (1 << 4)
>> +#define m_VCLK_INV            (1 << 3)
>> +#define v_VCLK_NOT_INV            (0 << 3)
>> +#define v_VCLK_INV            (1 << 3)
>> +#define m_REG_CLK_SOURCE        (1 << 2)
>> +#define v_REG_CLK_SOURCE_TMDS        (0 << 2)
>> +#define v_REG_CLK_SOURCE_SYS        (1 << 2)
>> +#define m_POWER                (1 << 1)
>> +#define v_PWR_ON            (0 << 1)
>> +#define v_PWR_OFF            (1 << 1)
>> +#define m_INT_POL            (1 << 0)
>> +#define v_INT_POL_HIGH            1
>> +#define v_INT_POL_LOW            0
>> +
>> +#define HDMI_VIDEO_CONTRL1        0x01
>> +#define m_VIDEO_INPUT_FORMAT        (7 << 1)
>> +#define m_DE_SOURCE            (1 << 0)
>> +#define v_VIDEO_INPUT_FORMAT(n)        (n << 1)
>> +#define v_DE_EXTERNAL            1
>> +#define v_DE_INTERNAL            0
>> +enum {
>> +    VIDEO_INPUT_SDR_RGB444 = 0,
>> +    VIDEO_INPUT_DDR_RGB444 = 5,
>> +    VIDEO_INPUT_DDR_YCBCR422 = 6
>> +};
>> +
>> +#define HDMI_VIDEO_CONTRL2        0x02
>> +#define m_VIDEO_OUTPUT_COLOR        (3 << 6)
>> +#define m_VIDEO_INPUT_BITS        (3 << 4)
>> +#define m_VIDEO_INPUT_CSP        (1 << 0)
>> +#define v_VIDEO_OUTPUT_COLOR(n)        (((n) & 0x3) << 6)
>> +#define v_VIDEO_INPUT_BITS(n)        (n << 4)
>> +#define v_VIDEO_INPUT_CSP(n)        (n << 0)
>> +enum {
>> +    VIDEO_INPUT_12BITS = 0,
>> +    VIDEO_INPUT_10BITS = 1,
>> +    VIDEO_INPUT_REVERT = 2,
>> +    VIDEO_INPUT_8BITS = 3,
>> +};
>> +
>> +#define HDMI_VIDEO_CONTRL        0x03
>> +#define m_VIDEO_AUTO_CSC        (1 << 7)
>> +#define v_VIDEO_AUTO_CSC(n)        (n << 7)
>> +#define m_VIDEO_C0_C2_SWAP        (1 << 0)
>> +#define v_VIDEO_C0_C2_SWAP(n)        (n << 0)
>> +enum {
>> +    C0_C2_CHANGE_ENABLE = 0,
>> +    C0_C2_CHANGE_DISABLE = 1,
>> +    AUTO_CSC_DISABLE = 0,
>> +    AUTO_CSC_ENABLE = 1,
>> +};
>> +
>> +#define HDMI_VIDEO_CONTRL3        0x04
>> +#define m_COLOR_DEPTH_NOT_INDICATED    (1 << 4)
>> +#define m_SOF                (1 << 3)
>> +#define m_COLOR_RANGE            (1 << 2)
>> +#define m_CSC                (1 << 0)
>> +#define v_COLOR_DEPTH_NOT_INDICATED(n)    ((n) << 4)
>> +#define v_SOF_ENABLE            (0 << 3)
>> +#define v_SOF_DISABLE            (1 << 3)
>> +#define v_COLOR_RANGE_FULL        (1 << 2)
>> +#define v_COLOR_RANGE_LIMITED        (0 << 2)
>> +#define v_CSC_ENABLE            1
>> +#define v_CSC_DISABLE            0
>> +
>> +#define HDMI_AV_MUTE            0x05
>> +#define m_AVMUTE_CLEAR            (1 << 7)
>> +#define m_AVMUTE_ENABLE            (1 << 6)
>> +#define m_AUDIO_MUTE            (1 << 1)
>> +#define m_VIDEO_BLACK            (1 << 0)
>> +#define v_AVMUTE_CLEAR(n)        (n << 7)
>> +#define v_AVMUTE_ENABLE(n)        (n << 6)
>> +#define v_AUDIO_MUTE(n)            (n << 1)
>> +#define v_VIDEO_MUTE(n)            (n << 0)
>> +
>> +#define HDMI_VIDEO_TIMING_CTL        0x08
>> +#define v_HSYNC_POLARITY(n)        (n << 3)
>> +#define v_VSYNC_POLARITY(n)        (n << 2)
>> +#define v_INETLACE(n)            (n << 1)
>> +#define v_EXTERANL_VIDEO(n)        (n << 0)
>> +
>> +#define HDMI_VIDEO_EXT_HTOTAL_L        0x09
>> +#define HDMI_VIDEO_EXT_HTOTAL_H        0x0a
>> +#define HDMI_VIDEO_EXT_HBLANK_L        0x0b
>> +#define HDMI_VIDEO_EXT_HBLANK_H        0x0c
>> +#define HDMI_VIDEO_EXT_HDELAY_L        0x0d
>> +#define HDMI_VIDEO_EXT_HDELAY_H        0x0e
>> +#define HDMI_VIDEO_EXT_HDURATION_L    0x0f
>> +#define HDMI_VIDEO_EXT_HDURATION_H    0x10
>> +#define HDMI_VIDEO_EXT_VTOTAL_L        0x11
>> +#define HDMI_VIDEO_EXT_VTOTAL_H        0x12
>> +#define HDMI_VIDEO_EXT_VBLANK        0x13
>> +#define HDMI_VIDEO_EXT_VDELAY        0x14
>> +#define HDMI_VIDEO_EXT_VDURATION    0x15
>> +
>> +#define HDMI_VIDEO_CSC_COEF        0x18
>> +
>> +#define HDMI_AUDIO_CTRL1        0x35
>> +enum {
>> +    CTS_SOURCE_INTERNAL = 0,
>> +    CTS_SOURCE_EXTERNAL = 1,
>> +};
>> +#define v_CTS_SOURCE(n)            (n << 7)
>> +
>> +enum {
>> +    DOWNSAMPLE_DISABLE = 0,
>> +    DOWNSAMPLE_1_2 = 1,
>> +    DOWNSAMPLE_1_4 = 2,
>> +};
>> +#define v_DOWN_SAMPLE(n)        (n << 5)
>> +
>> +enum {
>> +    AUDIO_SOURCE_IIS = 0,
>> +    AUDIO_SOURCE_SPDIF = 1,
>> +};
>> +#define v_AUDIO_SOURCE(n)        (n << 3)
>> +
>> +#define v_MCLK_ENABLE(n)        (n << 2)
>> +enum {
>> +    MCLK_128FS = 0,
>> +    MCLK_256FS = 1,
>> +    MCLK_384FS = 2,
>> +    MCLK_512FS = 3,
>> +};
>> +#define v_MCLK_RATIO(n)            (n)
>> +
>> +#define AUDIO_SAMPLE_RATE        0x37
>> +enum {
>> +    AUDIO_32K = 0x3,
>> +    AUDIO_441K = 0x0,
>> +    AUDIO_48K = 0x2,
>> +    AUDIO_882K = 0x8,
>> +    AUDIO_96K = 0xa,
>> +    AUDIO_1764K = 0xc,
>> +    AUDIO_192K = 0xe,
>> +};
>> +
>> +#define AUDIO_I2S_MODE            0x38
>> +enum {
>> +    I2S_CHANNEL_1_2 = 1,
>> +    I2S_CHANNEL_3_4 = 3,
>> +    I2S_CHANNEL_5_6 = 7,
>> +    I2S_CHANNEL_7_8 = 0xf
>> +};
>> +#define v_I2S_CHANNEL(n)        ((n) << 2)
>> +enum {
>> +    I2S_STANDARD = 0,
>> +    I2S_LEFT_JUSTIFIED = 1,
>> +    I2S_RIGHT_JUSTIFIED = 2,
>> +};
>> +#define v_I2S_MODE(n)            (n)
>> +
>> +#define AUDIO_I2S_MAP            0x39
>> +#define AUDIO_I2S_SWAPS_SPDIF        0x3a
>> +#define v_SPIDF_FREQ(n)            (n)
>> +
>> +#define N_32K                0x1000
>> +#define N_441K                0x1880
>> +#define N_882K                0x3100
>> +#define N_1764K                0x6200
>> +#define N_48K                0x1800
>> +#define N_96K                0x3000
>> +#define N_192K                0x6000
>> +
>> +#define HDMI_AUDIO_CHANNEL_STATUS    0x3e
>> +#define m_AUDIO_STATUS_NLPCM        (1 << 7)
>> +#define m_AUDIO_STATUS_USE        (1 << 6)
>> +#define m_AUDIO_STATUS_COPYRIGHT    (1 << 5)
>> +#define m_AUDIO_STATUS_ADDITION        (3 << 2)
>> +#define m_AUDIO_STATUS_CLK_ACCURACY    (2 << 0)
>> +#define v_AUDIO_STATUS_NLPCM(n)        ((n & 1) << 7)
>> +#define AUDIO_N_H            0x3f
>> +#define AUDIO_N_M            0x40
>> +#define AUDIO_N_L            0x41
>> +
>> +#define HDMI_AUDIO_CTS_H        0x45
>> +#define HDMI_AUDIO_CTS_M        0x46
>> +#define HDMI_AUDIO_CTS_L        0x47
>> +
>> +#define HDMI_DDC_CLK_L            0x4b
>> +#define HDMI_DDC_CLK_H            0x4c
>> +
>> +#define HDMI_EDID_SEGMENT_POINTER    0x4d
>> +#define HDMI_EDID_WORD_ADDR        0x4e
>> +#define HDMI_EDID_FIFO_OFFSET        0x4f
>> +#define HDMI_EDID_FIFO_ADDR        0x50
>> +
>> +#define HDMI_PACKET_SEND_MANUAL        0x9c
>> +#define HDMI_PACKET_SEND_AUTO        0x9d
>> +#define m_PACKET_GCP_EN            (1 << 7)
>> +#define m_PACKET_MSI_EN            (1 << 6)
>> +#define m_PACKET_SDI_EN            (1 << 5)
>> +#define m_PACKET_VSI_EN            (1 << 4)
>> +#define v_PACKET_GCP_EN(n)        ((n & 1) << 7)
>> +#define v_PACKET_MSI_EN(n)        ((n & 1) << 6)
>> +#define v_PACKET_SDI_EN(n)        ((n & 1) << 5)
>> +#define v_PACKET_VSI_EN(n)        ((n & 1) << 4)
>> +
>> +#define HDMI_CONTROL_PACKET_BUF_INDEX    0x9f
>> +enum {
>> +    INFOFRAME_VSI = 0x05,
>> +    INFOFRAME_AVI = 0x06,
>> +    INFOFRAME_AAI = 0x08,
>> +};
>> +
>> +#define HDMI_CONTROL_PACKET_ADDR    0xa0
>> +#define HDMI_SIZE_VSI_INFOFRAME        0x0A
>> +#define HDMI_SIZE_AVI_INFOFRAME        0x11
>> +#define HDMI_SIZE_AUDIO_INFOFRAME    0x0F
>> +enum {
>> +    AVI_COLOR_MODE_RGB = 0,
>> +    AVI_COLOR_MODE_YCBCR422 = 1,
>> +    AVI_COLOR_MODE_YCBCR444 = 2,
>> +    AVI_COLORIMETRY_NO_DATA = 0,
>> +
>> +    AVI_COLORIMETRY_SMPTE_170M = 1,
>> +    AVI_COLORIMETRY_ITU709 = 2,
>> +    AVI_COLORIMETRY_EXTENDED = 3,
>> +
>> +    AVI_CODED_FRAME_ASPECT_NO_DATA = 0,
>> +    AVI_CODED_FRAME_ASPECT_4_3 = 1,
>> +    AVI_CODED_FRAME_ASPECT_16_9 = 2,
>> +
>> +    ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME = 0x08,
>> +    ACTIVE_ASPECT_RATE_4_3 = 0x09,
>> +    ACTIVE_ASPECT_RATE_16_9 = 0x0A,
>> +    ACTIVE_ASPECT_RATE_14_9 = 0x0B,
>> +};
>> +
>> +#define HDMI_HDCP_CTRL            0x52
>> +#define m_HDMI_DVI            (1 << 1)
>> +#define v_HDMI_DVI(n)            (n << 1)
>> +
>> +#define HDMI_INTERRUPT_MASK1        0xc0
>> +#define HDMI_INTERRUPT_STATUS1        0xc1
>> +#define    m_INT_ACTIVE_VSYNC        (1 << 5)
>> +#define m_INT_EDID_READY        (1 << 2)
>> +
>> +#define HDMI_INTERRUPT_MASK2        0xc2
>> +#define HDMI_INTERRUPT_STATUS2        0xc3
>> +#define m_INT_HDCP_ERR            (1 << 7)
>> +#define m_INT_BKSV_FLAG            (1 << 6)
>> +#define m_INT_HDCP_OK            (1 << 4)
>> +
>> +#define HDMI_STATUS            0xc8
>> +#define m_HOTPLUG            (1 << 7)
>> +#define m_MASK_INT_HOTPLUG        (1 << 5)
>> +#define m_INT_HOTPLUG            (1 << 1)
>> +#define v_MASK_INT_HOTPLUG(n)        ((n & 0x1) << 5)
>> +
>> +#define HDMI_COLORBAR                   0xc9
>> +
>> +#define HDMI_PHY_SYNC            0xce
>> +#define HDMI_PHY_SYS_CTL        0xe0
>> +#define m_TMDS_CLK_SOURCE        (1 << 5)
>> +#define v_TMDS_FROM_PLL            (0 << 5)
>> +#define v_TMDS_FROM_GEN            (1 << 5)
>> +#define m_PHASE_CLK            (1 << 4)
>> +#define v_DEFAULT_PHASE            (0 << 4)
>> +#define v_SYNC_PHASE            (1 << 4)
>> +#define m_TMDS_CURRENT_PWR        (1 << 3)
>> +#define v_TURN_ON_CURRENT        (0 << 3)
>> +#define v_CAT_OFF_CURRENT        (1 << 3)
>> +#define m_BANDGAP_PWR            (1 << 2)
>> +#define v_BANDGAP_PWR_UP        (0 << 2)
>> +#define v_BANDGAP_PWR_DOWN        (1 << 2)
>> +#define m_PLL_PWR            (1 << 1)
>> +#define v_PLL_PWR_UP            (0 << 1)
>> +#define v_PLL_PWR_DOWN            (1 << 1)
>> +#define m_TMDS_CHG_PWR            (1 << 0)
>> +#define v_TMDS_CHG_PWR_UP        (0 << 0)
>> +#define v_TMDS_CHG_PWR_DOWN        (1 << 0)
>> +
>> +#define HDMI_PHY_CHG_PWR        0xe1
>> +#define v_CLK_CHG_PWR(n)        ((n & 1) << 3)
>> +#define v_DATA_CHG_PWR(n)        ((n & 7) << 0)
>> +
>> +#define HDMI_PHY_DRIVER            0xe2
>> +#define v_CLK_MAIN_DRIVER(n)        (n << 4)
>> +#define v_DATA_MAIN_DRIVER(n)        (n << 0)
>> +
>> +#define HDMI_PHY_PRE_EMPHASIS        0xe3
>> +#define v_PRE_EMPHASIS(n)        ((n & 7) << 4)
>> +#define v_CLK_PRE_DRIVER(n)        ((n & 3) << 2)
>> +#define v_DATA_PRE_DRIVER(n)        ((n & 3) << 0)
>> +
>> +#define HDMI_PHY_FEEDBACK_DIV_RATIO_LOW        0xe7
>> +#define v_FEEDBACK_DIV_LOW(n)            (n & 0xff)
>> +#define HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH    0xe8
>> +#define v_FEEDBACK_DIV_HIGH(n)            (n & 1)
>> +
>> +#define HDMI_PHY_PRE_DIV_RATIO        0xed
>> +#define v_PRE_DIV_RATIO(n)        (n & 0x1f)
>> +
>> +#define HDMI_CEC_CTRL            0xd0
>> +#define m_ADJUST_FOR_HISENSE        (1 << 6)
>> +#define m_REJECT_RX_BROADCAST        (1 << 5)
>> +#define m_BUSFREETIME_ENABLE        (1 << 2)
>> +#define m_REJECT_RX            (1 << 1)
>> +#define m_START_TX            (1 << 0)
>> +
>> +#define HDMI_CEC_DATA            0xd1
>> +#define HDMI_CEC_TX_OFFSET        0xd2
>> +#define HDMI_CEC_RX_OFFSET        0xd3
>> +#define HDMI_CEC_CLK_H            0xd4
>> +#define HDMI_CEC_CLK_L            0xd5
>> +#define HDMI_CEC_TX_LENGTH        0xd6
>> +#define HDMI_CEC_RX_LENGTH        0xd7
>> +#define HDMI_CEC_TX_INT_MASK        0xd8
>> +#define m_TX_DONE            (1 << 3)
>> +#define m_TX_NOACK            (1 << 2)
>> +#define m_TX_BROADCAST_REJ        (1 << 1)
>> +#define m_TX_BUSNOTFREE            (1 << 0)
>> +
>> +#define HDMI_CEC_RX_INT_MASK        0xd9
>> +#define m_RX_LA_ERR            (1 << 4)
>> +#define m_RX_GLITCH            (1 << 3)
>> +#define m_RX_DONE            (1 << 0)
>> +
>> +#define HDMI_CEC_TX_INT            0xda
>> +#define HDMI_CEC_RX_INT            0xdb
>> +#define HDMI_CEC_BUSFREETIME_L        0xdc
>> +#define HDMI_CEC_BUSFREETIME_H        0xdd
>> +#define HDMI_CEC_LOGICADDR        0xde
>> +
>> +#endif /* __INNO_HDMI_H__ */
>
>




From ykk at rock-chips.com  Mon Jan 18 02:44:16 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Mon, 18 Jan 2016 18:44:16 +0800
Subject: [PATCH] clk: rockchip: rk3036: enable the CLK_IGNORE_UNUSED flag
 for hclk_vio_bus
In-Reply-To: <1816378.OnNVlMiORS@phil>
References: <1452858854-841-1-git-send-email-ykk@rock-chips.com>
 <1816378.OnNVlMiORS@phil>
Message-ID: <569CC200.1050504@rock-chips.com>



On 01/18/2016 03:14 AM, Heiko Stuebner wrote:
> Am Freitag, 15. Januar 2016, 19:54:14 schrieb Yakir Yang:
>> HCLK_VIO_BUS is the noc bus controller clock for display module,
>> due to it shouldn't belong to any driver, but we need it enabled,
>> so just mark it as the CLK_IGNORE_UNUSED flag.
>>
>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
> applied to my clk-branch for 4.6

Thanks  ;)

- Yakir
> Thanks
> Heiko
>
>
>




From ykk at rock-chips.com  Mon Jan 18 02:48:38 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Mon, 18 Jan 2016 18:48:38 +0800
Subject: [PATCH v12 0/18] Add Analogix Core Display Port Driver
In-Reply-To: <1670568.VBNHbJuGkf@phil>
References: <1450873538-18304-1-git-send-email-ykk@rock-chips.com>
 <2322926.s1viZ6tSS3@phil> <1670568.VBNHbJuGkf@phil>
Message-ID: <569CC306.8030207@rock-chips.com>

Hi Heiko,

Sorry for the delay reply ;)

On 01/17/2016 10:25 PM, Heiko Stuebner wrote:
> Hi Yakir,
>
> Am Mittwoch, 13. Januar 2016, 15:59:51 schrieb Heiko Stuebner:
>> Am Mittwoch, 23. Dezember 2015, 20:25:38 schrieb Yakir Yang:
>>>     The Samsung Exynos eDP controller and Rockchip RK3288 eDP controller
>>>
>>> share the same IP, so a lot of parts can be re-used. I split the common
>>> code into bridge directory, then rk3288 and exynos only need to keep
>>> some platform code. Cause I can't find the exact IP name of exynos dp
>>> controller, so I decide to name dp core driver with "analogix" which I
>>> find in rk3288 eDP TRM
>> could you rebase your patches on top of Dave's drm-next branch [0] please?
>> The exynos part got some const attributes for the *_func_ops structs and
>> drm_encoder_init got an additional parameter.
Sure
>> I'm still hoping that we can get this finally committed once 4.5-rc1 is
>> released in 1.5 weeks :-) .
>>
>> I did try to merge things together [1], the system at least comes up and
>> detects the panel (EDID is read correctly and it turns the backlight on)
>> but I don't see any output on the display - hdmi works fine though and
>> X11 is claiming everything to be fine in both cases.
Hmmm.... I would double check in my side.
>> So I don't know yet if I made a mistake when putting this together or
>> there is an issue on the driver-side.
> I was able to debug this a bit more, and it seems the encoder_helper_funcs
> don't like the prepare/commit callbacks and like enable better.
Ah..... Yep, Atomic helper seems wouldn't call the prepaer/commit, 
instead we can use the enbale/disable helper functions.
> So when I do something similar to Marks patch a8eef71d38da ("drm/rockchip:
> dw_hdmi: use encoder enable function"), like in the attached patch (to be
> folded into the original addition of those functions), I get display output
> again.

Thanks for your patient debug, I would send the new version out soon  ;)

- Yakir
>
> Heiko
>
>> [0] http://cgit.freedesktop.org/~airlied/linux/log/?h=drm-next
>> [1] https://github.com/mmind/linux-rockchip/tree/tmp/analogixdp_v12-veyron
>> I left out patch16, per your talk with Jingoo and me not seeing any AUX
>> CH errors without it.




From ykk at rock-chips.com  Mon Jan 18 06:42:03 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Mon, 18 Jan 2016 22:42:03 +0800
Subject: [PATCH v4 0/1] Introduce Innosilicon HDMI driver on Rockchip platforms
Message-ID: <1453128123-29155-1-git-send-email-ykk@rock-chips.com>


Here are a brief introduction to Innosilicon HDMI IP:
 - Support HDMI 1.4a, HDCP 1.2 and DVI 1.0 standard compliant transmitter
 - Support HDMI1.4 a/b 3D function defined in HDMI 1.4 a/b spec
 - Digital video interface supports a pixel size of 24, 30, 36, 48bits color
   depth in RGB
 - S/PDIF output supports PCM, Dolby Digital, DTS digital audio transmission
   (32-192kHz Fs) using IEC60958 and IEC 61937
 - The EDID and CEC function are also supported by Innosilicon HDMI Transmitter
   Controlle


Changes in v4:
- Modify the commit title "drm/rockchip: hdmi: ..." (Mark)
- Correct the "DKMS" to "DPMS" (Mark)
- Fix over 80 characters problems (Mark)
- Remove encoder .prepare/.commit helper functions, and move the vop mode
  configure function into encoder .enable helper functions. (Mark)

Changes in v3:
- Use encoder enable/disable function, and remove the encoder DPMS function
- Keep HDMI PLL power on in standby mode

Changes in v2:
- Using DRM atomic helper functions for connector init (Mark)
- Remove "hdmi->connector.encoder = encoder;" (Mark)

Yakir Yang (1):
  drm/rockchip: hdmi: add Innosilicon HDMI support

 drivers/gpu/drm/rockchip/Kconfig     |   8 +
 drivers/gpu/drm/rockchip/Makefile    |   1 +
 drivers/gpu/drm/rockchip/inno_hdmi.c | 992 +++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/inno_hdmi.h | 364 +++++++++++++
 4 files changed, 1365 insertions(+)
 create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.c
 create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.h

-- 
1.9.1




From ykk at rock-chips.com  Mon Jan 18 06:42:20 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Mon, 18 Jan 2016 22:42:20 +0800
Subject: [PATCH v4] drm/rockchip: hdmi: add Innosilicon HDMI support
In-Reply-To: <1453128123-29155-1-git-send-email-ykk@rock-chips.com>
References: <1453128123-29155-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1453128140-29208-1-git-send-email-ykk@rock-chips.com>

The Innosilicon HDMI is a low power HDMI 1.4 transmitter
IP, and it have been integrated on some rockchip CPUs
(like RK3036, RK312x).

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v4:
- Modify the commit title "drm/rockchip: hdmi: ..." (Mark)
- Correct the "DKMS" to "DPMS" (Mark)
- Fix over 80 characters problems (Mark)
- Remove encoder .prepare/.commit helper functions, and move the vop mode
  configure function into encoder .enable helper functions. (Mark)

Changes in v3:
- Use encoder enable/disable function, and remove the encoder DPMS function
- Keep HDMI PLL power on in standby mode

Changes in v2:
- Using DRM atomic helper functions for connector init (Mark)
- Remove "hdmi->connector.encoder = encoder;" (Mark)

 drivers/gpu/drm/rockchip/Kconfig     |   8 +
 drivers/gpu/drm/rockchip/Makefile    |   1 +
 drivers/gpu/drm/rockchip/inno_hdmi.c | 992 +++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/inno_hdmi.h | 364 +++++++++++++
 4 files changed, 1365 insertions(+)
 create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.c
 create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.h

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 35215f6..a5014e0 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -25,3 +25,11 @@ config ROCKCHIP_DW_HDMI
 	  for the Synopsys DesignWare HDMI driver. If you want to
 	  enable HDMI on RK3288 based SoC, you should selet this
 	  option.
+
+config ROCKCHIP_INNO_HDMI
+	tristate "Rockchip specific extensions for Innosilicon HDMI"
+        depends on DRM_ROCKCHIP
+        help
+	  This selects support for Rockchip SoC specific extensions
+	  for the Innosilicon HDMI driver. If you want to enable
+	  HDMI on RK3036 based SoC, you should selet this option.
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 9e6e992..96b9db6 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -6,5 +6,6 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
 		rockchip_drm_gem.o rockchip_drm_vop.o
 
 obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
+obj-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
 
 obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_vop_reg.o
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
new file mode 100644
index 0000000..fc212b8
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -0,0 +1,992 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ *    Zheng Yang <zhengyang at rock-chips.com>
+ *    Yakir Yang <ykk at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/hdmi.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+
+#include <drm/drm_of.h>
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_encoder_slave.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_vop.h"
+
+#include "inno_hdmi.h"
+
+#define to_inno_hdmi(x)	container_of(x, struct inno_hdmi, x)
+
+struct hdmi_data_info {
+	int vic;
+	bool sink_is_hdmi;
+	bool sink_has_audio;
+	unsigned int enc_in_format;
+	unsigned int enc_out_format;
+	unsigned int colorimetry;
+};
+
+struct inno_hdmi_i2c {
+	struct i2c_adapter adap;
+
+	u8 ddc_addr;
+	u8 segment_addr;
+
+	struct mutex lock;
+	struct completion cmp;
+};
+
+struct inno_hdmi {
+	struct device *dev;
+	struct drm_device *drm_dev;
+
+	int irq;
+	struct clk *pclk;
+	void __iomem *regs;
+
+	struct drm_connector	connector;
+	struct drm_encoder	encoder;
+
+	struct inno_hdmi_i2c *i2c;
+	struct i2c_adapter *ddc;
+
+	unsigned int tmds_rate;
+
+	struct hdmi_data_info	hdmi_data;
+	struct drm_display_mode previous_mode;
+};
+
+enum {
+	CSC_ITU601_16_235_TO_RGB_0_255_8BIT,
+	CSC_ITU601_0_255_TO_RGB_0_255_8BIT,
+	CSC_ITU709_16_235_TO_RGB_0_255_8BIT,
+	CSC_RGB_0_255_TO_ITU601_16_235_8BIT,
+	CSC_RGB_0_255_TO_ITU709_16_235_8BIT,
+	CSC_RGB_0_255_TO_RGB_16_235_8BIT,
+};
+
+static const char coeff_csc[][24] = {
+	/*
+	 * YUV2RGB:601 SD mode(Y[16:235], UV[16:240], RGB[0:255]):
+	 *   R = 1.164*Y + 1.596*V - 204
+	 *   G = 1.164*Y - 0.391*U - 0.813*V + 154
+	 *   B = 1.164*Y + 2.018*U - 258
+	 */
+	{
+		0x04, 0xa7, 0x00, 0x00, 0x06, 0x62, 0x02, 0xcc,
+		0x04, 0xa7, 0x11, 0x90, 0x13, 0x40, 0x00, 0x9a,
+		0x04, 0xa7, 0x08, 0x12, 0x00, 0x00, 0x03, 0x02
+	},
+	/*
+	 * YUV2RGB:601 SD mode(YUV[0:255],RGB[0:255]):
+	 *   R = Y + 1.402*V - 248
+	 *   G = Y - 0.344*U - 0.714*V + 135
+	 *   B = Y + 1.772*U - 227
+	 */
+	{
+		0x04, 0x00, 0x00, 0x00, 0x05, 0x9b, 0x02, 0xf8,
+		0x04, 0x00, 0x11, 0x60, 0x12, 0xdb, 0x00, 0x87,
+		0x04, 0x00, 0x07, 0x16, 0x00, 0x00, 0x02, 0xe3
+	},
+	/*
+	 * YUV2RGB:709 HD mode(Y[16:235],UV[16:240],RGB[0:255]):
+	 *   R = 1.164*Y + 1.793*V - 248
+	 *   G = 1.164*Y - 0.213*U - 0.534*V + 77
+	 *   B = 1.164*Y + 2.115*U - 289
+	 */
+	{
+		0x04, 0xa7, 0x00, 0x00, 0x07, 0x2c, 0x02, 0xf8,
+		0x04, 0xa7, 0x10, 0xda, 0x12, 0x22, 0x00, 0x4d,
+		0x04, 0xa7, 0x08, 0x74, 0x00, 0x00, 0x03, 0x21
+	},
+
+	/*
+	 * RGB2YUV:601 SD mode:
+	 *   Cb = -0.291G - 0.148R + 0.439B + 128
+	 *   Y  = 0.504G  + 0.257R + 0.098B + 16
+	 *   Cr = -0.368G + 0.439R - 0.071B + 128
+	 */
+	{
+		0x11, 0x5f, 0x01, 0x82, 0x10, 0x23, 0x00, 0x80,
+		0x02, 0x1c, 0x00, 0xa1, 0x00, 0x36, 0x00, 0x1e,
+		0x11, 0x29, 0x10, 0x59, 0x01, 0x82, 0x00, 0x80
+	},
+	/*
+	 * RGB2YUV:709 HD mode:
+	 *   Cb = - 0.338G - 0.101R + 0.439B + 128
+	 *   Y  = 0.614G   + 0.183R + 0.062B + 16
+	 *   Cr = - 0.399G + 0.439R - 0.040B + 128
+	 */
+	{
+		0x11, 0x98, 0x01, 0xc1, 0x10, 0x28, 0x00, 0x80,
+		0x02, 0x74, 0x00, 0xbb, 0x00, 0x3f, 0x00, 0x10,
+		0x11, 0x5a, 0x10, 0x67, 0x01, 0xc1, 0x00, 0x80
+	},
+	/*
+	 * RGB[0:255]2RGB[16:235]:
+	 *   R' = R x (235-16)/255 + 16;
+	 *   G' = G x (235-16)/255 + 16;
+	 *   B' = B x (235-16)/255 + 16;
+	 */
+	{
+		0x00, 0x00, 0x03, 0x6F, 0x00, 0x00, 0x00, 0x10,
+		0x03, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+		0x00, 0x00, 0x00, 0x00, 0x03, 0x6F, 0x00, 0x10
+	},
+};
+
+static inline u8 hdmi_readb(struct inno_hdmi *hdmi, u16 offset)
+{
+	return readl_relaxed(hdmi->regs + (offset) * 0x04);
+}
+
+static inline void hdmi_writeb(struct inno_hdmi *hdmi, u16 offset, u32 val)
+{
+	writel_relaxed(val, hdmi->regs + (offset) * 0x04);
+}
+
+static inline void hdmi_modb(struct inno_hdmi *hdmi, u16 offset,
+			     u32 msk, u32 val)
+{
+	u8 temp = hdmi_readb(hdmi, offset) & ~msk;
+
+	temp |= val & msk;
+	hdmi_writeb(hdmi, offset, temp);
+}
+
+static void inno_hdmi_i2c_init(struct inno_hdmi *hdmi)
+{
+	int ddc_bus_freq;
+
+	ddc_bus_freq = (hdmi->tmds_rate >> 2) / HDMI_SCL_RATE;
+
+	hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
+	hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
+
+	/* Clear the EDID interrupt flag and mute the interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+}
+
+static void inno_hdmi_sys_power(struct inno_hdmi *hdmi, bool enable)
+{
+	if (enable)
+		hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON);
+	else
+		hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
+}
+
+static void inno_hdmi_set_pwr_mode(struct inno_hdmi *hdmi, int mode)
+{
+	switch (mode) {
+	case NORMAL:
+		inno_hdmi_sys_power(hdmi, false);
+
+		hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x6f);
+		hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0xbb);
+
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x14);
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x10);
+		hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x0f);
+		hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x01);
+
+		inno_hdmi_sys_power(hdmi, true);
+		break;
+
+	case LOWER_PWR:
+		inno_hdmi_sys_power(hdmi, false);
+		hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
+
+		break;
+
+	default:
+		dev_err(hdmi->dev, "Unknown power mode %d\n", mode);
+	}
+}
+
+static void inno_hdmi_reset(struct inno_hdmi *hdmi)
+{
+	u32 val;
+	u32 msk;
+
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_DIGITAL, v_NOT_RST_DIGITAL);
+	udelay(100);
+
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_ANALOG, v_NOT_RST_ANALOG);
+	udelay(100);
+
+	msk = m_REG_CLK_INV | m_REG_CLK_SOURCE | m_POWER | m_INT_POL;
+	val = v_REG_CLK_INV | v_REG_CLK_SOURCE_SYS | v_PWR_ON | v_INT_POL_HIGH;
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, msk, val);
+
+	inno_hdmi_set_pwr_mode(hdmi, NORMAL);
+}
+
+static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi)
+{
+	char info[HDMI_SIZE_AVI_INFOFRAME] = {0};
+	int avi_color_mode;
+	int i;
+
+	hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_AVI);
+
+	info[0] = 0x82;
+	info[1] = 0x02;
+	info[2] = 0x0D;
+	info[3] = info[0] + info[1] + info[2];
+
+	if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_RGB)
+		avi_color_mode = AVI_COLOR_MODE_RGB;
+	else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV444)
+		avi_color_mode = AVI_COLOR_MODE_YCBCR444;
+	else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV422)
+		avi_color_mode = AVI_COLOR_MODE_YCBCR422;
+	else
+		avi_color_mode = AVI_COLOR_MODE_RGB;
+
+	info[4] = (avi_color_mode << 5);
+	info[5] = (AVI_COLORIMETRY_NO_DATA << 6) |
+		  (AVI_CODED_FRAME_ASPECT_NO_DATA << 4) |
+		  ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME;
+
+	info[6] = 0;
+	info[7] = hdmi->hdmi_data.vic;
+
+	if (hdmi->hdmi_data.vic == 6 || hdmi->hdmi_data.vic == 7 ||
+	    hdmi->hdmi_data.vic == 21 || hdmi->hdmi_data.vic == 22)
+		info[8] = 1;
+	else
+		info[8] = 0;
+
+	/* Calculate avi info frame checKsum */
+	for (i = 4; i < HDMI_SIZE_AVI_INFOFRAME; i++)
+		info[3] += info[i];
+	info[3] = 0x100 - info[3];
+
+	for (i = 0; i < HDMI_SIZE_AVI_INFOFRAME; i++)
+		hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i, info[i]);
+
+	return 0;
+}
+
+static int inno_hdmi_config_video_vsi(struct inno_hdmi *hdmi)
+{
+	char info[HDMI_SIZE_VSI_INFOFRAME] = {0};
+	int i;
+
+	hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, m_PACKET_VSI_EN,
+		  v_PACKET_VSI_EN(0));
+
+	hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_VSI);
+
+	/* Header Bytes */
+	info[0] = 0x81;
+	info[1] = 0x01;
+
+	/* PB1 - PB3 contain the 24bit IEEE Registration Identifier */
+	info[4] = 0x03;
+	info[5] = 0x0c;
+	info[6] = 0x00;
+
+	/* PB4 - HDMI_Video_Format into bits 7:5 */
+	info[7] = 0;
+
+	/*
+	 * PB5 - Depending on the video format, this byte will contain
+	 * either the HDMI_VIC code in buts 7:0, OR the 3D_Structure in
+	 * bits 7:4.
+	 */
+	info[2] = 0x06 - 2;
+	info[8] = 0;
+	info[9] = 0;
+
+	info[3] = info[0] + info[1] + info[2];
+
+	/* Calculate info frame checKsum */
+	for (i = 4; i < HDMI_SIZE_VSI_INFOFRAME; i++)
+		info[3] += info[i];
+	info[3] = 0x100 - info[3];
+
+	for (i = 0; i < HDMI_SIZE_VSI_INFOFRAME; i++)
+		hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i, info[i]);
+
+	hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, m_PACKET_VSI_EN,
+		  v_PACKET_VSI_EN(1));
+
+	return 0;
+}
+
+static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
+{
+	struct hdmi_data_info *data = &hdmi->hdmi_data;
+	int c0_c2_change = 0;
+	int csc_enable = 0;
+	int csc_mode = 0;
+	int auto_csc = 0;
+	int value;
+	int i;
+
+	/* Input video mode is SDR RGB24bit, data enable signal from external */
+	hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL1, v_DE_EXTERNAL |
+		    v_VIDEO_INPUT_FORMAT(VIDEO_INPUT_SDR_RGB444));
+
+	/* Input color hardcode to RGB, and output color hardcode to RGB888 */
+	value = v_VIDEO_INPUT_BITS(VIDEO_INPUT_8BITS) |
+		v_VIDEO_OUTPUT_COLOR(0) |
+		v_VIDEO_INPUT_CSP(0);
+	hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL2, value);
+
+	if (data->enc_out_format == data->enc_out_format) {
+		if ((data->enc_in_format == HDMI_COLORSPACE_RGB) ||
+		    (data->enc_in_format >= HDMI_COLORSPACE_YUV444)) {
+			value = v_SOF_DISABLE | v_COLOR_DEPTH_NOT_INDICATED(1);
+			hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
+
+			hdmi_modb(hdmi, HDMI_VIDEO_CONTRL,
+				  m_VIDEO_AUTO_CSC | m_VIDEO_C0_C2_SWAP,
+				  v_VIDEO_AUTO_CSC(AUTO_CSC_DISABLE) |
+				  v_VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE));
+			return 0;
+		}
+	}
+
+	if (data->colorimetry == HDMI_COLORIMETRY_ITU_601) {
+		if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
+		    (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
+			csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT;
+			auto_csc = AUTO_CSC_DISABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = v_CSC_ENABLE;
+		} else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
+			   (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
+			csc_mode = CSC_ITU601_16_235_TO_RGB_0_255_8BIT;
+			auto_csc = AUTO_CSC_ENABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = v_CSC_DISABLE;
+		}
+	} else {
+		if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
+		    (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
+			csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT;
+			auto_csc = AUTO_CSC_DISABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = v_CSC_ENABLE;
+		} else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
+			   (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
+			csc_mode = CSC_ITU709_16_235_TO_RGB_0_255_8BIT;
+			auto_csc = AUTO_CSC_ENABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = v_CSC_DISABLE;
+		}
+	}
+
+	for (i = 0; i < 24; i++)
+		hdmi_writeb(hdmi, HDMI_VIDEO_CSC_COEF + i,
+			    coeff_csc[csc_mode][i]);
+
+	value = v_SOF_DISABLE | csc_enable | v_COLOR_DEPTH_NOT_INDICATED(1);
+	hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
+	hdmi_modb(hdmi, HDMI_VIDEO_CONTRL, m_VIDEO_AUTO_CSC |
+		  m_VIDEO_C0_C2_SWAP, v_VIDEO_AUTO_CSC(auto_csc) |
+		  v_VIDEO_C0_C2_SWAP(c0_c2_change));
+
+	return 0;
+}
+
+static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi,
+					 struct drm_display_mode *mode)
+{
+	int value;
+
+	/* Set detail external video timing polarity and interlace mode */
+	value = v_EXTERANL_VIDEO(1);
+	value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
+		 v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
+	value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
+		 v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
+	value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
+		 v_INETLACE(1) : v_INETLACE(0);
+	hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
+
+	/* Set detail external video timing */
+	value = mode->htotal;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
+
+	value = mode->htotal - mode->hdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
+
+	value = mode->hsync_start - mode->hdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
+
+	value = mode->hsync_end - mode->hsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
+
+	value = mode->vtotal;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
+
+	value = mode->vtotal - mode->vdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
+
+	value = mode->vsync_start - mode->vdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
+
+	value = mode->vsync_end - mode->vsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
+
+	hdmi_writeb(hdmi, HDMI_PHY_PRE_DIV_RATIO, 0x1e);
+	hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_LOW, 0x2c);
+	hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH, 0x01);
+
+	return 0;
+}
+
+static int inno_hdmi_setup(struct inno_hdmi *hdmi,
+			   struct drm_display_mode *mode)
+{
+	int value;
+
+	hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
+
+	hdmi->hdmi_data.enc_in_format = HDMI_COLORSPACE_RGB;
+	hdmi->hdmi_data.enc_out_format = HDMI_COLORSPACE_RGB;
+
+	if ((hdmi->hdmi_data.vic == 6) || (hdmi->hdmi_data.vic == 7) ||
+	    (hdmi->hdmi_data.vic == 21) || (hdmi->hdmi_data.vic == 22) ||
+	    (hdmi->hdmi_data.vic == 2) || (hdmi->hdmi_data.vic == 3) ||
+	    (hdmi->hdmi_data.vic == 17) || (hdmi->hdmi_data.vic == 18))
+		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601;
+	else
+		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
+
+	/* Mute video and audio output */
+	hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
+		  v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1));
+
+	/* Set HDMI Mode */
+	hdmi_writeb(hdmi, HDMI_HDCP_CTRL,
+		    v_HDMI_DVI(hdmi->hdmi_data.sink_is_hdmi));
+
+	inno_hdmi_config_video_timing(hdmi, mode);
+
+	inno_hdmi_config_video_csc(hdmi);
+
+	if (hdmi->hdmi_data.sink_is_hdmi) {
+		inno_hdmi_config_video_avi(hdmi);
+		inno_hdmi_config_video_vsi(hdmi);
+	}
+
+	/*
+	 * When IP controller have configured to an accurate video
+	 * timing, then the TMDS clock source would be switched to
+	 * DCLK_LCDC, so we need to init the TMDS rate to mode pixel
+	 * clock rate, and reconfigure the DDC clock.
+	 */
+	hdmi->tmds_rate = mode->clock * 1000;
+	inno_hdmi_i2c_init(hdmi);
+
+	/* Unmute video and audio output */
+	hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
+		  v_AUDIO_MUTE(0) | v_VIDEO_MUTE(0));
+
+	return 0;
+}
+
+static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder,
+				       struct drm_display_mode *mode,
+				       struct drm_display_mode *adj_mode)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+
+	inno_hdmi_setup(hdmi, adj_mode);
+
+	/* Store the display mode for plugin/DPMS poweron events */
+	memcpy(&hdmi->previous_mode, adj_mode, sizeof(hdmi->previous_mode));
+}
+
+static void inno_hdmi_encoder_enable(struct drm_encoder *encoder)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+
+	rockchip_drm_crtc_mode_config(encoder->crtc, DRM_MODE_CONNECTOR_HDMIA,
+				      ROCKCHIP_OUT_MODE_P888);
+
+	inno_hdmi_set_pwr_mode(hdmi, NORMAL);
+}
+
+static void inno_hdmi_encoder_disable(struct drm_encoder *encoder)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+
+	inno_hdmi_set_pwr_mode(hdmi, LOWER_PWR);
+}
+
+static bool inno_hdmi_encoder_mode_fixup(struct drm_encoder *encoder,
+					 const struct drm_display_mode *mode,
+					 struct drm_display_mode *adj_mode)
+{
+	return true;
+}
+
+static struct drm_encoder_helper_funcs inno_hdmi_encoder_helper_funcs = {
+	.enable     = inno_hdmi_encoder_enable,
+	.disable    = inno_hdmi_encoder_disable,
+	.mode_fixup = inno_hdmi_encoder_mode_fixup,
+	.mode_set   = inno_hdmi_encoder_mode_set,
+};
+
+static struct drm_encoder_funcs inno_hdmi_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static enum drm_connector_status
+inno_hdmi_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+
+	return (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
+		connector_status_connected : connector_status_disconnected;
+}
+
+static int inno_hdmi_connector_get_modes(struct drm_connector *connector)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+	struct edid *edid;
+	int ret = 0;
+
+	if (!hdmi->ddc)
+		return 0;
+
+	edid = drm_get_edid(connector, hdmi->ddc);
+	if (edid) {
+		hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
+		hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
+		drm_mode_connector_update_edid_property(connector, edid);
+		ret = drm_add_edid_modes(connector, edid);
+		kfree(edid);
+	}
+
+	return ret;
+}
+
+static enum drm_mode_status
+inno_hdmi_connector_mode_valid(struct drm_connector *connector,
+			       struct drm_display_mode *mode)
+{
+	return MODE_OK;
+}
+
+static struct drm_encoder *
+inno_hdmi_connector_best_encoder(struct drm_connector *connector)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+
+	return &hdmi->encoder;
+}
+
+static int
+inno_hdmi_probe_single_connector_modes(struct drm_connector *connector,
+				       uint32_t maxX, uint32_t maxY)
+{
+	return drm_helper_probe_single_connector_modes(connector, 1920, 1080);
+}
+
+static void inno_hdmi_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs inno_hdmi_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.fill_modes = inno_hdmi_probe_single_connector_modes,
+	.detect = inno_hdmi_connector_detect,
+	.destroy = inno_hdmi_connector_destroy,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static struct drm_connector_helper_funcs inno_hdmi_connector_helper_funcs = {
+	.get_modes = inno_hdmi_connector_get_modes,
+	.mode_valid = inno_hdmi_connector_mode_valid,
+	.best_encoder = inno_hdmi_connector_best_encoder,
+};
+
+static int inno_hdmi_register(struct drm_device *drm, struct inno_hdmi *hdmi)
+{
+	struct drm_encoder *encoder = &hdmi->encoder;
+	struct device *dev = hdmi->dev;
+
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
+
+	/*
+	 * If we failed to find the CRTC(s) which this encoder is
+	 * supposed to be connected to, it's because the CRTC has
+	 * not been registered yet.  Defer probing, and hope that
+	 * the required CRTC is added later.
+	 */
+	if (encoder->possible_crtcs == 0)
+		return -EPROBE_DEFER;
+
+	drm_encoder_helper_add(encoder, &inno_hdmi_encoder_helper_funcs);
+	drm_encoder_init(drm, encoder, &inno_hdmi_encoder_funcs,
+			 DRM_MODE_ENCODER_TMDS, NULL);
+
+	hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
+
+	drm_connector_helper_add(&hdmi->connector,
+				 &inno_hdmi_connector_helper_funcs);
+	drm_connector_init(drm, &hdmi->connector, &inno_hdmi_connector_funcs,
+			   DRM_MODE_CONNECTOR_HDMIA);
+
+	drm_mode_connector_attach_encoder(&hdmi->connector, encoder);
+
+	return 0;
+}
+
+static irqreturn_t inno_hdmi_i2c_irq(struct inno_hdmi *hdmi)
+{
+	struct inno_hdmi_i2c *i2c = hdmi->i2c;
+	u8 stat;
+
+	stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
+	if (!(stat & m_INT_EDID_READY))
+		return IRQ_NONE;
+
+	/* Clear HDMI EDID interrupt flag */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+
+	complete(&i2c->cmp);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t inno_hdmi_hardirq(int irq, void *dev_id)
+{
+	struct inno_hdmi *hdmi = dev_id;
+	irqreturn_t ret = IRQ_NONE;
+	u8 interrupt;
+
+	if (hdmi->i2c)
+		ret = inno_hdmi_i2c_irq(hdmi);
+
+	interrupt = hdmi_readb(hdmi, HDMI_STATUS);
+	if (interrupt & m_INT_HOTPLUG) {
+		hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG);
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	return ret;
+}
+
+static irqreturn_t inno_hdmi_irq(int irq, void *dev_id)
+{
+	struct inno_hdmi *hdmi = dev_id;
+
+	drm_helper_hpd_irq_event(hdmi->connector.dev);
+
+	return IRQ_HANDLED;
+}
+
+static int inno_hdmi_i2c_wait(struct inno_hdmi *hdmi)
+{
+	struct inno_hdmi_i2c *i2c = hdmi->i2c;
+	int stat;
+
+	stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
+	if (!stat) {
+		stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
+		if (!stat)
+			return -EAGAIN;
+	}
+
+	return 0;
+}
+
+static int inno_hdmi_i2c_read(struct inno_hdmi *hdmi, struct i2c_msg *msgs)
+{
+	int length = msgs->len;
+	u8 *buf = msgs->buf;
+	int ret;
+
+	ret = inno_hdmi_i2c_wait(hdmi);
+	if (ret)
+		return ret;
+
+	while (length--)
+		*buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
+
+	return 0;
+}
+
+static int inno_hdmi_i2c_write(struct inno_hdmi *hdmi, struct i2c_msg *msgs)
+{
+	struct inno_hdmi_i2c *i2c = hdmi->i2c;
+
+	/*
+	 * The DDC module only support read EDID message, so
+	 * we assume that each word write to this i2c adapter
+	 * should be the offset of EDID word address.
+	 */
+	if ((msgs->len != 1) ||
+	    ((msgs->addr != DDC_ADDR) && (msgs->addr != DDC_SEGMENT_ADDR)))
+		return -EINVAL;
+
+	reinit_completion(&i2c->cmp);
+
+	if (msgs->addr == DDC_SEGMENT_ADDR)
+		hdmi->i2c->segment_addr = msgs->buf[0];
+	if (msgs->addr == DDC_ADDR)
+		hdmi->i2c->ddc_addr = msgs->buf[0];
+
+	/* Set edid fifo first addr */
+	hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
+
+	/* Set edid word address 0x00/0x80 */
+	hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
+
+	/* Set edid segment pointer */
+	hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr);
+
+	return 0;
+}
+
+static int inno_hdmi_i2c_xfer(struct i2c_adapter *adap,
+			      struct i2c_msg *msgs, int num)
+{
+	struct inno_hdmi *hdmi = i2c_get_adapdata(adap);
+	struct inno_hdmi_i2c *i2c = hdmi->i2c;
+	int i, ret = 0;
+
+	mutex_lock(&i2c->lock);
+
+	/* Clear the EDID interrupt flag and unmute the interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY);
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+
+	for (i = 0; i < num; i++) {
+		dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n",
+			i + 1, num, msgs[i].len, msgs[i].flags);
+
+		if (msgs[i].flags & I2C_M_RD)
+			ret = inno_hdmi_i2c_read(hdmi, &msgs[i]);
+		else
+			ret = inno_hdmi_i2c_write(hdmi, &msgs[i]);
+
+		if (ret < 0)
+			break;
+	}
+
+	if (!ret)
+		ret = num;
+
+	/* Mute HDMI EDID interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
+
+	mutex_unlock(&i2c->lock);
+
+	return ret;
+}
+
+static u32 inno_hdmi_i2c_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm inno_hdmi_algorithm = {
+	.master_xfer	= inno_hdmi_i2c_xfer,
+	.functionality	= inno_hdmi_i2c_func,
+};
+
+static struct i2c_adapter *inno_hdmi_i2c_adapter(struct inno_hdmi *hdmi)
+{
+	struct i2c_adapter *adap;
+	struct inno_hdmi_i2c *i2c;
+	int ret;
+
+	i2c = devm_kzalloc(hdmi->dev, sizeof(*i2c), GFP_KERNEL);
+	if (!i2c)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_init(&i2c->lock);
+	init_completion(&i2c->cmp);
+
+	adap = &i2c->adap;
+	adap->class = I2C_CLASS_DDC;
+	adap->owner = THIS_MODULE;
+	adap->dev.parent = hdmi->dev;
+	adap->dev.of_node = hdmi->dev->of_node;
+	adap->algo = &inno_hdmi_algorithm;
+	strlcpy(adap->name, "Inno HDMI", sizeof(adap->name));
+	i2c_set_adapdata(adap, hdmi);
+
+	ret = i2c_add_adapter(adap);
+	if (ret) {
+		dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name);
+		devm_kfree(hdmi->dev, i2c);
+		return ERR_PTR(ret);
+	}
+
+	hdmi->i2c = i2c;
+
+	dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name);
+
+	return adap;
+}
+
+static int inno_hdmi_bind(struct device *dev, struct device *master,
+				 void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = data;
+	struct inno_hdmi *hdmi;
+	struct resource *iores;
+	int irq;
+	int ret;
+
+	hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
+	if (!hdmi)
+		return -ENOMEM;
+
+	hdmi->dev = dev;
+	hdmi->drm_dev = drm;
+
+	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!iores)
+		return -ENXIO;
+
+	hdmi->regs = devm_ioremap_resource(dev, iores);
+	if (IS_ERR(hdmi->regs))
+		return PTR_ERR(hdmi->regs);
+
+	hdmi->pclk = devm_clk_get(hdmi->dev, "pclk");
+	if (IS_ERR(hdmi->pclk)) {
+		dev_err(hdmi->dev, "Unable to get HDMI pclk clk\n");
+		return PTR_ERR(hdmi->pclk);
+	}
+
+	ret = clk_prepare_enable(hdmi->pclk);
+	if (ret) {
+		dev_err(hdmi->dev, "Cannot enable HDMI pclk clock: %d\n", ret);
+		return ret;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	inno_hdmi_reset(hdmi);
+
+	hdmi->ddc = inno_hdmi_i2c_adapter(hdmi);
+	if (IS_ERR(hdmi->ddc)) {
+		hdmi->ddc = NULL;
+		return PTR_ERR(hdmi->ddc);
+	}
+
+	/*
+	 * When IP controller haven't configured to an accurate video
+	 * timing, then the TMDS clock source would be switched to
+	 * PCLK_HDMI, so we need to init the TMDS rate to PCLK rate,
+	 * and reconfigure the DDC clock.
+	 */
+	hdmi->tmds_rate = clk_get_rate(hdmi->pclk);
+	inno_hdmi_i2c_init(hdmi);
+
+	ret = inno_hdmi_register(drm, hdmi);
+	if (ret)
+		return ret;
+
+	dev_set_drvdata(dev, hdmi);
+
+	/* Unmute hotplug interrupt */
+	hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
+
+	ret = devm_request_threaded_irq(dev, irq, inno_hdmi_hardirq,
+					inno_hdmi_irq, IRQF_SHARED,
+					dev_name(dev), hdmi);
+
+	return ret;
+}
+
+static void inno_hdmi_unbind(struct device *dev, struct device *master,
+			     void *data)
+{
+	struct inno_hdmi *hdmi = dev_get_drvdata(dev);
+
+	hdmi->connector.funcs->destroy(&hdmi->connector);
+	hdmi->encoder.funcs->destroy(&hdmi->encoder);
+
+	clk_disable_unprepare(hdmi->pclk);
+	i2c_put_adapter(hdmi->ddc);
+}
+
+static const struct component_ops inno_hdmi_ops = {
+	.bind	= inno_hdmi_bind,
+	.unbind	= inno_hdmi_unbind,
+};
+
+static int inno_hdmi_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &inno_hdmi_ops);
+}
+
+static int inno_hdmi_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &inno_hdmi_ops);
+
+	return 0;
+}
+
+static const struct of_device_id inno_hdmi_dt_ids[] = {
+	{ .compatible = "rockchip,rk3036-inno-hdmi",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, inno_hdmi_dt_ids);
+
+static struct platform_driver inno_hdmi_driver = {
+	.probe  = inno_hdmi_probe,
+	.remove = inno_hdmi_remove,
+	.driver = {
+		.name = "innohdmi-rockchip",
+		.of_match_table = inno_hdmi_dt_ids,
+	},
+};
+
+module_platform_driver(inno_hdmi_driver);
+
+MODULE_AUTHOR("Zheng Yang <zhengyang at rock-chips.com>");
+MODULE_AUTHOR("Yakir Yang <ykk at rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip Specific INNO-HDMI Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:innohdmi-rockchip");
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.h b/drivers/gpu/drm/rockchip/inno_hdmi.h
new file mode 100644
index 0000000..4ff17ad
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.h
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ *    Zheng Yang <zhengyang at rock-chips.com>
+ *    Yakir Yang <ykk at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __INNO_HDMI_H__
+#define __INNO_HDMI_H__
+
+#define DDC_SEGMENT_ADDR		0x30
+
+enum PWR_MODE {
+	NORMAL,
+	LOWER_PWR,
+};
+
+#define HDMI_SCL_RATE			(100*1000)
+#define DDC_BUS_FREQ_L			0x4b
+#define DDC_BUS_FREQ_H			0x4c
+
+#define HDMI_SYS_CTRL			0x00
+#define m_RST_ANALOG			(1 << 6)
+#define v_RST_ANALOG			(0 << 6)
+#define v_NOT_RST_ANALOG		(1 << 6)
+#define m_RST_DIGITAL			(1 << 5)
+#define v_RST_DIGITAL			(0 << 5)
+#define v_NOT_RST_DIGITAL		(1 << 5)
+#define m_REG_CLK_INV			(1 << 4)
+#define v_REG_CLK_NOT_INV		(0 << 4)
+#define v_REG_CLK_INV			(1 << 4)
+#define m_VCLK_INV			(1 << 3)
+#define v_VCLK_NOT_INV			(0 << 3)
+#define v_VCLK_INV			(1 << 3)
+#define m_REG_CLK_SOURCE		(1 << 2)
+#define v_REG_CLK_SOURCE_TMDS		(0 << 2)
+#define v_REG_CLK_SOURCE_SYS		(1 << 2)
+#define m_POWER				(1 << 1)
+#define v_PWR_ON			(0 << 1)
+#define v_PWR_OFF			(1 << 1)
+#define m_INT_POL			(1 << 0)
+#define v_INT_POL_HIGH			1
+#define v_INT_POL_LOW			0
+
+#define HDMI_VIDEO_CONTRL1		0x01
+#define m_VIDEO_INPUT_FORMAT		(7 << 1)
+#define m_DE_SOURCE			(1 << 0)
+#define v_VIDEO_INPUT_FORMAT(n)		(n << 1)
+#define v_DE_EXTERNAL			1
+#define v_DE_INTERNAL			0
+enum {
+	VIDEO_INPUT_SDR_RGB444 = 0,
+	VIDEO_INPUT_DDR_RGB444 = 5,
+	VIDEO_INPUT_DDR_YCBCR422 = 6
+};
+
+#define HDMI_VIDEO_CONTRL2		0x02
+#define m_VIDEO_OUTPUT_COLOR		(3 << 6)
+#define m_VIDEO_INPUT_BITS		(3 << 4)
+#define m_VIDEO_INPUT_CSP		(1 << 0)
+#define v_VIDEO_OUTPUT_COLOR(n)		(((n) & 0x3) << 6)
+#define v_VIDEO_INPUT_BITS(n)		(n << 4)
+#define v_VIDEO_INPUT_CSP(n)		(n << 0)
+enum {
+	VIDEO_INPUT_12BITS = 0,
+	VIDEO_INPUT_10BITS = 1,
+	VIDEO_INPUT_REVERT = 2,
+	VIDEO_INPUT_8BITS = 3,
+};
+
+#define HDMI_VIDEO_CONTRL		0x03
+#define m_VIDEO_AUTO_CSC		(1 << 7)
+#define v_VIDEO_AUTO_CSC(n)		(n << 7)
+#define m_VIDEO_C0_C2_SWAP		(1 << 0)
+#define v_VIDEO_C0_C2_SWAP(n)		(n << 0)
+enum {
+	C0_C2_CHANGE_ENABLE = 0,
+	C0_C2_CHANGE_DISABLE = 1,
+	AUTO_CSC_DISABLE = 0,
+	AUTO_CSC_ENABLE = 1,
+};
+
+#define HDMI_VIDEO_CONTRL3		0x04
+#define m_COLOR_DEPTH_NOT_INDICATED	(1 << 4)
+#define m_SOF				(1 << 3)
+#define m_COLOR_RANGE			(1 << 2)
+#define m_CSC				(1 << 0)
+#define v_COLOR_DEPTH_NOT_INDICATED(n)	((n) << 4)
+#define v_SOF_ENABLE			(0 << 3)
+#define v_SOF_DISABLE			(1 << 3)
+#define v_COLOR_RANGE_FULL		(1 << 2)
+#define v_COLOR_RANGE_LIMITED		(0 << 2)
+#define v_CSC_ENABLE			1
+#define v_CSC_DISABLE			0
+
+#define HDMI_AV_MUTE			0x05
+#define m_AVMUTE_CLEAR			(1 << 7)
+#define m_AVMUTE_ENABLE			(1 << 6)
+#define m_AUDIO_MUTE			(1 << 1)
+#define m_VIDEO_BLACK			(1 << 0)
+#define v_AVMUTE_CLEAR(n)		(n << 7)
+#define v_AVMUTE_ENABLE(n)		(n << 6)
+#define v_AUDIO_MUTE(n)			(n << 1)
+#define v_VIDEO_MUTE(n)			(n << 0)
+
+#define HDMI_VIDEO_TIMING_CTL		0x08
+#define v_HSYNC_POLARITY(n)		(n << 3)
+#define v_VSYNC_POLARITY(n)		(n << 2)
+#define v_INETLACE(n)			(n << 1)
+#define v_EXTERANL_VIDEO(n)		(n << 0)
+
+#define HDMI_VIDEO_EXT_HTOTAL_L		0x09
+#define HDMI_VIDEO_EXT_HTOTAL_H		0x0a
+#define HDMI_VIDEO_EXT_HBLANK_L		0x0b
+#define HDMI_VIDEO_EXT_HBLANK_H		0x0c
+#define HDMI_VIDEO_EXT_HDELAY_L		0x0d
+#define HDMI_VIDEO_EXT_HDELAY_H		0x0e
+#define HDMI_VIDEO_EXT_HDURATION_L	0x0f
+#define HDMI_VIDEO_EXT_HDURATION_H	0x10
+#define HDMI_VIDEO_EXT_VTOTAL_L		0x11
+#define HDMI_VIDEO_EXT_VTOTAL_H		0x12
+#define HDMI_VIDEO_EXT_VBLANK		0x13
+#define HDMI_VIDEO_EXT_VDELAY		0x14
+#define HDMI_VIDEO_EXT_VDURATION	0x15
+
+#define HDMI_VIDEO_CSC_COEF		0x18
+
+#define HDMI_AUDIO_CTRL1		0x35
+enum {
+	CTS_SOURCE_INTERNAL = 0,
+	CTS_SOURCE_EXTERNAL = 1,
+};
+#define v_CTS_SOURCE(n)			(n << 7)
+
+enum {
+	DOWNSAMPLE_DISABLE = 0,
+	DOWNSAMPLE_1_2 = 1,
+	DOWNSAMPLE_1_4 = 2,
+};
+#define v_DOWN_SAMPLE(n)		(n << 5)
+
+enum {
+	AUDIO_SOURCE_IIS = 0,
+	AUDIO_SOURCE_SPDIF = 1,
+};
+#define v_AUDIO_SOURCE(n)		(n << 3)
+
+#define v_MCLK_ENABLE(n)		(n << 2)
+enum {
+	MCLK_128FS = 0,
+	MCLK_256FS = 1,
+	MCLK_384FS = 2,
+	MCLK_512FS = 3,
+};
+#define v_MCLK_RATIO(n)			(n)
+
+#define AUDIO_SAMPLE_RATE		0x37
+enum {
+	AUDIO_32K = 0x3,
+	AUDIO_441K = 0x0,
+	AUDIO_48K = 0x2,
+	AUDIO_882K = 0x8,
+	AUDIO_96K = 0xa,
+	AUDIO_1764K = 0xc,
+	AUDIO_192K = 0xe,
+};
+
+#define AUDIO_I2S_MODE			0x38
+enum {
+	I2S_CHANNEL_1_2 = 1,
+	I2S_CHANNEL_3_4 = 3,
+	I2S_CHANNEL_5_6 = 7,
+	I2S_CHANNEL_7_8 = 0xf
+};
+#define v_I2S_CHANNEL(n)		((n) << 2)
+enum {
+	I2S_STANDARD = 0,
+	I2S_LEFT_JUSTIFIED = 1,
+	I2S_RIGHT_JUSTIFIED = 2,
+};
+#define v_I2S_MODE(n)			(n)
+
+#define AUDIO_I2S_MAP			0x39
+#define AUDIO_I2S_SWAPS_SPDIF		0x3a
+#define v_SPIDF_FREQ(n)			(n)
+
+#define N_32K				0x1000
+#define N_441K				0x1880
+#define N_882K				0x3100
+#define N_1764K				0x6200
+#define N_48K				0x1800
+#define N_96K				0x3000
+#define N_192K				0x6000
+
+#define HDMI_AUDIO_CHANNEL_STATUS	0x3e
+#define m_AUDIO_STATUS_NLPCM		(1 << 7)
+#define m_AUDIO_STATUS_USE		(1 << 6)
+#define m_AUDIO_STATUS_COPYRIGHT	(1 << 5)
+#define m_AUDIO_STATUS_ADDITION		(3 << 2)
+#define m_AUDIO_STATUS_CLK_ACCURACY	(2 << 0)
+#define v_AUDIO_STATUS_NLPCM(n)		((n & 1) << 7)
+#define AUDIO_N_H			0x3f
+#define AUDIO_N_M			0x40
+#define AUDIO_N_L			0x41
+
+#define HDMI_AUDIO_CTS_H		0x45
+#define HDMI_AUDIO_CTS_M		0x46
+#define HDMI_AUDIO_CTS_L		0x47
+
+#define HDMI_DDC_CLK_L			0x4b
+#define HDMI_DDC_CLK_H			0x4c
+
+#define HDMI_EDID_SEGMENT_POINTER	0x4d
+#define HDMI_EDID_WORD_ADDR		0x4e
+#define HDMI_EDID_FIFO_OFFSET		0x4f
+#define HDMI_EDID_FIFO_ADDR		0x50
+
+#define HDMI_PACKET_SEND_MANUAL		0x9c
+#define HDMI_PACKET_SEND_AUTO		0x9d
+#define m_PACKET_GCP_EN			(1 << 7)
+#define m_PACKET_MSI_EN			(1 << 6)
+#define m_PACKET_SDI_EN			(1 << 5)
+#define m_PACKET_VSI_EN			(1 << 4)
+#define v_PACKET_GCP_EN(n)		((n & 1) << 7)
+#define v_PACKET_MSI_EN(n)		((n & 1) << 6)
+#define v_PACKET_SDI_EN(n)		((n & 1) << 5)
+#define v_PACKET_VSI_EN(n)		((n & 1) << 4)
+
+#define HDMI_CONTROL_PACKET_BUF_INDEX	0x9f
+enum {
+	INFOFRAME_VSI = 0x05,
+	INFOFRAME_AVI = 0x06,
+	INFOFRAME_AAI = 0x08,
+};
+
+#define HDMI_CONTROL_PACKET_ADDR	0xa0
+#define HDMI_SIZE_VSI_INFOFRAME		0x0A
+#define HDMI_SIZE_AVI_INFOFRAME		0x11
+#define HDMI_SIZE_AUDIO_INFOFRAME	0x0F
+enum {
+	AVI_COLOR_MODE_RGB = 0,
+	AVI_COLOR_MODE_YCBCR422 = 1,
+	AVI_COLOR_MODE_YCBCR444 = 2,
+	AVI_COLORIMETRY_NO_DATA = 0,
+
+	AVI_COLORIMETRY_SMPTE_170M = 1,
+	AVI_COLORIMETRY_ITU709 = 2,
+	AVI_COLORIMETRY_EXTENDED = 3,
+
+	AVI_CODED_FRAME_ASPECT_NO_DATA = 0,
+	AVI_CODED_FRAME_ASPECT_4_3 = 1,
+	AVI_CODED_FRAME_ASPECT_16_9 = 2,
+
+	ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME = 0x08,
+	ACTIVE_ASPECT_RATE_4_3 = 0x09,
+	ACTIVE_ASPECT_RATE_16_9 = 0x0A,
+	ACTIVE_ASPECT_RATE_14_9 = 0x0B,
+};
+
+#define HDMI_HDCP_CTRL			0x52
+#define m_HDMI_DVI			(1 << 1)
+#define v_HDMI_DVI(n)			(n << 1)
+
+#define HDMI_INTERRUPT_MASK1		0xc0
+#define HDMI_INTERRUPT_STATUS1		0xc1
+#define	m_INT_ACTIVE_VSYNC		(1 << 5)
+#define m_INT_EDID_READY		(1 << 2)
+
+#define HDMI_INTERRUPT_MASK2		0xc2
+#define HDMI_INTERRUPT_STATUS2		0xc3
+#define m_INT_HDCP_ERR			(1 << 7)
+#define m_INT_BKSV_FLAG			(1 << 6)
+#define m_INT_HDCP_OK			(1 << 4)
+
+#define HDMI_STATUS			0xc8
+#define m_HOTPLUG			(1 << 7)
+#define m_MASK_INT_HOTPLUG		(1 << 5)
+#define m_INT_HOTPLUG			(1 << 1)
+#define v_MASK_INT_HOTPLUG(n)		((n & 0x1) << 5)
+
+#define HDMI_COLORBAR                   0xc9
+
+#define HDMI_PHY_SYNC			0xce
+#define HDMI_PHY_SYS_CTL		0xe0
+#define m_TMDS_CLK_SOURCE		(1 << 5)
+#define v_TMDS_FROM_PLL			(0 << 5)
+#define v_TMDS_FROM_GEN			(1 << 5)
+#define m_PHASE_CLK			(1 << 4)
+#define v_DEFAULT_PHASE			(0 << 4)
+#define v_SYNC_PHASE			(1 << 4)
+#define m_TMDS_CURRENT_PWR		(1 << 3)
+#define v_TURN_ON_CURRENT		(0 << 3)
+#define v_CAT_OFF_CURRENT		(1 << 3)
+#define m_BANDGAP_PWR			(1 << 2)
+#define v_BANDGAP_PWR_UP		(0 << 2)
+#define v_BANDGAP_PWR_DOWN		(1 << 2)
+#define m_PLL_PWR			(1 << 1)
+#define v_PLL_PWR_UP			(0 << 1)
+#define v_PLL_PWR_DOWN			(1 << 1)
+#define m_TMDS_CHG_PWR			(1 << 0)
+#define v_TMDS_CHG_PWR_UP		(0 << 0)
+#define v_TMDS_CHG_PWR_DOWN		(1 << 0)
+
+#define HDMI_PHY_CHG_PWR		0xe1
+#define v_CLK_CHG_PWR(n)		((n & 1) << 3)
+#define v_DATA_CHG_PWR(n)		((n & 7) << 0)
+
+#define HDMI_PHY_DRIVER			0xe2
+#define v_CLK_MAIN_DRIVER(n)		(n << 4)
+#define v_DATA_MAIN_DRIVER(n)		(n << 0)
+
+#define HDMI_PHY_PRE_EMPHASIS		0xe3
+#define v_PRE_EMPHASIS(n)		((n & 7) << 4)
+#define v_CLK_PRE_DRIVER(n)		((n & 3) << 2)
+#define v_DATA_PRE_DRIVER(n)		((n & 3) << 0)
+
+#define HDMI_PHY_FEEDBACK_DIV_RATIO_LOW		0xe7
+#define v_FEEDBACK_DIV_LOW(n)			(n & 0xff)
+#define HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH	0xe8
+#define v_FEEDBACK_DIV_HIGH(n)			(n & 1)
+
+#define HDMI_PHY_PRE_DIV_RATIO		0xed
+#define v_PRE_DIV_RATIO(n)		(n & 0x1f)
+
+#define HDMI_CEC_CTRL			0xd0
+#define m_ADJUST_FOR_HISENSE		(1 << 6)
+#define m_REJECT_RX_BROADCAST		(1 << 5)
+#define m_BUSFREETIME_ENABLE		(1 << 2)
+#define m_REJECT_RX			(1 << 1)
+#define m_START_TX			(1 << 0)
+
+#define HDMI_CEC_DATA			0xd1
+#define HDMI_CEC_TX_OFFSET		0xd2
+#define HDMI_CEC_RX_OFFSET		0xd3
+#define HDMI_CEC_CLK_H			0xd4
+#define HDMI_CEC_CLK_L			0xd5
+#define HDMI_CEC_TX_LENGTH		0xd6
+#define HDMI_CEC_RX_LENGTH		0xd7
+#define HDMI_CEC_TX_INT_MASK		0xd8
+#define m_TX_DONE			(1 << 3)
+#define m_TX_NOACK			(1 << 2)
+#define m_TX_BROADCAST_REJ		(1 << 1)
+#define m_TX_BUSNOTFREE			(1 << 0)
+
+#define HDMI_CEC_RX_INT_MASK		0xd9
+#define m_RX_LA_ERR			(1 << 4)
+#define m_RX_GLITCH			(1 << 3)
+#define m_RX_DONE			(1 << 0)
+
+#define HDMI_CEC_TX_INT			0xda
+#define HDMI_CEC_RX_INT			0xdb
+#define HDMI_CEC_BUSFREETIME_L		0xdc
+#define HDMI_CEC_BUSFREETIME_H		0xdd
+#define HDMI_CEC_LOGICADDR		0xde
+
+#endif /* __INNO_HDMI_H__ */
-- 
1.9.1




From linux at arm.linux.org.uk  Mon Jan 18 09:15:14 2016
From: linux at arm.linux.org.uk (Russell King - ARM Linux)
Date: Mon, 18 Jan 2016 17:15:14 +0000
Subject: [PATCH v4] drm/rockchip: hdmi: add Innosilicon HDMI support
In-Reply-To: <1453128140-29208-1-git-send-email-ykk@rock-chips.com>
References: <1453128123-29155-1-git-send-email-ykk@rock-chips.com>
 <1453128140-29208-1-git-send-email-ykk@rock-chips.com>
Message-ID: <20160118171514.GG19062@n2100.arm.linux.org.uk>

Hi,

Some comments below...

On Mon, Jan 18, 2016 at 10:42:20PM +0800, Yakir Yang wrote:
> +static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi)
> +{
> +	char info[HDMI_SIZE_AVI_INFOFRAME] = {0};
> +	int avi_color_mode;
> +	int i;
> +
> +	hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_AVI);
> +
> +	info[0] = 0x82;
> +	info[1] = 0x02;
> +	info[2] = 0x0D;
> +	info[3] = info[0] + info[1] + info[2];
> +
> +	if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_RGB)
> +		avi_color_mode = AVI_COLOR_MODE_RGB;
> +	else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV444)
> +		avi_color_mode = AVI_COLOR_MODE_YCBCR444;
> +	else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV422)
> +		avi_color_mode = AVI_COLOR_MODE_YCBCR422;
> +	else
> +		avi_color_mode = AVI_COLOR_MODE_RGB;
> +
> +	info[4] = (avi_color_mode << 5);
> +	info[5] = (AVI_COLORIMETRY_NO_DATA << 6) |
> +		  (AVI_CODED_FRAME_ASPECT_NO_DATA << 4) |
> +		  ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME;
> +
> +	info[6] = 0;
> +	info[7] = hdmi->hdmi_data.vic;
> +
> +	if (hdmi->hdmi_data.vic == 6 || hdmi->hdmi_data.vic == 7 ||
> +	    hdmi->hdmi_data.vic == 21 || hdmi->hdmi_data.vic == 22)
> +		info[8] = 1;
> +	else
> +		info[8] = 0;
> +
> +	/* Calculate avi info frame checKsum */
> +	for (i = 4; i < HDMI_SIZE_AVI_INFOFRAME; i++)
> +		info[3] += info[i];
> +	info[3] = 0x100 - info[3];
> +
> +	for (i = 0; i < HDMI_SIZE_AVI_INFOFRAME; i++)
> +		hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i, info[i]);
> +
> +	return 0;

Is there a reason why the helpers in drivers/video/hdmi.c can't be used
to generate this?

hdmi_avi_infoframe_init / hdmi_avi_infoframe_pack and
drm_hdmi_avi_infoframe_from_display_mode ?

> +}
> +
> +static int inno_hdmi_config_video_vsi(struct inno_hdmi *hdmi)
> +{
> +	char info[HDMI_SIZE_VSI_INFOFRAME] = {0};
> +	int i;
> +
> +	hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, m_PACKET_VSI_EN,
> +		  v_PACKET_VSI_EN(0));
> +
> +	hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_VSI);
> +
> +	/* Header Bytes */
> +	info[0] = 0x81;
> +	info[1] = 0x01;
> +
> +	/* PB1 - PB3 contain the 24bit IEEE Registration Identifier */
> +	info[4] = 0x03;
> +	info[5] = 0x0c;
> +	info[6] = 0x00;
> +
> +	/* PB4 - HDMI_Video_Format into bits 7:5 */
> +	info[7] = 0;
> +
> +	/*
> +	 * PB5 - Depending on the video format, this byte will contain
> +	 * either the HDMI_VIC code in buts 7:0, OR the 3D_Structure in
> +	 * bits 7:4.
> +	 */
> +	info[2] = 0x06 - 2;
> +	info[8] = 0;
> +	info[9] = 0;
> +
> +	info[3] = info[0] + info[1] + info[2];
> +
> +	/* Calculate info frame checKsum */
> +	for (i = 4; i < HDMI_SIZE_VSI_INFOFRAME; i++)
> +		info[3] += info[i];
> +	info[3] = 0x100 - info[3];
> +
> +	for (i = 0; i < HDMI_SIZE_VSI_INFOFRAME; i++)
> +		hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i, info[i]);
> +
> +	hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, m_PACKET_VSI_EN,
> +		  v_PACKET_VSI_EN(1));
> +
> +	return 0;

hdmi_vendor_infoframe_init / hdmi_vendor_infoframe_pack?

You can probably use the same function to upload the control packet
too - something like:

static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi, int setup_rc,
	union hdmi_infoframe *frame, u32 mask, u32 disable, u32 enable)
{
	if (mask)
		hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, disable);

	if (setup_rc >= 0) {
		u8 packed_frame[YOUR_MAXIMUM_INFO_FRAME_SIZE];
		ssize_t rc, i;

		rc = hdmi_infoframe_pack(frame, packed_frame,
					 sizeof(packed_frame));
		if (rc < 0)
			return rc;

		for (i = 0; i < rc; i++)
			hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i, buf[i]);

		if (mask)
			hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, enable);
	}

	return setup_rc;
}

static int inno_hdmi_config_video_vsi(struct inno_hdmi *hdmi)
{
	union hdmi_infoframe frame;

	rc = drm_hdmi_vendor_infoframe_from_display_mode(&frame.vendor.hdmi,
							 the_drm_display_mode);

	return inno_hdmi_upload_frame(hdmi, rc, &frame, m_PACKET_VSI_EN,
				      v_PACKET_VSI_EN(0), v_PACKET_VSI_EN(1));
}

> +static int inno_hdmi_i2c_wait(struct inno_hdmi *hdmi)
> +{
> +	struct inno_hdmi_i2c *i2c = hdmi->i2c;
> +	int stat;
> +
> +	stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
> +	if (!stat) {
> +		stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
> +		if (!stat)
> +			return -EAGAIN;

What's the reason for this double wait_for_completion?  This probably
needs a comment, otherwise it looks like some weird cut-n-paste error.

> +static int inno_hdmi_i2c_write(struct inno_hdmi *hdmi, struct i2c_msg *msgs)
> +{
> +	struct inno_hdmi_i2c *i2c = hdmi->i2c;

You seem to have a local i2c pointer...

> +
> +	/*
> +	 * The DDC module only support read EDID message, so
> +	 * we assume that each word write to this i2c adapter
> +	 * should be the offset of EDID word address.
> +	 */
> +	if ((msgs->len != 1) ||
> +	    ((msgs->addr != DDC_ADDR) && (msgs->addr != DDC_SEGMENT_ADDR)))
> +		return -EINVAL;
> +
> +	reinit_completion(&i2c->cmp);
> +
> +	if (msgs->addr == DDC_SEGMENT_ADDR)
> +		hdmi->i2c->segment_addr = msgs->buf[0];
> +	if (msgs->addr == DDC_ADDR)
> +		hdmi->i2c->ddc_addr = msgs->buf[0];
> +
> +	/* Set edid fifo first addr */
> +	hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
> +
> +	/* Set edid word address 0x00/0x80 */
> +	hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
> +
> +	/* Set edid segment pointer */
> +	hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr);

but then you don't use it in the four locations above.


-- 
RMK's Patch system: http://www.arm.linux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.


From ykk at rock-chips.com  Mon Jan 18 19:50:29 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Tue, 19 Jan 2016 11:50:29 +0800
Subject: [PATCH v4] drm/rockchip: hdmi: add Innosilicon HDMI support
In-Reply-To: <20160118171514.GG19062@n2100.arm.linux.org.uk>
References: <1453128123-29155-1-git-send-email-ykk@rock-chips.com>
 <1453128140-29208-1-git-send-email-ykk@rock-chips.com>
 <20160118171514.GG19062@n2100.arm.linux.org.uk>
Message-ID: <569DB285.1000009@rock-chips.com>

Hi Russell,

Thanks for your comments :-D

On 01/19/2016 01:15 AM, Russell King - ARM Linux wrote:
> Hi,
>
> Some comments below...
>
> On Mon, Jan 18, 2016 at 10:42:20PM +0800, Yakir Yang wrote:
>> +static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi)
>> +{
>> +	char info[HDMI_SIZE_AVI_INFOFRAME] = {0};
>> +	int avi_color_mode;
>> +	int i;
>> +
>> +	hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_AVI);
>> +
>> +	info[0] = 0x82;
>> +	info[1] = 0x02;
>> +	info[2] = 0x0D;
>> +	info[3] = info[0] + info[1] + info[2];
>> +
>> +	if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_RGB)
>> +		avi_color_mode = AVI_COLOR_MODE_RGB;
>> +	else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV444)
>> +		avi_color_mode = AVI_COLOR_MODE_YCBCR444;
>> +	else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV422)
>> +		avi_color_mode = AVI_COLOR_MODE_YCBCR422;
>> +	else
>> +		avi_color_mode = AVI_COLOR_MODE_RGB;
>> +
>> +	info[4] = (avi_color_mode << 5);
>> +	info[5] = (AVI_COLORIMETRY_NO_DATA << 6) |
>> +		  (AVI_CODED_FRAME_ASPECT_NO_DATA << 4) |
>> +		  ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME;
>> +
>> +	info[6] = 0;
>> +	info[7] = hdmi->hdmi_data.vic;
>> +
>> +	if (hdmi->hdmi_data.vic == 6 || hdmi->hdmi_data.vic == 7 ||
>> +	    hdmi->hdmi_data.vic == 21 || hdmi->hdmi_data.vic == 22)
>> +		info[8] = 1;
>> +	else
>> +		info[8] = 0;
>> +
>> +	/* Calculate avi info frame checKsum */
>> +	for (i = 4; i < HDMI_SIZE_AVI_INFOFRAME; i++)
>> +		info[3] += info[i];
>> +	info[3] = 0x100 - info[3];
>> +
>> +	for (i = 0; i < HDMI_SIZE_AVI_INFOFRAME; i++)
>> +		hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i, info[i]);
>> +
>> +	return 0;
> Is there a reason why the helpers in drivers/video/hdmi.c can't be used
> to generate this?
>
> hdmi_avi_infoframe_init / hdmi_avi_infoframe_pack and
> drm_hdmi_avi_infoframe_from_display_mode ?

Great, thanks for point out, it would make code much clean with those 
helper functions.

>> +}
>> +
>> +static int inno_hdmi_config_video_vsi(struct inno_hdmi *hdmi)
>> +{
>> +	char info[HDMI_SIZE_VSI_INFOFRAME] = {0};
>> +	int i;
>> +
>> +	hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, m_PACKET_VSI_EN,
>> +		  v_PACKET_VSI_EN(0));
>> +
>> +	hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_VSI);
>> +
>> +	/* Header Bytes */
>> +	info[0] = 0x81;
>> +	info[1] = 0x01;
>> +
>> +	/* PB1 - PB3 contain the 24bit IEEE Registration Identifier */
>> +	info[4] = 0x03;
>> +	info[5] = 0x0c;
>> +	info[6] = 0x00;
>> +
>> +	/* PB4 - HDMI_Video_Format into bits 7:5 */
>> +	info[7] = 0;
>> +
>> +	/*
>> +	 * PB5 - Depending on the video format, this byte will contain
>> +	 * either the HDMI_VIC code in buts 7:0, OR the 3D_Structure in
>> +	 * bits 7:4.
>> +	 */
>> +	info[2] = 0x06 - 2;
>> +	info[8] = 0;
>> +	info[9] = 0;
>> +
>> +	info[3] = info[0] + info[1] + info[2];
>> +
>> +	/* Calculate info frame checKsum */
>> +	for (i = 4; i < HDMI_SIZE_VSI_INFOFRAME; i++)
>> +		info[3] += info[i];
>> +	info[3] = 0x100 - info[3];
>> +
>> +	for (i = 0; i < HDMI_SIZE_VSI_INFOFRAME; i++)
>> +		hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i, info[i]);
>> +
>> +	hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, m_PACKET_VSI_EN,
>> +		  v_PACKET_VSI_EN(1));
>> +
>> +	return 0;
> hdmi_vendor_infoframe_init / hdmi_vendor_infoframe_pack?
>
> You can probably use the same function to upload the control packet
> too - something like:
>
> static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi, int setup_rc,
> 	union hdmi_infoframe *frame, u32 mask, u32 disable, u32 enable)
> {
> 	if (mask)
> 		hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, disable);
>
> 	if (setup_rc >= 0) {
> 		u8 packed_frame[YOUR_MAXIMUM_INFO_FRAME_SIZE];
> 		ssize_t rc, i;
>
> 		rc = hdmi_infoframe_pack(frame, packed_frame,
> 					 sizeof(packed_frame));
> 		if (rc < 0)
> 			return rc;
>
> 		for (i = 0; i < rc; i++)
> 			hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i, buf[i]);
>
> 		if (mask)
> 			hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, enable);
> 	}
>
> 	return setup_rc;
> }
>
> static int inno_hdmi_config_video_vsi(struct inno_hdmi *hdmi)
> {
> 	union hdmi_infoframe frame;
>
> 	rc = drm_hdmi_vendor_infoframe_from_display_mode(&frame.vendor.hdmi,
> 							 the_drm_display_mode);
>
> 	return inno_hdmi_upload_frame(hdmi, rc, &frame, m_PACKET_VSI_EN,
> 				      v_PACKET_VSI_EN(0), v_PACKET_VSI_EN(1));
> }
>

Ah.... appreciate for the code,  it's great suggest, thanks.

Also I found a mistaken with previous "inno_hdmi_config_video_vsi"
function that we don't need to send the HDMI vendor infoframes if
we are not using a 4K or stereoscopic 3D mode.  :)

>> +static int inno_hdmi_i2c_wait(struct inno_hdmi *hdmi)
>> +{
>> +	struct inno_hdmi_i2c *i2c = hdmi->i2c;
>> +	int stat;
>> +
>> +	stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
>> +	if (!stat) {
>> +		stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
>> +		if (!stat)
>> +			return -EAGAIN;
> What's the reason for this double wait_for_completion?  This probably
> needs a comment, otherwise it looks like some weird cut-n-paste error.

Yep, you 're right, don't need double wait here.

>> +static int inno_hdmi_i2c_write(struct inno_hdmi *hdmi, struct i2c_msg *msgs)
>> +{
>> +	struct inno_hdmi_i2c *i2c = hdmi->i2c;
> You seem to have a local i2c pointer...
Ah, got it  ;)
>> +
>> +	/*
>> +	 * The DDC module only support read EDID message, so
>> +	 * we assume that each word write to this i2c adapter
>> +	 * should be the offset of EDID word address.
>> +	 */
>> +	if ((msgs->len != 1) ||
>> +	    ((msgs->addr != DDC_ADDR) && (msgs->addr != DDC_SEGMENT_ADDR)))
>> +		return -EINVAL;
>> +
>> +	reinit_completion(&i2c->cmp);
>> +
>> +	if (msgs->addr == DDC_SEGMENT_ADDR)
>> +		hdmi->i2c->segment_addr = msgs->buf[0];
>> +	if (msgs->addr == DDC_ADDR)
>> +		hdmi->i2c->ddc_addr = msgs->buf[0];
>> +
>> +	/* Set edid fifo first addr */
>> +	hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
>> +
>> +	/* Set edid word address 0x00/0x80 */
>> +	hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
>> +
>> +	/* Set edid segment pointer */
>> +	hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr);
> but then you don't use it in the four locations above.
>
>

Thanks,
- Yakir




From ykk at rock-chips.com  Mon Jan 18 19:59:01 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Tue, 19 Jan 2016 11:59:01 +0800
Subject: [PATCH v5 0/1] Introduce Innosilicon HDMI driver on Rockchip platforms
Message-ID: <1453175941-18066-1-git-send-email-ykk@rock-chips.com>


Here are a brief introduction to Innosilicon HDMI IP:
 - Support HDMI 1.4a, HDCP 1.2 and DVI 1.0 standard compliant transmitter
 - Support HDMI1.4 a/b 3D function defined in HDMI 1.4 a/b spec
 - Digital video interface supports a pixel size of 24, 30, 36, 48bits color
   depth in RGB
 - S/PDIF output supports PCM, Dolby Digital, DTS digital audio transmission
   (32-192kHz Fs) using IEC60958 and IEC 61937
 - The EDID and CEC function are also supported by Innosilicon HDMI Transmitter
   Controlle


Changes in v5:
- Use hdmi_infoframe helper functions to packed the infoframe (Russell)
- Remove the unused double wait_for_completion_timeout for ddc transfer (Russell)
- Remove the unused local variable in "inno_hdmi_i2c_write()" function (Russell)

Changes in v4:
- Modify the commit title "drm/rockchip: hdmi: ..." (Mark)
- Correct the "DKMS" to "DPMS" (Mark)
- Fix over 80 characters problems (Mark)
- Remove encoder .prepare/.commit helper functions, and move the vop mode
  configure function into encoder .enable helper functions. (Mark)

Changes in v3:
- Use encoder enable/disable function, and remove the encoder DPMS function
- Keep HDMI PLL power on in standby mode

Changes in v2:
- Using DRM atomic helper functions for connector init (Mark)
- Remove "hdmi->connector.encoder = encoder;" (Mark)

Yakir Yang (1):
  drm/rockchip: hdmi: add Innosilicon HDMI support

 drivers/gpu/drm/rockchip/Kconfig     |   8 +
 drivers/gpu/drm/rockchip/Makefile    |   1 +
 drivers/gpu/drm/rockchip/inno_hdmi.c | 941 +++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/inno_hdmi.h | 362 ++++++++++++++
 4 files changed, 1312 insertions(+)
 create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.c
 create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.h

-- 
1.9.1




From ykk at rock-chips.com  Mon Jan 18 20:01:10 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Tue, 19 Jan 2016 12:01:10 +0800
Subject: [PATCH v5] drm/rockchip: hdmi: add Innosilicon HDMI support
In-Reply-To: <1453175941-18066-1-git-send-email-ykk@rock-chips.com>
References: <1453175941-18066-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1453176070-18120-1-git-send-email-ykk@rock-chips.com>

The Innosilicon HDMI is a low power HDMI 1.4 transmitter
IP, and it have been integrated on some rockchip CPUs
(like RK3036, RK312x).

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v5:
- Use hdmi_infoframe helper functions to packed the infoframe (Russell)
- Remove the unused double wait_for_completion_timeout for ddc transfer (Russell)
- Remove the unused local variable in "inno_hdmi_i2c_write()" function (Russell)

Changes in v4:
- Modify the commit title "drm/rockchip: hdmi: ..." (Mark)
- Correct the "DKMS" to "DPMS" (Mark)
- Fix over 80 characters problems (Mark)
- Remove encoder .prepare/.commit helper functions, and move the vop mode
  configure function into encoder .enable helper functions. (Mark)

Changes in v3:
- Use encoder enable/disable function, and remove the encoder DPMS function
- Keep HDMI PLL power on in standby mode

Changes in v2:
- Using DRM atomic helper functions for connector init (Mark)
- Remove "hdmi->connector.encoder = encoder;" (Mark)

 drivers/gpu/drm/rockchip/Kconfig     |   8 +
 drivers/gpu/drm/rockchip/Makefile    |   1 +
 drivers/gpu/drm/rockchip/inno_hdmi.c | 941 +++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/inno_hdmi.h | 362 ++++++++++++++
 4 files changed, 1312 insertions(+)
 create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.c
 create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.h

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 35215f6..a5014e0 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -25,3 +25,11 @@ config ROCKCHIP_DW_HDMI
 	  for the Synopsys DesignWare HDMI driver. If you want to
 	  enable HDMI on RK3288 based SoC, you should selet this
 	  option.
+
+config ROCKCHIP_INNO_HDMI
+	tristate "Rockchip specific extensions for Innosilicon HDMI"
+        depends on DRM_ROCKCHIP
+        help
+	  This selects support for Rockchip SoC specific extensions
+	  for the Innosilicon HDMI driver. If you want to enable
+	  HDMI on RK3036 based SoC, you should selet this option.
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 9e6e992..96b9db6 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -6,5 +6,6 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
 		rockchip_drm_gem.o rockchip_drm_vop.o
 
 obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
+obj-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
 
 obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_vop_reg.o
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
new file mode 100644
index 0000000..4da3020
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -0,0 +1,941 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ *    Zheng Yang <zhengyang at rock-chips.com>
+ *    Yakir Yang <ykk at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/hdmi.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+
+#include <drm/drm_of.h>
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_encoder_slave.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_vop.h"
+
+#include "inno_hdmi.h"
+
+#define to_inno_hdmi(x)	container_of(x, struct inno_hdmi, x)
+
+struct hdmi_data_info {
+	int vic;
+	bool sink_is_hdmi;
+	bool sink_has_audio;
+	unsigned int enc_in_format;
+	unsigned int enc_out_format;
+	unsigned int colorimetry;
+};
+
+struct inno_hdmi_i2c {
+	struct i2c_adapter adap;
+
+	u8 ddc_addr;
+	u8 segment_addr;
+
+	struct mutex lock;
+	struct completion cmp;
+};
+
+struct inno_hdmi {
+	struct device *dev;
+	struct drm_device *drm_dev;
+
+	int irq;
+	struct clk *pclk;
+	void __iomem *regs;
+
+	struct drm_connector	connector;
+	struct drm_encoder	encoder;
+
+	struct inno_hdmi_i2c *i2c;
+	struct i2c_adapter *ddc;
+
+	unsigned int tmds_rate;
+
+	struct hdmi_data_info	hdmi_data;
+	struct drm_display_mode previous_mode;
+};
+
+enum {
+	CSC_ITU601_16_235_TO_RGB_0_255_8BIT,
+	CSC_ITU601_0_255_TO_RGB_0_255_8BIT,
+	CSC_ITU709_16_235_TO_RGB_0_255_8BIT,
+	CSC_RGB_0_255_TO_ITU601_16_235_8BIT,
+	CSC_RGB_0_255_TO_ITU709_16_235_8BIT,
+	CSC_RGB_0_255_TO_RGB_16_235_8BIT,
+};
+
+static const char coeff_csc[][24] = {
+	/*
+	 * YUV2RGB:601 SD mode(Y[16:235], UV[16:240], RGB[0:255]):
+	 *   R = 1.164*Y + 1.596*V - 204
+	 *   G = 1.164*Y - 0.391*U - 0.813*V + 154
+	 *   B = 1.164*Y + 2.018*U - 258
+	 */
+	{
+		0x04, 0xa7, 0x00, 0x00, 0x06, 0x62, 0x02, 0xcc,
+		0x04, 0xa7, 0x11, 0x90, 0x13, 0x40, 0x00, 0x9a,
+		0x04, 0xa7, 0x08, 0x12, 0x00, 0x00, 0x03, 0x02
+	},
+	/*
+	 * YUV2RGB:601 SD mode(YUV[0:255],RGB[0:255]):
+	 *   R = Y + 1.402*V - 248
+	 *   G = Y - 0.344*U - 0.714*V + 135
+	 *   B = Y + 1.772*U - 227
+	 */
+	{
+		0x04, 0x00, 0x00, 0x00, 0x05, 0x9b, 0x02, 0xf8,
+		0x04, 0x00, 0x11, 0x60, 0x12, 0xdb, 0x00, 0x87,
+		0x04, 0x00, 0x07, 0x16, 0x00, 0x00, 0x02, 0xe3
+	},
+	/*
+	 * YUV2RGB:709 HD mode(Y[16:235],UV[16:240],RGB[0:255]):
+	 *   R = 1.164*Y + 1.793*V - 248
+	 *   G = 1.164*Y - 0.213*U - 0.534*V + 77
+	 *   B = 1.164*Y + 2.115*U - 289
+	 */
+	{
+		0x04, 0xa7, 0x00, 0x00, 0x07, 0x2c, 0x02, 0xf8,
+		0x04, 0xa7, 0x10, 0xda, 0x12, 0x22, 0x00, 0x4d,
+		0x04, 0xa7, 0x08, 0x74, 0x00, 0x00, 0x03, 0x21
+	},
+
+	/*
+	 * RGB2YUV:601 SD mode:
+	 *   Cb = -0.291G - 0.148R + 0.439B + 128
+	 *   Y  = 0.504G  + 0.257R + 0.098B + 16
+	 *   Cr = -0.368G + 0.439R - 0.071B + 128
+	 */
+	{
+		0x11, 0x5f, 0x01, 0x82, 0x10, 0x23, 0x00, 0x80,
+		0x02, 0x1c, 0x00, 0xa1, 0x00, 0x36, 0x00, 0x1e,
+		0x11, 0x29, 0x10, 0x59, 0x01, 0x82, 0x00, 0x80
+	},
+	/*
+	 * RGB2YUV:709 HD mode:
+	 *   Cb = - 0.338G - 0.101R + 0.439B + 128
+	 *   Y  = 0.614G   + 0.183R + 0.062B + 16
+	 *   Cr = - 0.399G + 0.439R - 0.040B + 128
+	 */
+	{
+		0x11, 0x98, 0x01, 0xc1, 0x10, 0x28, 0x00, 0x80,
+		0x02, 0x74, 0x00, 0xbb, 0x00, 0x3f, 0x00, 0x10,
+		0x11, 0x5a, 0x10, 0x67, 0x01, 0xc1, 0x00, 0x80
+	},
+	/*
+	 * RGB[0:255]2RGB[16:235]:
+	 *   R' = R x (235-16)/255 + 16;
+	 *   G' = G x (235-16)/255 + 16;
+	 *   B' = B x (235-16)/255 + 16;
+	 */
+	{
+		0x00, 0x00, 0x03, 0x6F, 0x00, 0x00, 0x00, 0x10,
+		0x03, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+		0x00, 0x00, 0x00, 0x00, 0x03, 0x6F, 0x00, 0x10
+	},
+};
+
+static inline u8 hdmi_readb(struct inno_hdmi *hdmi, u16 offset)
+{
+	return readl_relaxed(hdmi->regs + (offset) * 0x04);
+}
+
+static inline void hdmi_writeb(struct inno_hdmi *hdmi, u16 offset, u32 val)
+{
+	writel_relaxed(val, hdmi->regs + (offset) * 0x04);
+}
+
+static inline void hdmi_modb(struct inno_hdmi *hdmi, u16 offset,
+			     u32 msk, u32 val)
+{
+	u8 temp = hdmi_readb(hdmi, offset) & ~msk;
+
+	temp |= val & msk;
+	hdmi_writeb(hdmi, offset, temp);
+}
+
+static void inno_hdmi_i2c_init(struct inno_hdmi *hdmi)
+{
+	int ddc_bus_freq;
+
+	ddc_bus_freq = (hdmi->tmds_rate >> 2) / HDMI_SCL_RATE;
+
+	hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
+	hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
+
+	/* Clear the EDID interrupt flag and mute the interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+}
+
+static void inno_hdmi_sys_power(struct inno_hdmi *hdmi, bool enable)
+{
+	if (enable)
+		hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON);
+	else
+		hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
+}
+
+static void inno_hdmi_set_pwr_mode(struct inno_hdmi *hdmi, int mode)
+{
+	switch (mode) {
+	case NORMAL:
+		inno_hdmi_sys_power(hdmi, false);
+
+		hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x6f);
+		hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0xbb);
+
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x14);
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x10);
+		hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x0f);
+		hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x01);
+
+		inno_hdmi_sys_power(hdmi, true);
+		break;
+
+	case LOWER_PWR:
+		inno_hdmi_sys_power(hdmi, false);
+		hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
+
+		break;
+
+	default:
+		dev_err(hdmi->dev, "Unknown power mode %d\n", mode);
+	}
+}
+
+static void inno_hdmi_reset(struct inno_hdmi *hdmi)
+{
+	u32 val;
+	u32 msk;
+
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_DIGITAL, v_NOT_RST_DIGITAL);
+	udelay(100);
+
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_ANALOG, v_NOT_RST_ANALOG);
+	udelay(100);
+
+	msk = m_REG_CLK_INV | m_REG_CLK_SOURCE | m_POWER | m_INT_POL;
+	val = v_REG_CLK_INV | v_REG_CLK_SOURCE_SYS | v_PWR_ON | v_INT_POL_HIGH;
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, msk, val);
+
+	inno_hdmi_set_pwr_mode(hdmi, NORMAL);
+}
+
+static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi, int setup_rc,
+				  union hdmi_infoframe *frame, u32 frame_index,
+				  u32 mask, u32 disable, u32 enable)
+{
+	if (mask)
+		hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, disable);
+
+	hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, frame_index);
+
+	if (setup_rc >= 0) {
+		u8 packed_frame[HDMI_MAXIMUM_INFO_FRAME_SIZE];
+		ssize_t rc, i;
+
+		rc = hdmi_infoframe_pack(frame, packed_frame,
+					 sizeof(packed_frame));
+		if (rc < 0)
+			return rc;
+
+		for (i = 0; i < rc; i++)
+			hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i,
+				    packed_frame[i]);
+
+		if (mask)
+			hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, enable);
+	}
+
+	return setup_rc;
+}
+
+static int inno_hdmi_config_video_vsi(struct inno_hdmi *hdmi,
+				      struct drm_display_mode *mode)
+{
+	union hdmi_infoframe frame;
+	int rc;
+
+	rc = drm_hdmi_vendor_infoframe_from_display_mode(&frame.vendor.hdmi,
+							 mode);
+
+	return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_VSI,
+		m_PACKET_VSI_EN, v_PACKET_VSI_EN(0), v_PACKET_VSI_EN(1));
+}
+
+static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
+				      struct drm_display_mode *mode)
+{
+	union hdmi_infoframe frame;
+	int rc;
+
+	rc = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, mode);
+
+	if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_RGB)
+		frame.avi.colorspace = HDMI_COLORSPACE_RGB;
+	else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV444)
+		frame.avi.colorspace = HDMI_COLORSPACE_YUV444;
+	else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV422)
+		frame.avi.colorspace = HDMI_COLORSPACE_YUV422;
+	else
+		frame.avi.colorspace = HDMI_COLORSPACE_RGB;
+
+	return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_AVI, 0, 0, 0);
+}
+
+static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
+{
+	struct hdmi_data_info *data = &hdmi->hdmi_data;
+	int c0_c2_change = 0;
+	int csc_enable = 0;
+	int csc_mode = 0;
+	int auto_csc = 0;
+	int value;
+	int i;
+
+	/* Input video mode is SDR RGB24bit, data enable signal from external */
+	hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL1, v_DE_EXTERNAL |
+		    v_VIDEO_INPUT_FORMAT(VIDEO_INPUT_SDR_RGB444));
+
+	/* Input color hardcode to RGB, and output color hardcode to RGB888 */
+	value = v_VIDEO_INPUT_BITS(VIDEO_INPUT_8BITS) |
+		v_VIDEO_OUTPUT_COLOR(0) |
+		v_VIDEO_INPUT_CSP(0);
+	hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL2, value);
+
+	if (data->enc_out_format == data->enc_out_format) {
+		if ((data->enc_in_format == HDMI_COLORSPACE_RGB) ||
+		    (data->enc_in_format >= HDMI_COLORSPACE_YUV444)) {
+			value = v_SOF_DISABLE | v_COLOR_DEPTH_NOT_INDICATED(1);
+			hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
+
+			hdmi_modb(hdmi, HDMI_VIDEO_CONTRL,
+				  m_VIDEO_AUTO_CSC | m_VIDEO_C0_C2_SWAP,
+				  v_VIDEO_AUTO_CSC(AUTO_CSC_DISABLE) |
+				  v_VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE));
+			return 0;
+		}
+	}
+
+	if (data->colorimetry == HDMI_COLORIMETRY_ITU_601) {
+		if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
+		    (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
+			csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT;
+			auto_csc = AUTO_CSC_DISABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = v_CSC_ENABLE;
+		} else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
+			   (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
+			csc_mode = CSC_ITU601_16_235_TO_RGB_0_255_8BIT;
+			auto_csc = AUTO_CSC_ENABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = v_CSC_DISABLE;
+		}
+	} else {
+		if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
+		    (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
+			csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT;
+			auto_csc = AUTO_CSC_DISABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = v_CSC_ENABLE;
+		} else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
+			   (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
+			csc_mode = CSC_ITU709_16_235_TO_RGB_0_255_8BIT;
+			auto_csc = AUTO_CSC_ENABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = v_CSC_DISABLE;
+		}
+	}
+
+	for (i = 0; i < 24; i++)
+		hdmi_writeb(hdmi, HDMI_VIDEO_CSC_COEF + i,
+			    coeff_csc[csc_mode][i]);
+
+	value = v_SOF_DISABLE | csc_enable | v_COLOR_DEPTH_NOT_INDICATED(1);
+	hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
+	hdmi_modb(hdmi, HDMI_VIDEO_CONTRL, m_VIDEO_AUTO_CSC |
+		  m_VIDEO_C0_C2_SWAP, v_VIDEO_AUTO_CSC(auto_csc) |
+		  v_VIDEO_C0_C2_SWAP(c0_c2_change));
+
+	return 0;
+}
+
+static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi,
+					 struct drm_display_mode *mode)
+{
+	int value;
+
+	/* Set detail external video timing polarity and interlace mode */
+	value = v_EXTERANL_VIDEO(1);
+	value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
+		 v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
+	value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
+		 v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
+	value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
+		 v_INETLACE(1) : v_INETLACE(0);
+	hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
+
+	/* Set detail external video timing */
+	value = mode->htotal;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
+
+	value = mode->htotal - mode->hdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
+
+	value = mode->hsync_start - mode->hdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
+
+	value = mode->hsync_end - mode->hsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
+
+	value = mode->vtotal;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
+
+	value = mode->vtotal - mode->vdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
+
+	value = mode->vsync_start - mode->vdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
+
+	value = mode->vsync_end - mode->vsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
+
+	hdmi_writeb(hdmi, HDMI_PHY_PRE_DIV_RATIO, 0x1e);
+	hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_LOW, 0x2c);
+	hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH, 0x01);
+
+	return 0;
+}
+
+static int inno_hdmi_setup(struct inno_hdmi *hdmi,
+			   struct drm_display_mode *mode)
+{
+	hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
+
+	hdmi->hdmi_data.enc_in_format = HDMI_COLORSPACE_RGB;
+	hdmi->hdmi_data.enc_out_format = HDMI_COLORSPACE_RGB;
+
+	if ((hdmi->hdmi_data.vic == 6) || (hdmi->hdmi_data.vic == 7) ||
+	    (hdmi->hdmi_data.vic == 21) || (hdmi->hdmi_data.vic == 22) ||
+	    (hdmi->hdmi_data.vic == 2) || (hdmi->hdmi_data.vic == 3) ||
+	    (hdmi->hdmi_data.vic == 17) || (hdmi->hdmi_data.vic == 18))
+		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601;
+	else
+		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
+
+	/* Mute video and audio output */
+	hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
+		  v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1));
+
+	/* Set HDMI Mode */
+	hdmi_writeb(hdmi, HDMI_HDCP_CTRL,
+		    v_HDMI_DVI(hdmi->hdmi_data.sink_is_hdmi));
+
+	inno_hdmi_config_video_timing(hdmi, mode);
+
+	inno_hdmi_config_video_csc(hdmi);
+
+	if (hdmi->hdmi_data.sink_is_hdmi) {
+		inno_hdmi_config_video_avi(hdmi, mode);
+		inno_hdmi_config_video_vsi(hdmi, mode);
+	}
+
+	/*
+	 * When IP controller have configured to an accurate video
+	 * timing, then the TMDS clock source would be switched to
+	 * DCLK_LCDC, so we need to init the TMDS rate to mode pixel
+	 * clock rate, and reconfigure the DDC clock.
+	 */
+	hdmi->tmds_rate = mode->clock * 1000;
+	inno_hdmi_i2c_init(hdmi);
+
+	/* Unmute video and audio output */
+	hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
+		  v_AUDIO_MUTE(0) | v_VIDEO_MUTE(0));
+
+	return 0;
+}
+
+static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder,
+				       struct drm_display_mode *mode,
+				       struct drm_display_mode *adj_mode)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+
+	inno_hdmi_setup(hdmi, adj_mode);
+
+	/* Store the display mode for plugin/DPMS poweron events */
+	memcpy(&hdmi->previous_mode, adj_mode, sizeof(hdmi->previous_mode));
+}
+
+static void inno_hdmi_encoder_enable(struct drm_encoder *encoder)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+
+	rockchip_drm_crtc_mode_config(encoder->crtc, DRM_MODE_CONNECTOR_HDMIA,
+				      ROCKCHIP_OUT_MODE_P888);
+
+	inno_hdmi_set_pwr_mode(hdmi, NORMAL);
+}
+
+static void inno_hdmi_encoder_disable(struct drm_encoder *encoder)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+
+	inno_hdmi_set_pwr_mode(hdmi, LOWER_PWR);
+}
+
+static bool inno_hdmi_encoder_mode_fixup(struct drm_encoder *encoder,
+					 const struct drm_display_mode *mode,
+					 struct drm_display_mode *adj_mode)
+{
+	return true;
+}
+
+static struct drm_encoder_helper_funcs inno_hdmi_encoder_helper_funcs = {
+	.enable     = inno_hdmi_encoder_enable,
+	.disable    = inno_hdmi_encoder_disable,
+	.mode_fixup = inno_hdmi_encoder_mode_fixup,
+	.mode_set   = inno_hdmi_encoder_mode_set,
+};
+
+static struct drm_encoder_funcs inno_hdmi_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static enum drm_connector_status
+inno_hdmi_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+
+	return (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
+		connector_status_connected : connector_status_disconnected;
+}
+
+static int inno_hdmi_connector_get_modes(struct drm_connector *connector)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+	struct edid *edid;
+	int ret = 0;
+
+	if (!hdmi->ddc)
+		return 0;
+
+	edid = drm_get_edid(connector, hdmi->ddc);
+	if (edid) {
+		hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
+		hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
+		drm_mode_connector_update_edid_property(connector, edid);
+		ret = drm_add_edid_modes(connector, edid);
+		kfree(edid);
+	}
+
+	return ret;
+}
+
+static enum drm_mode_status
+inno_hdmi_connector_mode_valid(struct drm_connector *connector,
+			       struct drm_display_mode *mode)
+{
+	return MODE_OK;
+}
+
+static struct drm_encoder *
+inno_hdmi_connector_best_encoder(struct drm_connector *connector)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+
+	return &hdmi->encoder;
+}
+
+static int
+inno_hdmi_probe_single_connector_modes(struct drm_connector *connector,
+				       uint32_t maxX, uint32_t maxY)
+{
+	return drm_helper_probe_single_connector_modes(connector, 1920, 1080);
+}
+
+static void inno_hdmi_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs inno_hdmi_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.fill_modes = inno_hdmi_probe_single_connector_modes,
+	.detect = inno_hdmi_connector_detect,
+	.destroy = inno_hdmi_connector_destroy,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static struct drm_connector_helper_funcs inno_hdmi_connector_helper_funcs = {
+	.get_modes = inno_hdmi_connector_get_modes,
+	.mode_valid = inno_hdmi_connector_mode_valid,
+	.best_encoder = inno_hdmi_connector_best_encoder,
+};
+
+static int inno_hdmi_register(struct drm_device *drm, struct inno_hdmi *hdmi)
+{
+	struct drm_encoder *encoder = &hdmi->encoder;
+	struct device *dev = hdmi->dev;
+
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
+
+	/*
+	 * If we failed to find the CRTC(s) which this encoder is
+	 * supposed to be connected to, it's because the CRTC has
+	 * not been registered yet.  Defer probing, and hope that
+	 * the required CRTC is added later.
+	 */
+	if (encoder->possible_crtcs == 0)
+		return -EPROBE_DEFER;
+
+	drm_encoder_helper_add(encoder, &inno_hdmi_encoder_helper_funcs);
+	drm_encoder_init(drm, encoder, &inno_hdmi_encoder_funcs,
+			 DRM_MODE_ENCODER_TMDS, NULL);
+
+	hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
+
+	drm_connector_helper_add(&hdmi->connector,
+				 &inno_hdmi_connector_helper_funcs);
+	drm_connector_init(drm, &hdmi->connector, &inno_hdmi_connector_funcs,
+			   DRM_MODE_CONNECTOR_HDMIA);
+
+	drm_mode_connector_attach_encoder(&hdmi->connector, encoder);
+
+	return 0;
+}
+
+static irqreturn_t inno_hdmi_i2c_irq(struct inno_hdmi *hdmi)
+{
+	struct inno_hdmi_i2c *i2c = hdmi->i2c;
+	u8 stat;
+
+	stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
+	if (!(stat & m_INT_EDID_READY))
+		return IRQ_NONE;
+
+	/* Clear HDMI EDID interrupt flag */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+
+	complete(&i2c->cmp);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t inno_hdmi_hardirq(int irq, void *dev_id)
+{
+	struct inno_hdmi *hdmi = dev_id;
+	irqreturn_t ret = IRQ_NONE;
+	u8 interrupt;
+
+	if (hdmi->i2c)
+		ret = inno_hdmi_i2c_irq(hdmi);
+
+	interrupt = hdmi_readb(hdmi, HDMI_STATUS);
+	if (interrupt & m_INT_HOTPLUG) {
+		hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG);
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	return ret;
+}
+
+static irqreturn_t inno_hdmi_irq(int irq, void *dev_id)
+{
+	struct inno_hdmi *hdmi = dev_id;
+
+	drm_helper_hpd_irq_event(hdmi->connector.dev);
+
+	return IRQ_HANDLED;
+}
+
+static int inno_hdmi_i2c_read(struct inno_hdmi *hdmi, struct i2c_msg *msgs)
+{
+	int length = msgs->len;
+	u8 *buf = msgs->buf;
+	int ret;
+
+	ret = wait_for_completion_timeout(&hdmi->i2c->cmp, HZ / 10);
+	if (!ret)
+		return -EAGAIN;
+
+	while (length--)
+		*buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
+
+	return 0;
+}
+
+static int inno_hdmi_i2c_write(struct inno_hdmi *hdmi, struct i2c_msg *msgs)
+{
+	/*
+	 * The DDC module only support read EDID message, so
+	 * we assume that each word write to this i2c adapter
+	 * should be the offset of EDID word address.
+	 */
+	if ((msgs->len != 1) ||
+	    ((msgs->addr != DDC_ADDR) && (msgs->addr != DDC_SEGMENT_ADDR)))
+		return -EINVAL;
+
+	reinit_completion(&hdmi->i2c->cmp);
+
+	if (msgs->addr == DDC_SEGMENT_ADDR)
+		hdmi->i2c->segment_addr = msgs->buf[0];
+	if (msgs->addr == DDC_ADDR)
+		hdmi->i2c->ddc_addr = msgs->buf[0];
+
+	/* Set edid fifo first addr */
+	hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
+
+	/* Set edid word address 0x00/0x80 */
+	hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
+
+	/* Set edid segment pointer */
+	hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr);
+
+	return 0;
+}
+
+static int inno_hdmi_i2c_xfer(struct i2c_adapter *adap,
+			      struct i2c_msg *msgs, int num)
+{
+	struct inno_hdmi *hdmi = i2c_get_adapdata(adap);
+	struct inno_hdmi_i2c *i2c = hdmi->i2c;
+	int i, ret = 0;
+
+	mutex_lock(&i2c->lock);
+
+	/* Clear the EDID interrupt flag and unmute the interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY);
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+
+	for (i = 0; i < num; i++) {
+		dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n",
+			i + 1, num, msgs[i].len, msgs[i].flags);
+
+		if (msgs[i].flags & I2C_M_RD)
+			ret = inno_hdmi_i2c_read(hdmi, &msgs[i]);
+		else
+			ret = inno_hdmi_i2c_write(hdmi, &msgs[i]);
+
+		if (ret < 0)
+			break;
+	}
+
+	if (!ret)
+		ret = num;
+
+	/* Mute HDMI EDID interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
+
+	mutex_unlock(&i2c->lock);
+
+	return ret;
+}
+
+static u32 inno_hdmi_i2c_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm inno_hdmi_algorithm = {
+	.master_xfer	= inno_hdmi_i2c_xfer,
+	.functionality	= inno_hdmi_i2c_func,
+};
+
+static struct i2c_adapter *inno_hdmi_i2c_adapter(struct inno_hdmi *hdmi)
+{
+	struct i2c_adapter *adap;
+	struct inno_hdmi_i2c *i2c;
+	int ret;
+
+	i2c = devm_kzalloc(hdmi->dev, sizeof(*i2c), GFP_KERNEL);
+	if (!i2c)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_init(&i2c->lock);
+	init_completion(&i2c->cmp);
+
+	adap = &i2c->adap;
+	adap->class = I2C_CLASS_DDC;
+	adap->owner = THIS_MODULE;
+	adap->dev.parent = hdmi->dev;
+	adap->dev.of_node = hdmi->dev->of_node;
+	adap->algo = &inno_hdmi_algorithm;
+	strlcpy(adap->name, "Inno HDMI", sizeof(adap->name));
+	i2c_set_adapdata(adap, hdmi);
+
+	ret = i2c_add_adapter(adap);
+	if (ret) {
+		dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name);
+		devm_kfree(hdmi->dev, i2c);
+		return ERR_PTR(ret);
+	}
+
+	hdmi->i2c = i2c;
+
+	dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name);
+
+	return adap;
+}
+
+static int inno_hdmi_bind(struct device *dev, struct device *master,
+				 void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = data;
+	struct inno_hdmi *hdmi;
+	struct resource *iores;
+	int irq;
+	int ret;
+
+	hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
+	if (!hdmi)
+		return -ENOMEM;
+
+	hdmi->dev = dev;
+	hdmi->drm_dev = drm;
+
+	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!iores)
+		return -ENXIO;
+
+	hdmi->regs = devm_ioremap_resource(dev, iores);
+	if (IS_ERR(hdmi->regs))
+		return PTR_ERR(hdmi->regs);
+
+	hdmi->pclk = devm_clk_get(hdmi->dev, "pclk");
+	if (IS_ERR(hdmi->pclk)) {
+		dev_err(hdmi->dev, "Unable to get HDMI pclk clk\n");
+		return PTR_ERR(hdmi->pclk);
+	}
+
+	ret = clk_prepare_enable(hdmi->pclk);
+	if (ret) {
+		dev_err(hdmi->dev, "Cannot enable HDMI pclk clock: %d\n", ret);
+		return ret;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	inno_hdmi_reset(hdmi);
+
+	hdmi->ddc = inno_hdmi_i2c_adapter(hdmi);
+	if (IS_ERR(hdmi->ddc)) {
+		hdmi->ddc = NULL;
+		return PTR_ERR(hdmi->ddc);
+	}
+
+	/*
+	 * When IP controller haven't configured to an accurate video
+	 * timing, then the TMDS clock source would be switched to
+	 * PCLK_HDMI, so we need to init the TMDS rate to PCLK rate,
+	 * and reconfigure the DDC clock.
+	 */
+	hdmi->tmds_rate = clk_get_rate(hdmi->pclk);
+	inno_hdmi_i2c_init(hdmi);
+
+	ret = inno_hdmi_register(drm, hdmi);
+	if (ret)
+		return ret;
+
+	dev_set_drvdata(dev, hdmi);
+
+	/* Unmute hotplug interrupt */
+	hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
+
+	ret = devm_request_threaded_irq(dev, irq, inno_hdmi_hardirq,
+					inno_hdmi_irq, IRQF_SHARED,
+					dev_name(dev), hdmi);
+
+	return ret;
+}
+
+static void inno_hdmi_unbind(struct device *dev, struct device *master,
+			     void *data)
+{
+	struct inno_hdmi *hdmi = dev_get_drvdata(dev);
+
+	hdmi->connector.funcs->destroy(&hdmi->connector);
+	hdmi->encoder.funcs->destroy(&hdmi->encoder);
+
+	clk_disable_unprepare(hdmi->pclk);
+	i2c_put_adapter(hdmi->ddc);
+}
+
+static const struct component_ops inno_hdmi_ops = {
+	.bind	= inno_hdmi_bind,
+	.unbind	= inno_hdmi_unbind,
+};
+
+static int inno_hdmi_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &inno_hdmi_ops);
+}
+
+static int inno_hdmi_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &inno_hdmi_ops);
+
+	return 0;
+}
+
+static const struct of_device_id inno_hdmi_dt_ids[] = {
+	{ .compatible = "rockchip,rk3036-inno-hdmi",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, inno_hdmi_dt_ids);
+
+static struct platform_driver inno_hdmi_driver = {
+	.probe  = inno_hdmi_probe,
+	.remove = inno_hdmi_remove,
+	.driver = {
+		.name = "innohdmi-rockchip",
+		.of_match_table = inno_hdmi_dt_ids,
+	},
+};
+
+module_platform_driver(inno_hdmi_driver);
+
+MODULE_AUTHOR("Zheng Yang <zhengyang at rock-chips.com>");
+MODULE_AUTHOR("Yakir Yang <ykk at rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip Specific INNO-HDMI Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:innohdmi-rockchip");
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.h b/drivers/gpu/drm/rockchip/inno_hdmi.h
new file mode 100644
index 0000000..aa7c415
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.h
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ *    Zheng Yang <zhengyang at rock-chips.com>
+ *    Yakir Yang <ykk at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __INNO_HDMI_H__
+#define __INNO_HDMI_H__
+
+#define DDC_SEGMENT_ADDR		0x30
+
+enum PWR_MODE {
+	NORMAL,
+	LOWER_PWR,
+};
+
+#define HDMI_SCL_RATE			(100*1000)
+#define DDC_BUS_FREQ_L			0x4b
+#define DDC_BUS_FREQ_H			0x4c
+
+#define HDMI_SYS_CTRL			0x00
+#define m_RST_ANALOG			(1 << 6)
+#define v_RST_ANALOG			(0 << 6)
+#define v_NOT_RST_ANALOG		(1 << 6)
+#define m_RST_DIGITAL			(1 << 5)
+#define v_RST_DIGITAL			(0 << 5)
+#define v_NOT_RST_DIGITAL		(1 << 5)
+#define m_REG_CLK_INV			(1 << 4)
+#define v_REG_CLK_NOT_INV		(0 << 4)
+#define v_REG_CLK_INV			(1 << 4)
+#define m_VCLK_INV			(1 << 3)
+#define v_VCLK_NOT_INV			(0 << 3)
+#define v_VCLK_INV			(1 << 3)
+#define m_REG_CLK_SOURCE		(1 << 2)
+#define v_REG_CLK_SOURCE_TMDS		(0 << 2)
+#define v_REG_CLK_SOURCE_SYS		(1 << 2)
+#define m_POWER				(1 << 1)
+#define v_PWR_ON			(0 << 1)
+#define v_PWR_OFF			(1 << 1)
+#define m_INT_POL			(1 << 0)
+#define v_INT_POL_HIGH			1
+#define v_INT_POL_LOW			0
+
+#define HDMI_VIDEO_CONTRL1		0x01
+#define m_VIDEO_INPUT_FORMAT		(7 << 1)
+#define m_DE_SOURCE			(1 << 0)
+#define v_VIDEO_INPUT_FORMAT(n)		(n << 1)
+#define v_DE_EXTERNAL			1
+#define v_DE_INTERNAL			0
+enum {
+	VIDEO_INPUT_SDR_RGB444 = 0,
+	VIDEO_INPUT_DDR_RGB444 = 5,
+	VIDEO_INPUT_DDR_YCBCR422 = 6
+};
+
+#define HDMI_VIDEO_CONTRL2		0x02
+#define m_VIDEO_OUTPUT_COLOR		(3 << 6)
+#define m_VIDEO_INPUT_BITS		(3 << 4)
+#define m_VIDEO_INPUT_CSP		(1 << 0)
+#define v_VIDEO_OUTPUT_COLOR(n)		(((n) & 0x3) << 6)
+#define v_VIDEO_INPUT_BITS(n)		(n << 4)
+#define v_VIDEO_INPUT_CSP(n)		(n << 0)
+enum {
+	VIDEO_INPUT_12BITS = 0,
+	VIDEO_INPUT_10BITS = 1,
+	VIDEO_INPUT_REVERT = 2,
+	VIDEO_INPUT_8BITS = 3,
+};
+
+#define HDMI_VIDEO_CONTRL		0x03
+#define m_VIDEO_AUTO_CSC		(1 << 7)
+#define v_VIDEO_AUTO_CSC(n)		(n << 7)
+#define m_VIDEO_C0_C2_SWAP		(1 << 0)
+#define v_VIDEO_C0_C2_SWAP(n)		(n << 0)
+enum {
+	C0_C2_CHANGE_ENABLE = 0,
+	C0_C2_CHANGE_DISABLE = 1,
+	AUTO_CSC_DISABLE = 0,
+	AUTO_CSC_ENABLE = 1,
+};
+
+#define HDMI_VIDEO_CONTRL3		0x04
+#define m_COLOR_DEPTH_NOT_INDICATED	(1 << 4)
+#define m_SOF				(1 << 3)
+#define m_COLOR_RANGE			(1 << 2)
+#define m_CSC				(1 << 0)
+#define v_COLOR_DEPTH_NOT_INDICATED(n)	((n) << 4)
+#define v_SOF_ENABLE			(0 << 3)
+#define v_SOF_DISABLE			(1 << 3)
+#define v_COLOR_RANGE_FULL		(1 << 2)
+#define v_COLOR_RANGE_LIMITED		(0 << 2)
+#define v_CSC_ENABLE			1
+#define v_CSC_DISABLE			0
+
+#define HDMI_AV_MUTE			0x05
+#define m_AVMUTE_CLEAR			(1 << 7)
+#define m_AVMUTE_ENABLE			(1 << 6)
+#define m_AUDIO_MUTE			(1 << 1)
+#define m_VIDEO_BLACK			(1 << 0)
+#define v_AVMUTE_CLEAR(n)		(n << 7)
+#define v_AVMUTE_ENABLE(n)		(n << 6)
+#define v_AUDIO_MUTE(n)			(n << 1)
+#define v_VIDEO_MUTE(n)			(n << 0)
+
+#define HDMI_VIDEO_TIMING_CTL		0x08
+#define v_HSYNC_POLARITY(n)		(n << 3)
+#define v_VSYNC_POLARITY(n)		(n << 2)
+#define v_INETLACE(n)			(n << 1)
+#define v_EXTERANL_VIDEO(n)		(n << 0)
+
+#define HDMI_VIDEO_EXT_HTOTAL_L		0x09
+#define HDMI_VIDEO_EXT_HTOTAL_H		0x0a
+#define HDMI_VIDEO_EXT_HBLANK_L		0x0b
+#define HDMI_VIDEO_EXT_HBLANK_H		0x0c
+#define HDMI_VIDEO_EXT_HDELAY_L		0x0d
+#define HDMI_VIDEO_EXT_HDELAY_H		0x0e
+#define HDMI_VIDEO_EXT_HDURATION_L	0x0f
+#define HDMI_VIDEO_EXT_HDURATION_H	0x10
+#define HDMI_VIDEO_EXT_VTOTAL_L		0x11
+#define HDMI_VIDEO_EXT_VTOTAL_H		0x12
+#define HDMI_VIDEO_EXT_VBLANK		0x13
+#define HDMI_VIDEO_EXT_VDELAY		0x14
+#define HDMI_VIDEO_EXT_VDURATION	0x15
+
+#define HDMI_VIDEO_CSC_COEF		0x18
+
+#define HDMI_AUDIO_CTRL1		0x35
+enum {
+	CTS_SOURCE_INTERNAL = 0,
+	CTS_SOURCE_EXTERNAL = 1,
+};
+#define v_CTS_SOURCE(n)			(n << 7)
+
+enum {
+	DOWNSAMPLE_DISABLE = 0,
+	DOWNSAMPLE_1_2 = 1,
+	DOWNSAMPLE_1_4 = 2,
+};
+#define v_DOWN_SAMPLE(n)		(n << 5)
+
+enum {
+	AUDIO_SOURCE_IIS = 0,
+	AUDIO_SOURCE_SPDIF = 1,
+};
+#define v_AUDIO_SOURCE(n)		(n << 3)
+
+#define v_MCLK_ENABLE(n)		(n << 2)
+enum {
+	MCLK_128FS = 0,
+	MCLK_256FS = 1,
+	MCLK_384FS = 2,
+	MCLK_512FS = 3,
+};
+#define v_MCLK_RATIO(n)			(n)
+
+#define AUDIO_SAMPLE_RATE		0x37
+enum {
+	AUDIO_32K = 0x3,
+	AUDIO_441K = 0x0,
+	AUDIO_48K = 0x2,
+	AUDIO_882K = 0x8,
+	AUDIO_96K = 0xa,
+	AUDIO_1764K = 0xc,
+	AUDIO_192K = 0xe,
+};
+
+#define AUDIO_I2S_MODE			0x38
+enum {
+	I2S_CHANNEL_1_2 = 1,
+	I2S_CHANNEL_3_4 = 3,
+	I2S_CHANNEL_5_6 = 7,
+	I2S_CHANNEL_7_8 = 0xf
+};
+#define v_I2S_CHANNEL(n)		((n) << 2)
+enum {
+	I2S_STANDARD = 0,
+	I2S_LEFT_JUSTIFIED = 1,
+	I2S_RIGHT_JUSTIFIED = 2,
+};
+#define v_I2S_MODE(n)			(n)
+
+#define AUDIO_I2S_MAP			0x39
+#define AUDIO_I2S_SWAPS_SPDIF		0x3a
+#define v_SPIDF_FREQ(n)			(n)
+
+#define N_32K				0x1000
+#define N_441K				0x1880
+#define N_882K				0x3100
+#define N_1764K				0x6200
+#define N_48K				0x1800
+#define N_96K				0x3000
+#define N_192K				0x6000
+
+#define HDMI_AUDIO_CHANNEL_STATUS	0x3e
+#define m_AUDIO_STATUS_NLPCM		(1 << 7)
+#define m_AUDIO_STATUS_USE		(1 << 6)
+#define m_AUDIO_STATUS_COPYRIGHT	(1 << 5)
+#define m_AUDIO_STATUS_ADDITION		(3 << 2)
+#define m_AUDIO_STATUS_CLK_ACCURACY	(2 << 0)
+#define v_AUDIO_STATUS_NLPCM(n)		((n & 1) << 7)
+#define AUDIO_N_H			0x3f
+#define AUDIO_N_M			0x40
+#define AUDIO_N_L			0x41
+
+#define HDMI_AUDIO_CTS_H		0x45
+#define HDMI_AUDIO_CTS_M		0x46
+#define HDMI_AUDIO_CTS_L		0x47
+
+#define HDMI_DDC_CLK_L			0x4b
+#define HDMI_DDC_CLK_H			0x4c
+
+#define HDMI_EDID_SEGMENT_POINTER	0x4d
+#define HDMI_EDID_WORD_ADDR		0x4e
+#define HDMI_EDID_FIFO_OFFSET		0x4f
+#define HDMI_EDID_FIFO_ADDR		0x50
+
+#define HDMI_PACKET_SEND_MANUAL		0x9c
+#define HDMI_PACKET_SEND_AUTO		0x9d
+#define m_PACKET_GCP_EN			(1 << 7)
+#define m_PACKET_MSI_EN			(1 << 6)
+#define m_PACKET_SDI_EN			(1 << 5)
+#define m_PACKET_VSI_EN			(1 << 4)
+#define v_PACKET_GCP_EN(n)		((n & 1) << 7)
+#define v_PACKET_MSI_EN(n)		((n & 1) << 6)
+#define v_PACKET_SDI_EN(n)		((n & 1) << 5)
+#define v_PACKET_VSI_EN(n)		((n & 1) << 4)
+
+#define HDMI_CONTROL_PACKET_BUF_INDEX	0x9f
+enum {
+	INFOFRAME_VSI = 0x05,
+	INFOFRAME_AVI = 0x06,
+	INFOFRAME_AAI = 0x08,
+};
+
+#define HDMI_CONTROL_PACKET_ADDR	0xa0
+#define HDMI_MAXIMUM_INFO_FRAME_SIZE	0x11
+enum {
+	AVI_COLOR_MODE_RGB = 0,
+	AVI_COLOR_MODE_YCBCR422 = 1,
+	AVI_COLOR_MODE_YCBCR444 = 2,
+	AVI_COLORIMETRY_NO_DATA = 0,
+
+	AVI_COLORIMETRY_SMPTE_170M = 1,
+	AVI_COLORIMETRY_ITU709 = 2,
+	AVI_COLORIMETRY_EXTENDED = 3,
+
+	AVI_CODED_FRAME_ASPECT_NO_DATA = 0,
+	AVI_CODED_FRAME_ASPECT_4_3 = 1,
+	AVI_CODED_FRAME_ASPECT_16_9 = 2,
+
+	ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME = 0x08,
+	ACTIVE_ASPECT_RATE_4_3 = 0x09,
+	ACTIVE_ASPECT_RATE_16_9 = 0x0A,
+	ACTIVE_ASPECT_RATE_14_9 = 0x0B,
+};
+
+#define HDMI_HDCP_CTRL			0x52
+#define m_HDMI_DVI			(1 << 1)
+#define v_HDMI_DVI(n)			(n << 1)
+
+#define HDMI_INTERRUPT_MASK1		0xc0
+#define HDMI_INTERRUPT_STATUS1		0xc1
+#define	m_INT_ACTIVE_VSYNC		(1 << 5)
+#define m_INT_EDID_READY		(1 << 2)
+
+#define HDMI_INTERRUPT_MASK2		0xc2
+#define HDMI_INTERRUPT_STATUS2		0xc3
+#define m_INT_HDCP_ERR			(1 << 7)
+#define m_INT_BKSV_FLAG			(1 << 6)
+#define m_INT_HDCP_OK			(1 << 4)
+
+#define HDMI_STATUS			0xc8
+#define m_HOTPLUG			(1 << 7)
+#define m_MASK_INT_HOTPLUG		(1 << 5)
+#define m_INT_HOTPLUG			(1 << 1)
+#define v_MASK_INT_HOTPLUG(n)		((n & 0x1) << 5)
+
+#define HDMI_COLORBAR                   0xc9
+
+#define HDMI_PHY_SYNC			0xce
+#define HDMI_PHY_SYS_CTL		0xe0
+#define m_TMDS_CLK_SOURCE		(1 << 5)
+#define v_TMDS_FROM_PLL			(0 << 5)
+#define v_TMDS_FROM_GEN			(1 << 5)
+#define m_PHASE_CLK			(1 << 4)
+#define v_DEFAULT_PHASE			(0 << 4)
+#define v_SYNC_PHASE			(1 << 4)
+#define m_TMDS_CURRENT_PWR		(1 << 3)
+#define v_TURN_ON_CURRENT		(0 << 3)
+#define v_CAT_OFF_CURRENT		(1 << 3)
+#define m_BANDGAP_PWR			(1 << 2)
+#define v_BANDGAP_PWR_UP		(0 << 2)
+#define v_BANDGAP_PWR_DOWN		(1 << 2)
+#define m_PLL_PWR			(1 << 1)
+#define v_PLL_PWR_UP			(0 << 1)
+#define v_PLL_PWR_DOWN			(1 << 1)
+#define m_TMDS_CHG_PWR			(1 << 0)
+#define v_TMDS_CHG_PWR_UP		(0 << 0)
+#define v_TMDS_CHG_PWR_DOWN		(1 << 0)
+
+#define HDMI_PHY_CHG_PWR		0xe1
+#define v_CLK_CHG_PWR(n)		((n & 1) << 3)
+#define v_DATA_CHG_PWR(n)		((n & 7) << 0)
+
+#define HDMI_PHY_DRIVER			0xe2
+#define v_CLK_MAIN_DRIVER(n)		(n << 4)
+#define v_DATA_MAIN_DRIVER(n)		(n << 0)
+
+#define HDMI_PHY_PRE_EMPHASIS		0xe3
+#define v_PRE_EMPHASIS(n)		((n & 7) << 4)
+#define v_CLK_PRE_DRIVER(n)		((n & 3) << 2)
+#define v_DATA_PRE_DRIVER(n)		((n & 3) << 0)
+
+#define HDMI_PHY_FEEDBACK_DIV_RATIO_LOW		0xe7
+#define v_FEEDBACK_DIV_LOW(n)			(n & 0xff)
+#define HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH	0xe8
+#define v_FEEDBACK_DIV_HIGH(n)			(n & 1)
+
+#define HDMI_PHY_PRE_DIV_RATIO		0xed
+#define v_PRE_DIV_RATIO(n)		(n & 0x1f)
+
+#define HDMI_CEC_CTRL			0xd0
+#define m_ADJUST_FOR_HISENSE		(1 << 6)
+#define m_REJECT_RX_BROADCAST		(1 << 5)
+#define m_BUSFREETIME_ENABLE		(1 << 2)
+#define m_REJECT_RX			(1 << 1)
+#define m_START_TX			(1 << 0)
+
+#define HDMI_CEC_DATA			0xd1
+#define HDMI_CEC_TX_OFFSET		0xd2
+#define HDMI_CEC_RX_OFFSET		0xd3
+#define HDMI_CEC_CLK_H			0xd4
+#define HDMI_CEC_CLK_L			0xd5
+#define HDMI_CEC_TX_LENGTH		0xd6
+#define HDMI_CEC_RX_LENGTH		0xd7
+#define HDMI_CEC_TX_INT_MASK		0xd8
+#define m_TX_DONE			(1 << 3)
+#define m_TX_NOACK			(1 << 2)
+#define m_TX_BROADCAST_REJ		(1 << 1)
+#define m_TX_BUSNOTFREE			(1 << 0)
+
+#define HDMI_CEC_RX_INT_MASK		0xd9
+#define m_RX_LA_ERR			(1 << 4)
+#define m_RX_GLITCH			(1 << 3)
+#define m_RX_DONE			(1 << 0)
+
+#define HDMI_CEC_TX_INT			0xda
+#define HDMI_CEC_RX_INT			0xdb
+#define HDMI_CEC_BUSFREETIME_L		0xdc
+#define HDMI_CEC_BUSFREETIME_H		0xdd
+#define HDMI_CEC_LOGICADDR		0xde
+
+#endif /* __INNO_HDMI_H__ */
-- 
1.9.1




From zhengsq at rock-chips.com  Mon Jan 18 23:03:00 2016
From: zhengsq at rock-chips.com (Shunqian Zheng)
Date: Tue, 19 Jan 2016 15:03:00 +0800
Subject: [PATCH v1] iommu/rockchip: reconstruct to support multi slaves
Message-ID: <1453186980-21055-1-git-send-email-zhengsq@rock-chips.com>

From: ZhengShunQian <zhengsq at rock-chips.com>

There are some IPs, such as video encoder/decoder, contains 2 slave iommus,
one for reading and the other for writing. They share the same irq and
clock with master.

This patch reconstructs to support this case by making them share the same
Page Directory, Page Tables and even the register operations.
That means every instruction to the reading MMU registers would be
duplicated to the writing MMU and vice versa.

Signed-off-by: ZhengShunQian <zhengsq at rock-chips.com>
---
 drivers/iommu/rockchip-iommu.c | 214 ++++++++++++++++++++++++++---------------
 1 file changed, 135 insertions(+), 79 deletions(-)

diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
index ebf0adb..a6f593a 100644
--- a/drivers/iommu/rockchip-iommu.c
+++ b/drivers/iommu/rockchip-iommu.c
@@ -86,7 +86,8 @@ struct rk_iommu_domain {
 
 struct rk_iommu {
 	struct device *dev;
-	void __iomem *base;
+	void __iomem **bases;
+	int num_mmu;
 	int irq;
 	struct list_head node; /* entry in rk_iommu_domain.iommus */
 	struct iommu_domain *domain; /* domain to which iommu is attached */
@@ -271,47 +272,70 @@ static u32 rk_iova_page_offset(dma_addr_t iova)
 	return (u32)(iova & RK_IOVA_PAGE_MASK) >> RK_IOVA_PAGE_SHIFT;
 }
 
-static u32 rk_iommu_read(struct rk_iommu *iommu, u32 offset)
+static u32 rk_iommu_read(void __iomem *base, u32 offset)
 {
-	return readl(iommu->base + offset);
+	return readl(base + offset);
 }
 
-static void rk_iommu_write(struct rk_iommu *iommu, u32 offset, u32 value)
+static void rk_iommu_write(void __iomem *base, u32 offset, u32 value)
 {
-	writel(value, iommu->base + offset);
+	writel(value, base + offset);
 }
 
 static void rk_iommu_command(struct rk_iommu *iommu, u32 command)
 {
-	writel(command, iommu->base + RK_MMU_COMMAND);
+	int i;
+
+	for (i = 0; i < iommu->num_mmu; i++)
+		writel(command, iommu->bases[i] + RK_MMU_COMMAND);
 }
 
+static void rk_iommu_base_command(void __iomem *base, u32 command)
+{
+	writel(command, base + RK_MMU_COMMAND);
+}
 static void rk_iommu_zap_lines(struct rk_iommu *iommu, dma_addr_t iova,
 			       size_t size)
 {
+	int i;
+
 	dma_addr_t iova_end = iova + size;
 	/*
 	 * TODO(djkurtz): Figure out when it is more efficient to shootdown the
 	 * entire iotlb rather than iterate over individual iovas.
 	 */
-	for (; iova < iova_end; iova += SPAGE_SIZE)
-		rk_iommu_write(iommu, RK_MMU_ZAP_ONE_LINE, iova);
+	for (i = 0; i < iommu->num_mmu; i++)
+		for (; iova < iova_end; iova += SPAGE_SIZE)
+			rk_iommu_write(iommu->bases[i], RK_MMU_ZAP_ONE_LINE, iova);
 }
 
 static bool rk_iommu_is_stall_active(struct rk_iommu *iommu)
 {
-	return rk_iommu_read(iommu, RK_MMU_STATUS) & RK_MMU_STATUS_STALL_ACTIVE;
+	bool active = true;
+	int i;
+
+	for (i = 0; i < iommu->num_mmu; i++)
+		active &= rk_iommu_read(iommu->bases[i], RK_MMU_STATUS) &
+					RK_MMU_STATUS_STALL_ACTIVE;
+
+	return active;
 }
 
 static bool rk_iommu_is_paging_enabled(struct rk_iommu *iommu)
 {
-	return rk_iommu_read(iommu, RK_MMU_STATUS) &
-			     RK_MMU_STATUS_PAGING_ENABLED;
+	bool enable = true;
+	int i;
+
+	for (i = 0; i < iommu->num_mmu; i++)
+		enable &= rk_iommu_read(iommu->bases[i], RK_MMU_STATUS) &
+					RK_MMU_STATUS_PAGING_ENABLED;
+
+	return enable;
 }
 
 static int rk_iommu_enable_stall(struct rk_iommu *iommu)
 {
-	int ret;
+	int ret, i;
 
 	if (rk_iommu_is_stall_active(iommu))
 		return 0;
@@ -324,15 +348,16 @@ static int rk_iommu_enable_stall(struct rk_iommu *iommu)
 
 	ret = rk_wait_for(rk_iommu_is_stall_active(iommu), 1);
 	if (ret)
-		dev_err(iommu->dev, "Enable stall request timed out, status: %#08x\n",
-			rk_iommu_read(iommu, RK_MMU_STATUS));
+		for (i = 0; i < iommu->num_mmu; i++)
+			dev_err(iommu->dev, "Enable stall request timed out, status: %#08x\n",
+				rk_iommu_read(iommu->bases[i], RK_MMU_STATUS));
 
 	return ret;
 }
 
 static int rk_iommu_disable_stall(struct rk_iommu *iommu)
 {
-	int ret;
+	int ret, i;
 
 	if (!rk_iommu_is_stall_active(iommu))
 		return 0;
@@ -341,15 +366,16 @@ static int rk_iommu_disable_stall(struct rk_iommu *iommu)
 
 	ret = rk_wait_for(!rk_iommu_is_stall_active(iommu), 1);
 	if (ret)
-		dev_err(iommu->dev, "Disable stall request timed out, status: %#08x\n",
-			rk_iommu_read(iommu, RK_MMU_STATUS));
+		for (i = 0; i < iommu->num_mmu; i++)
+			dev_err(iommu->dev, "Disable stall request timed out, status: %#08x\n",
+				rk_iommu_read(iommu->bases[i], RK_MMU_STATUS));
 
 	return ret;
 }
 
 static int rk_iommu_enable_paging(struct rk_iommu *iommu)
 {
-	int ret;
+	int ret, i;
 
 	if (rk_iommu_is_paging_enabled(iommu))
 		return 0;
@@ -358,15 +384,16 @@ static int rk_iommu_enable_paging(struct rk_iommu *iommu)
 
 	ret = rk_wait_for(rk_iommu_is_paging_enabled(iommu), 1);
 	if (ret)
-		dev_err(iommu->dev, "Enable paging request timed out, status: %#08x\n",
-			rk_iommu_read(iommu, RK_MMU_STATUS));
+		for (i = 0; i < iommu->num_mmu; i++)
+			dev_err(iommu->dev, "Enable paging request timed out, status: %#08x\n",
+				rk_iommu_read(iommu->bases[i], RK_MMU_STATUS));
 
 	return ret;
 }
 
 static int rk_iommu_disable_paging(struct rk_iommu *iommu)
 {
-	int ret;
+	int ret, i;
 
 	if (!rk_iommu_is_paging_enabled(iommu))
 		return 0;
@@ -375,41 +402,49 @@ static int rk_iommu_disable_paging(struct rk_iommu *iommu)
 
 	ret = rk_wait_for(!rk_iommu_is_paging_enabled(iommu), 1);
 	if (ret)
-		dev_err(iommu->dev, "Disable paging request timed out, status: %#08x\n",
-			rk_iommu_read(iommu, RK_MMU_STATUS));
+		for (i = 0; i < iommu->num_mmu; i++)
+			dev_err(iommu->dev, "Disable paging request timed out, status: %#08x\n",
+				rk_iommu_read(iommu->bases[i], RK_MMU_STATUS));
 
 	return ret;
 }
 
 static int rk_iommu_force_reset(struct rk_iommu *iommu)
 {
-	int ret;
+	int ret, i;
 	u32 dte_addr;
 
 	/*
 	 * Check if register DTE_ADDR is working by writing DTE_ADDR_DUMMY
 	 * and verifying that upper 5 nybbles are read back.
 	 */
-	rk_iommu_write(iommu, RK_MMU_DTE_ADDR, DTE_ADDR_DUMMY);
+	for (i = 0; i < iommu->num_mmu; i++) {
+		rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, DTE_ADDR_DUMMY);
 
-	dte_addr = rk_iommu_read(iommu, RK_MMU_DTE_ADDR);
-	if (dte_addr != (DTE_ADDR_DUMMY & RK_DTE_PT_ADDRESS_MASK)) {
-		dev_err(iommu->dev, "Error during raw reset. MMU_DTE_ADDR is not functioning\n");
-		return -EFAULT;
+		dte_addr = rk_iommu_read(iommu->bases[i], RK_MMU_DTE_ADDR);
+		if (dte_addr != (DTE_ADDR_DUMMY & RK_DTE_PT_ADDRESS_MASK)) {
+			dev_err(iommu->dev, "Error during raw reset. MMU_DTE_ADDR is not functioning\n");
+			return -EFAULT;
+		}
 	}
 
 	rk_iommu_command(iommu, RK_MMU_CMD_FORCE_RESET);
 
-	ret = rk_wait_for(rk_iommu_read(iommu, RK_MMU_DTE_ADDR) == 0x00000000,
-			  FORCE_RESET_TIMEOUT);
-	if (ret)
-		dev_err(iommu->dev, "FORCE_RESET command timed out\n");
+	for (i = 0; i < iommu->num_mmu; i++) {
+		ret = rk_wait_for(rk_iommu_read(iommu->bases[i], RK_MMU_DTE_ADDR) == 0x00000000,
+				  FORCE_RESET_TIMEOUT);
+		if (ret) {
+			dev_err(iommu->dev, "FORCE_RESET command timed out\n");
+			return ret;
+		}
+	}
 
-	return ret;
+	return 0;
 }
 
-static void log_iova(struct rk_iommu *iommu, dma_addr_t iova)
+static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
 {
+	void __iomem *base = iommu->bases[index];
 	u32 dte_index, pte_index, page_offset;
 	u32 mmu_dte_addr;
 	phys_addr_t mmu_dte_addr_phys, dte_addr_phys;
@@ -425,7 +460,7 @@ static void log_iova(struct rk_iommu *iommu, dma_addr_t iova)
 	pte_index = rk_iova_pte_index(iova);
 	page_offset = rk_iova_page_offset(iova);
 
-	mmu_dte_addr = rk_iommu_read(iommu, RK_MMU_DTE_ADDR);
+	mmu_dte_addr = rk_iommu_read(base, RK_MMU_DTE_ADDR);
 	mmu_dte_addr_phys = (phys_addr_t)mmu_dte_addr;
 
 	dte_addr_phys = mmu_dte_addr_phys + (4 * dte_index);
@@ -460,51 +495,56 @@ static irqreturn_t rk_iommu_irq(int irq, void *dev_id)
 	u32 status;
 	u32 int_status;
 	dma_addr_t iova;
+	irqreturn_t ret = IRQ_NONE;
+	int i;
 
-	int_status = rk_iommu_read(iommu, RK_MMU_INT_STATUS);
-	if (int_status == 0)
-		return IRQ_NONE;
+	for (i = 0; i < iommu->num_mmu; i++) {
+		int_status = rk_iommu_read(iommu->bases[i], RK_MMU_INT_STATUS);
+		if (int_status == 0)
+			continue;
 
-	iova = rk_iommu_read(iommu, RK_MMU_PAGE_FAULT_ADDR);
+		ret = IRQ_HANDLED;
+		iova = rk_iommu_read(iommu->bases[i], RK_MMU_PAGE_FAULT_ADDR);
 
-	if (int_status & RK_MMU_IRQ_PAGE_FAULT) {
-		int flags;
+		if (int_status & RK_MMU_IRQ_PAGE_FAULT) {
+			int flags;
 
-		status = rk_iommu_read(iommu, RK_MMU_STATUS);
-		flags = (status & RK_MMU_STATUS_PAGE_FAULT_IS_WRITE) ?
-				IOMMU_FAULT_WRITE : IOMMU_FAULT_READ;
+			status = rk_iommu_read(iommu->bases[i], RK_MMU_STATUS);
+			flags = (status & RK_MMU_STATUS_PAGE_FAULT_IS_WRITE) ?
+					IOMMU_FAULT_WRITE : IOMMU_FAULT_READ;
 
-		dev_err(iommu->dev, "Page fault at %pad of type %s\n",
-			&iova,
-			(flags == IOMMU_FAULT_WRITE) ? "write" : "read");
+			dev_err(iommu->dev, "Page fault at %pad of type %s\n",
+				&iova,
+				(flags == IOMMU_FAULT_WRITE) ? "write" : "read");
 
-		log_iova(iommu, iova);
+			log_iova(iommu, i, iova);
 
-		/*
-		 * Report page fault to any installed handlers.
-		 * Ignore the return code, though, since we always zap cache
-		 * and clear the page fault anyway.
-		 */
-		if (iommu->domain)
-			report_iommu_fault(iommu->domain, iommu->dev, iova,
-					   flags);
-		else
-			dev_err(iommu->dev, "Page fault while iommu not attached to domain?\n");
+			/*
+			 * Report page fault to any installed handlers.
+			 * Ignore the return code, though, since we always zap cache
+			 * and clear the page fault anyway.
+			 */
+			if (iommu->domain)
+				report_iommu_fault(iommu->domain, iommu->dev, iova,
+						   flags);
+			else
+				dev_err(iommu->dev, "Page fault while iommu not attached to domain?\n");
 
-		rk_iommu_command(iommu, RK_MMU_CMD_ZAP_CACHE);
-		rk_iommu_command(iommu, RK_MMU_CMD_PAGE_FAULT_DONE);
-	}
+			rk_iommu_base_command(iommu->bases[i], RK_MMU_CMD_ZAP_CACHE);
+			rk_iommu_base_command(iommu->bases[i], RK_MMU_CMD_PAGE_FAULT_DONE);
+		}
 
-	if (int_status & RK_MMU_IRQ_BUS_ERROR)
-		dev_err(iommu->dev, "BUS_ERROR occurred at %pad\n", &iova);
+		if (int_status & RK_MMU_IRQ_BUS_ERROR)
+			dev_err(iommu->dev, "BUS_ERROR occurred at %pad\n", &iova);
 
-	if (int_status & ~RK_MMU_IRQ_MASK)
-		dev_err(iommu->dev, "unexpected int_status: %#08x\n",
-			int_status);
+		if (int_status & ~RK_MMU_IRQ_MASK)
+			dev_err(iommu->dev, "unexpected int_status: %#08x\n",
+				int_status);
 
-	rk_iommu_write(iommu, RK_MMU_INT_CLEAR, int_status);
+		rk_iommu_write(iommu->bases[i], RK_MMU_INT_CLEAR, int_status);
+	}
 
-	return IRQ_HANDLED;
+	return ret;
 }
 
 static phys_addr_t rk_iommu_iova_to_phys(struct iommu_domain *domain,
@@ -746,7 +786,7 @@ static int rk_iommu_attach_device(struct iommu_domain *domain,
 	struct rk_iommu *iommu;
 	struct rk_iommu_domain *rk_domain = to_rk_domain(domain);
 	unsigned long flags;
-	int ret;
+	int ret, i;
 	phys_addr_t dte_addr;
 
 	/*
@@ -773,9 +813,11 @@ static int rk_iommu_attach_device(struct iommu_domain *domain,
 		return ret;
 
 	dte_addr = virt_to_phys(rk_domain->dt);
-	rk_iommu_write(iommu, RK_MMU_DTE_ADDR, dte_addr);
-	rk_iommu_command(iommu, RK_MMU_CMD_ZAP_CACHE);
-	rk_iommu_write(iommu, RK_MMU_INT_MASK, RK_MMU_IRQ_MASK);
+	for (i = 0; i < iommu->num_mmu; i++) {
+		rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, dte_addr);
+		rk_iommu_command(iommu->bases[i], RK_MMU_CMD_ZAP_CACHE);
+		rk_iommu_write(iommu->bases[i], RK_MMU_INT_MASK, RK_MMU_IRQ_MASK);
+	}
 
 	ret = rk_iommu_enable_paging(iommu);
 	if (ret)
@@ -798,6 +840,7 @@ static void rk_iommu_detach_device(struct iommu_domain *domain,
 	struct rk_iommu *iommu;
 	struct rk_iommu_domain *rk_domain = to_rk_domain(domain);
 	unsigned long flags;
+	int i;
 
 	/* Allow 'virtual devices' (eg drm) to detach from domain */
 	iommu = rk_iommu_from_dev(dev);
@@ -811,8 +854,10 @@ static void rk_iommu_detach_device(struct iommu_domain *domain,
 	/* Ignore error while disabling, just keep going */
 	rk_iommu_enable_stall(iommu);
 	rk_iommu_disable_paging(iommu);
-	rk_iommu_write(iommu, RK_MMU_INT_MASK, 0);
-	rk_iommu_write(iommu, RK_MMU_DTE_ADDR, 0);
+	for (i = 0; i < iommu->num_mmu; i++) {
+		rk_iommu_write(iommu->bases[i], RK_MMU_INT_MASK, 0);
+		rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, 0);
+	}
 	rk_iommu_disable_stall(iommu);
 
 	devm_free_irq(dev, iommu->irq, iommu);
@@ -988,6 +1033,7 @@ static int rk_iommu_probe(struct platform_device *pdev)
 	struct device *dev = &pdev->dev;
 	struct rk_iommu *iommu;
 	struct resource *res;
+	int i;
 
 	iommu = devm_kzalloc(dev, sizeof(*iommu), GFP_KERNEL);
 	if (!iommu)
@@ -995,11 +1041,21 @@ static int rk_iommu_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, iommu);
 	iommu->dev = dev;
+	iommu->num_mmu = 0;
+	iommu->bases = devm_kzalloc(dev, sizeof(*iommu->bases) * iommu->num_mmu,
+				    GFP_KERNEL);
+	if (!iommu->bases)
+		return -ENOMEM;
 
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	iommu->base = devm_ioremap_resource(&pdev->dev, res);
-	if (IS_ERR(iommu->base))
-		return PTR_ERR(iommu->base);
+	for (i = 0; i < pdev->num_resources; i++) {
+		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+		iommu->bases[i] = devm_ioremap_resource(&pdev->dev, res);
+		if (IS_ERR(iommu->bases[i]))
+			continue;
+		iommu->num_mmu++;
+	}
+	if (iommu->num_mmu == 0)
+		return PTR_ERR(iommu->bases[0]);
 
 	iommu->irq = platform_get_irq(pdev, 0);
 	if (iommu->irq < 0) {
-- 
1.9.1



From andy.yan at rock-chips.com  Tue Jan 19 00:31:27 2016
From: andy.yan at rock-chips.com (Andy Yan)
Date: Tue, 19 Jan 2016 16:31:27 +0800
Subject: [PATCH v2 1/4] dt-bindings: power: reset: add document for
 reboot-mode driver
In-Reply-To: <CALAqxLUxh3=LhoHxqiRm_5L4G6m1Vctp=aUg+9_uAtLkFwW9bw@mail.gmail.com>
References: <1452598029-8222-1-git-send-email-andy.yan@rock-chips.com>
 <1452598189-8272-1-git-send-email-andy.yan@rock-chips.com>
 <CALAqxLUxh3=LhoHxqiRm_5L4G6m1Vctp=aUg+9_uAtLkFwW9bw@mail.gmail.com>
Message-ID: <569DF45F.3050908@rock-chips.com>

Hi John:

On 2016?01?16? 06:41, John Stultz wrote:
> On Tue, Jan 12, 2016 at 3:29 AM, Andy Yan <andy.yan at rock-chips.com> wrote:
>> add device tree binding document for reboot-mode driver
>>
>> Signed-off-by: Andy Yan <andy.yan at rock-chips.com>
>>
>> ---
>>
>> Changes in v2: None
>> Changes in v1: None
>>
>>   .../bindings/power/reset/reboot-mode.txt           | 41 +++++++++++++++++
>>   .../bindings/power/reset/syscon-reboot-mode.txt    | 52 ++++++++++++++++++++++
>>   2 files changed, 93 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/power/reset/reboot-mode.txt
>>   create mode 100644 Documentation/devicetree/bindings/power/reset/syscon-reboot-mode.txt
>>
>> diff --git a/Documentation/devicetree/bindings/power/reset/reboot-mode.txt b/Documentation/devicetree/bindings/power/reset/reboot-mode.txt
>> new file mode 100644
>> index 0000000..81d9f66
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/power/reset/reboot-mode.txt
>> @@ -0,0 +1,41 @@
>> +Generic reboot mode core map driver
>> +
>> +This driver get reboot mode arguments and call the write
>> +interface to stores the magic value in special register
>> +or ram . Then the bootloader can read it and take different
>> +action according to the argument stored.
>> +
>> +Required properties:
>> +- compatible: only support "syscon-reboot-mode" now.
>> +
>> +Each mode is represented as a sub-node of reboot_mode:
>> +
>> +Subnode required properties:
>> +- linux,mode: reboot mode command,such as "loader", "recovery", "fastboot".
>> +- loader,magic: magic number for the mode, this is vendor specific.
>> +
>> +Example:
>> +       reboot_mode {
>> +               compatible = "syscon-reboot-mode";
>> +               offset = <0x40>;
>> +
>> +               loader {
>> +                       linux,mode = "loader";
>> +                       loader,magic = <BOOT_LOADER>;
>> +               };
>> +
>> +               maskrom {
>> +                       linux,mode = "maskrom";
>> +                       loader,magic = <BOOT_MASKROM>;
>> +               };
>> +
>> +               recovery {
>> +                       linux,mode = "recovery";
>> +                       loader,magic = <BOOT_RECOVERY>;
>> +               };
>> +
>> +               fastboot {
>> +                       linux,mode = "fastboot";
>> +                       loader,magic = <BOOT_FASTBOOT>;
>> +               };
>> +        };
>
> So one minor thought here. While the commands are somewhat vendor
> specific, would it be a good idea for the example commands to match
> the common commands on Android devices?  For example, usually
> "bootloader" is what gets you into fastboot mode on nexus devices.
>
> thanks
> -john
>
>
>
>

   When I run the "adb help" , I got the following message:
   adb reboot [bootloader|recovery] - reboots the device, optionally 
into the bootloader or recovery program
   adb reboot-bootloader        - reboots the device into the bootloader

   It  only says "adb reboot-bootloader" will reboot the device into 
bootloader, not specific to fastboot.

   I add @Simon from Google here, maybe he can give some suggestions.





From andy.yan at rock-chips.com  Tue Jan 19 00:38:20 2016
From: andy.yan at rock-chips.com (Andy Yan)
Date: Tue, 19 Jan 2016 16:38:20 +0800
Subject: [PATCH v2 2/4] power: reset: add reboot mode driver
In-Reply-To: <CALAqxLVNUG_cVbavXC34sNPv68jbMghti31+PrgQ7mpPngXh6g@mail.gmail.com>
References: <1452598029-8222-1-git-send-email-andy.yan@rock-chips.com>
 <1452598319-8324-1-git-send-email-andy.yan@rock-chips.com>
 <CALAqxLVNUG_cVbavXC34sNPv68jbMghti31+PrgQ7mpPngXh6g@mail.gmail.com>
Message-ID: <569DF5FC.1080402@rock-chips.com>

Hi John:

On 2016?01?16? 04:27, John Stultz wrote:
> On Tue, Jan 12, 2016 at 3:31 AM, Andy Yan <andy.yan at rock-chips.com> wrote:
>> This driver parse the reboot commands like "reboot loader"
>> and "reboot recovery" to get a boot mode described in the
>> device tree , then call the write interfae to store the boot
>> mode in some persistent storage  like special register or ram,
>> which can be read by the bootloader after system reboot, then
>> the bootloader can take different action according to the mode
>> stored.
>>
>> This is commonly used on Android based devices, which in order
>> to reboot the device into fastboot or recovery mode.
>>
>> Signed-off-by: Andy Yan <andy.yan at rock-chips.com>
>
> Hey Andy!
>    Thanks for keeping this work going! I've just successfully reworked
> my Nexus7 tree to use your implementation (using the syscon version,
> setting up a syscon for IMEM as Bjorn had requested earlier).
>
> All is working well so far!
>
> The one thing I was working on supporting with my own version that
> seems to be missing here are for devices that use string based codes,
> rather then magic numbers.
>
> This was mostly a theoretical issue. I think the Galaxy Nexus used it,
> and when I was looking at some of the HTC devices, they support a text
> based reason along with the magic code, but at least in some
> implementations the text mode isn't used, so I suspect there its just
> for extra debugging.  So this may not be critical to solve until
> someone tries to add support for such a device.
>
> Anyway, I'm going to look at porting this to the HiKey board next
> (which just uses reserved ram, not syscon), so I'll try to do a an
> SRAM driver implementaiton to see how that goes.
>
> thanks!
> -john
>
>
>
>

  Very glad to hear news from you. And welcome your SRAM based driver 
implementation.






From ykk at rock-chips.com  Tue Jan 19 01:58:37 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Tue, 19 Jan 2016 17:58:37 +0800
Subject: [PATCH v12.1 01/17] drm: bridge: analogix/dp: split exynos dp driver
 to bridge directory
In-Reply-To: <1450873538-18304-1-git-send-email-ykk@rock-chips.com>
References: <1450873538-18304-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1453197517-13085-1-git-send-email-ykk@rock-chips.com>

Split the dp core driver from exynos directory to bridge directory,
and rename the core driver to analogix_dp_*, rename the platform
code to exynos_dp.

Beside the new analogix_dp driver would export six hooks.
"analogix_dp_bind()" and "analogix_dp_unbind()"
"analogix_dp_suspned()" and "analogix_dp_resume()"
"analogix_dp_detect()" and "analogix_dp_get_modes()"

The bind/unbind symbols is used for analogix platform driver to connect
with analogix_dp core driver. And the detect/get_modes is used for analogix
platform driver to init the connector.

They reason why connector need register in helper driver is rockchip drm
haven't implement the atomic API, but Exynos drm have implement it, so
there would need two different connector helper functions, that's why we
leave the connector register in helper driver.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
There is no new changes in v12.1, but just rebase the whole patches on
top of Dave's drm-next branch [0] (Heiko)

Changes in v12.1: None
Changes in v12:
- Move the connector init to analogix_dp driver, and using ATOMIC helper(Heiko)

Changes in v11:
- Uses tabs to fix the indentation issues in analogix_dp_core.h (Heiko)

Changes in v10: None
Changes in v9: None
Changes in v8: None
Changes in v7: None
Changes in v6:
- Fix the Kconfig recursive dependency (Javier)

Changes in v5:
- Correct the check condition of gpio_is_valid when driver try to get
  the "hpd-gpios" DT propery. (Heiko)
- Move the platform attach callback in the front of core driver bridge
  attch function. Cause once platform failed at attach, core driver should
  still failed, so no need to init connector before platform attached (Krzysztof)
- Keep code style no changes with the previous exynos_dp_code.c in this
  patch, and update commit message about the new export symbol (Krzysztof)
- Gather the device type patch (v4 11/16) into this one. (Krzysztof)
- leave out the connector registration to analogix platform driver. (Thierry)

Changes in v4:
- Update "analogix,hpd-gpios" to "hpd-gpios" DT propery. (Rob)
- Rename "analogix_dp-exynos.c" file name to "exynos_dp.c" (Jingoo)
- Create a separate folder for analogix code in bridge/ (Archit)

Changes in v3:
- Move exynos's video_timing code to analogix_dp-exynos platform driver,
  add get_modes method to struct analogix_dp_plat_data. (Thierry)
- Rename some "samsung*" dts propery to "analogix*". (Heiko)

Changes in v2:
- Remove new copyright (Jingoo)
- Fix compiled failed due to analogix_dp_device misspell

 drivers/gpu/drm/bridge/Kconfig                     |    2 +
 drivers/gpu/drm/bridge/Makefile                    |    1 +
 drivers/gpu/drm/bridge/analogix/Kconfig            |    3 +
 drivers/gpu/drm/bridge/analogix/Makefile           |    1 +
 .../analogix/analogix_dp_core.c}                   |  816 +++++--------
 drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  277 +++++
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  | 1263 ++++++++++++++++++++
 .../analogix/analogix_dp_reg.h}                    |  258 ++--
 drivers/gpu/drm/exynos/Kconfig                     |    3 +-
 drivers/gpu/drm/exynos/Makefile                    |    2 +-
 drivers/gpu/drm/exynos/exynos_dp.c                 |  328 +++++
 drivers/gpu/drm/exynos/exynos_dp_core.h            |  282 -----
 drivers/gpu/drm/exynos/exynos_dp_reg.c             | 1263 --------------------
 include/drm/bridge/analogix_dp.h                   |   40 +
 14 files changed, 2373 insertions(+), 2166 deletions(-)
 create mode 100644 drivers/gpu/drm/bridge/analogix/Kconfig
 create mode 100644 drivers/gpu/drm/bridge/analogix/Makefile
 rename drivers/gpu/drm/{exynos/exynos_dp_core.c => bridge/analogix/analogix_dp_core.c} (52%)
 create mode 100644 drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
 create mode 100644 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
 rename drivers/gpu/drm/{exynos/exynos_dp_reg.h => bridge/analogix/analogix_dp_reg.h} (64%)
 create mode 100644 drivers/gpu/drm/exynos/exynos_dp.c
 delete mode 100644 drivers/gpu/drm/exynos/exynos_dp_core.h
 delete mode 100644 drivers/gpu/drm/exynos/exynos_dp_reg.c
 create mode 100644 include/drm/bridge/analogix_dp.h

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 27e2022..efd94e0 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -40,4 +40,6 @@ config DRM_PARADE_PS8622
 	---help---
 	  Parade eDP-LVDS bridge chip driver.
 
+source "drivers/gpu/drm/bridge/analogix/Kconfig"
+
 endmenu
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index f13c33d..ff821f4 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o
 obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o
 obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
 obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
+obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/
diff --git a/drivers/gpu/drm/bridge/analogix/Kconfig b/drivers/gpu/drm/bridge/analogix/Kconfig
new file mode 100644
index 0000000..80f286f
--- /dev/null
+++ b/drivers/gpu/drm/bridge/analogix/Kconfig
@@ -0,0 +1,3 @@
+config DRM_ANALOGIX_DP
+	tristate
+	depends on DRM
diff --git a/drivers/gpu/drm/bridge/analogix/Makefile b/drivers/gpu/drm/bridge/analogix/Makefile
new file mode 100644
index 0000000..9107b86
--- /dev/null
+++ b/drivers/gpu/drm/bridge/analogix/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix_dp_core.o analogix_dp_reg.o
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
similarity index 52%
rename from drivers/gpu/drm/exynos/exynos_dp_core.c
rename to drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index b79c316..392c296 100644
--- a/drivers/gpu/drm/exynos/exynos_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -1,14 +1,14 @@
 /*
- * Samsung SoC DP (Display Port) interface driver.
- *
- * Copyright (C) 2012 Samsung Electronics Co., Ltd.
- * Author: Jingoo Han <jg1.han at samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
+* Analogix DP (Display Port) core interface driver.
+*
+* Copyright (C) 2012 Samsung Electronics Co., Ltd.
+* Author: Jingoo Han <jg1.han at samsung.com>
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*/
 
 #include <linux/module.h>
 #include <linux/platform_device.h>
@@ -18,65 +18,51 @@
 #include <linux/interrupt.h>
 #include <linux/of.h>
 #include <linux/of_gpio.h>
-#include <linux/of_graph.h>
 #include <linux/gpio.h>
 #include <linux/component.h>
 #include <linux/phy/phy.h>
-#include <video/of_display_timing.h>
-#include <video/of_videomode.h>
 
 #include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
-#include <drm/drm_atomic_helper.h>
 #include <drm/drm_panel.h>
 
-#include "exynos_dp_core.h"
-#include "exynos_drm_crtc.h"
+#include <drm/bridge/analogix_dp.h>
 
-#define ctx_from_connector(c)	container_of(c, struct exynos_dp_device, \
-					connector)
+#include "analogix_dp_core.h"
 
-static inline struct exynos_drm_crtc *dp_to_crtc(struct exynos_dp_device *dp)
-{
-	return to_exynos_crtc(dp->encoder.crtc);
-}
-
-static inline struct exynos_dp_device *encoder_to_dp(
-						struct drm_encoder *e)
-{
-	return container_of(e, struct exynos_dp_device, encoder);
-}
+#define to_dp(nm)	container_of(nm, struct analogix_dp_device, nm)
 
 struct bridge_init {
 	struct i2c_client *client;
 	struct device_node *node;
 };
 
-static void exynos_dp_init_dp(struct exynos_dp_device *dp)
+static void analogix_dp_init_dp(struct analogix_dp_device *dp)
 {
-	exynos_dp_reset(dp);
+	analogix_dp_reset(dp);
 
-	exynos_dp_swreset(dp);
+	analogix_dp_swreset(dp);
 
-	exynos_dp_init_analog_param(dp);
-	exynos_dp_init_interrupt(dp);
+	analogix_dp_init_analog_param(dp);
+	analogix_dp_init_interrupt(dp);
 
 	/* SW defined function Normal operation */
-	exynos_dp_enable_sw_function(dp);
+	analogix_dp_enable_sw_function(dp);
 
-	exynos_dp_config_interrupt(dp);
-	exynos_dp_init_analog_func(dp);
+	analogix_dp_config_interrupt(dp);
+	analogix_dp_init_analog_func(dp);
 
-	exynos_dp_init_hpd(dp);
-	exynos_dp_init_aux(dp);
+	analogix_dp_init_hpd(dp);
+	analogix_dp_init_aux(dp);
 }
 
-static int exynos_dp_detect_hpd(struct exynos_dp_device *dp)
+static int analogix_dp_detect_hpd(struct analogix_dp_device *dp)
 {
 	int timeout_loop = 0;
 
-	while (exynos_dp_get_plug_in_status(dp) != 0) {
+	while (analogix_dp_get_plug_in_status(dp) != 0) {
 		timeout_loop++;
 		if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) {
 			dev_err(dp->dev, "failed to get hpd plug status\n");
@@ -88,7 +74,7 @@ static int exynos_dp_detect_hpd(struct exynos_dp_device *dp)
 	return 0;
 }
 
-static unsigned char exynos_dp_calc_edid_check_sum(unsigned char *edid_data)
+static unsigned char analogix_dp_calc_edid_check_sum(unsigned char *edid_data)
 {
 	int i;
 	unsigned char sum = 0;
@@ -99,7 +85,7 @@ static unsigned char exynos_dp_calc_edid_check_sum(unsigned char *edid_data)
 	return sum;
 }
 
-static int exynos_dp_read_edid(struct exynos_dp_device *dp)
+static int analogix_dp_read_edid(struct analogix_dp_device *dp)
 {
 	unsigned char edid[EDID_BLOCK_LENGTH * 2];
 	unsigned int extend_block = 0;
@@ -114,7 +100,7 @@ static int exynos_dp_read_edid(struct exynos_dp_device *dp)
 	 */
 
 	/* Read Extension Flag, Number of 128-byte EDID extension blocks */
-	retval = exynos_dp_read_byte_from_i2c(dp, I2C_EDID_DEVICE_ADDR,
+	retval = analogix_dp_read_byte_from_i2c(dp, I2C_EDID_DEVICE_ADDR,
 				EDID_EXTENSION_FLAG,
 				&extend_block);
 	if (retval)
@@ -124,7 +110,7 @@ static int exynos_dp_read_edid(struct exynos_dp_device *dp)
 		dev_dbg(dp->dev, "EDID data includes a single extension!\n");
 
 		/* Read EDID data */
-		retval = exynos_dp_read_bytes_from_i2c(dp, I2C_EDID_DEVICE_ADDR,
+		retval = analogix_dp_read_bytes_from_i2c(dp, I2C_EDID_DEVICE_ADDR,
 						EDID_HEADER_PATTERN,
 						EDID_BLOCK_LENGTH,
 						&edid[EDID_HEADER_PATTERN]);
@@ -132,14 +118,14 @@ static int exynos_dp_read_edid(struct exynos_dp_device *dp)
 			dev_err(dp->dev, "EDID Read failed!\n");
 			return -EIO;
 		}
-		sum = exynos_dp_calc_edid_check_sum(edid);
+		sum = analogix_dp_calc_edid_check_sum(edid);
 		if (sum != 0) {
 			dev_err(dp->dev, "EDID bad checksum!\n");
 			return -EIO;
 		}
 
 		/* Read additional EDID data */
-		retval = exynos_dp_read_bytes_from_i2c(dp,
+		retval = analogix_dp_read_bytes_from_i2c(dp,
 				I2C_EDID_DEVICE_ADDR,
 				EDID_BLOCK_LENGTH,
 				EDID_BLOCK_LENGTH,
@@ -148,19 +134,19 @@ static int exynos_dp_read_edid(struct exynos_dp_device *dp)
 			dev_err(dp->dev, "EDID Read failed!\n");
 			return -EIO;
 		}
-		sum = exynos_dp_calc_edid_check_sum(&edid[EDID_BLOCK_LENGTH]);
+		sum = analogix_dp_calc_edid_check_sum(&edid[EDID_BLOCK_LENGTH]);
 		if (sum != 0) {
 			dev_err(dp->dev, "EDID bad checksum!\n");
 			return -EIO;
 		}
 
-		exynos_dp_read_byte_from_dpcd(dp, DP_TEST_REQUEST,
+		analogix_dp_read_byte_from_dpcd(dp, DP_TEST_REQUEST,
 					&test_vector);
 		if (test_vector & DP_TEST_LINK_EDID_READ) {
-			exynos_dp_write_byte_to_dpcd(dp,
+			analogix_dp_write_byte_to_dpcd(dp,
 				DP_TEST_EDID_CHECKSUM,
 				edid[EDID_BLOCK_LENGTH + EDID_CHECKSUM]);
-			exynos_dp_write_byte_to_dpcd(dp,
+			analogix_dp_write_byte_to_dpcd(dp,
 				DP_TEST_RESPONSE,
 				DP_TEST_EDID_CHECKSUM_WRITE);
 		}
@@ -168,7 +154,7 @@ static int exynos_dp_read_edid(struct exynos_dp_device *dp)
 		dev_info(dp->dev, "EDID data does not include any extensions.\n");
 
 		/* Read EDID data */
-		retval = exynos_dp_read_bytes_from_i2c(dp,
+		retval = analogix_dp_read_bytes_from_i2c(dp,
 				I2C_EDID_DEVICE_ADDR,
 				EDID_HEADER_PATTERN,
 				EDID_BLOCK_LENGTH,
@@ -177,20 +163,20 @@ static int exynos_dp_read_edid(struct exynos_dp_device *dp)
 			dev_err(dp->dev, "EDID Read failed!\n");
 			return -EIO;
 		}
-		sum = exynos_dp_calc_edid_check_sum(edid);
+		sum = analogix_dp_calc_edid_check_sum(edid);
 		if (sum != 0) {
 			dev_err(dp->dev, "EDID bad checksum!\n");
 			return -EIO;
 		}
 
-		exynos_dp_read_byte_from_dpcd(dp,
+		analogix_dp_read_byte_from_dpcd(dp,
 			DP_TEST_REQUEST,
 			&test_vector);
 		if (test_vector & DP_TEST_LINK_EDID_READ) {
-			exynos_dp_write_byte_to_dpcd(dp,
+			analogix_dp_write_byte_to_dpcd(dp,
 				DP_TEST_EDID_CHECKSUM,
 				edid[EDID_CHECKSUM]);
-			exynos_dp_write_byte_to_dpcd(dp,
+			analogix_dp_write_byte_to_dpcd(dp,
 				DP_TEST_RESPONSE,
 				DP_TEST_EDID_CHECKSUM_WRITE);
 		}
@@ -200,21 +186,21 @@ static int exynos_dp_read_edid(struct exynos_dp_device *dp)
 	return 0;
 }
 
-static int exynos_dp_handle_edid(struct exynos_dp_device *dp)
+static int analogix_dp_handle_edid(struct analogix_dp_device *dp)
 {
 	u8 buf[12];
 	int i;
 	int retval;
 
 	/* Read DPCD DP_DPCD_REV~RECEIVE_PORT1_CAP_1 */
-	retval = exynos_dp_read_bytes_from_dpcd(dp, DP_DPCD_REV,
+	retval = analogix_dp_read_bytes_from_dpcd(dp, DP_DPCD_REV,
 				12, buf);
 	if (retval)
 		return retval;
 
 	/* Read EDID */
 	for (i = 0; i < 3; i++) {
-		retval = exynos_dp_read_edid(dp);
+		retval = analogix_dp_read_edid(dp);
 		if (!retval)
 			break;
 	}
@@ -222,73 +208,73 @@ static int exynos_dp_handle_edid(struct exynos_dp_device *dp)
 	return retval;
 }
 
-static void exynos_dp_enable_rx_to_enhanced_mode(struct exynos_dp_device *dp,
+static void analogix_dp_enable_rx_to_enhanced_mode(struct analogix_dp_device *dp,
 						bool enable)
 {
 	u8 data;
 
-	exynos_dp_read_byte_from_dpcd(dp, DP_LANE_COUNT_SET, &data);
+	analogix_dp_read_byte_from_dpcd(dp, DP_LANE_COUNT_SET, &data);
 
 	if (enable)
-		exynos_dp_write_byte_to_dpcd(dp, DP_LANE_COUNT_SET,
+		analogix_dp_write_byte_to_dpcd(dp, DP_LANE_COUNT_SET,
 			DP_LANE_COUNT_ENHANCED_FRAME_EN |
 			DPCD_LANE_COUNT_SET(data));
 	else
-		exynos_dp_write_byte_to_dpcd(dp, DP_LANE_COUNT_SET,
+		analogix_dp_write_byte_to_dpcd(dp, DP_LANE_COUNT_SET,
 			DPCD_LANE_COUNT_SET(data));
 }
 
-static int exynos_dp_is_enhanced_mode_available(struct exynos_dp_device *dp)
+static int analogix_dp_is_enhanced_mode_available(struct analogix_dp_device *dp)
 {
 	u8 data;
 	int retval;
 
-	exynos_dp_read_byte_from_dpcd(dp, DP_MAX_LANE_COUNT, &data);
+	analogix_dp_read_byte_from_dpcd(dp, DP_MAX_LANE_COUNT, &data);
 	retval = DPCD_ENHANCED_FRAME_CAP(data);
 
 	return retval;
 }
 
-static void exynos_dp_set_enhanced_mode(struct exynos_dp_device *dp)
+static void analogix_dp_set_enhanced_mode(struct analogix_dp_device *dp)
 {
 	u8 data;
 
-	data = exynos_dp_is_enhanced_mode_available(dp);
-	exynos_dp_enable_rx_to_enhanced_mode(dp, data);
-	exynos_dp_enable_enhanced_mode(dp, data);
+	data = analogix_dp_is_enhanced_mode_available(dp);
+	analogix_dp_enable_rx_to_enhanced_mode(dp, data);
+	analogix_dp_enable_enhanced_mode(dp, data);
 }
 
-static void exynos_dp_training_pattern_dis(struct exynos_dp_device *dp)
+static void analogix_dp_training_pattern_dis(struct analogix_dp_device *dp)
 {
-	exynos_dp_set_training_pattern(dp, DP_NONE);
+	analogix_dp_set_training_pattern(dp, DP_NONE);
 
-	exynos_dp_write_byte_to_dpcd(dp,
+	analogix_dp_write_byte_to_dpcd(dp,
 		DP_TRAINING_PATTERN_SET,
 		DP_TRAINING_PATTERN_DISABLE);
 }
 
-static void exynos_dp_set_lane_lane_pre_emphasis(struct exynos_dp_device *dp,
+static void analogix_dp_set_lane_lane_pre_emphasis(struct analogix_dp_device *dp,
 					int pre_emphasis, int lane)
 {
 	switch (lane) {
 	case 0:
-		exynos_dp_set_lane0_pre_emphasis(dp, pre_emphasis);
+		analogix_dp_set_lane0_pre_emphasis(dp, pre_emphasis);
 		break;
 	case 1:
-		exynos_dp_set_lane1_pre_emphasis(dp, pre_emphasis);
+		analogix_dp_set_lane1_pre_emphasis(dp, pre_emphasis);
 		break;
 
 	case 2:
-		exynos_dp_set_lane2_pre_emphasis(dp, pre_emphasis);
+		analogix_dp_set_lane2_pre_emphasis(dp, pre_emphasis);
 		break;
 
 	case 3:
-		exynos_dp_set_lane3_pre_emphasis(dp, pre_emphasis);
+		analogix_dp_set_lane3_pre_emphasis(dp, pre_emphasis);
 		break;
 	}
 }
 
-static int exynos_dp_link_start(struct exynos_dp_device *dp)
+static int analogix_dp_link_start(struct analogix_dp_device *dp)
 {
 	u8 buf[4];
 	int lane, lane_count, pll_tries, retval;
@@ -302,25 +288,25 @@ static int exynos_dp_link_start(struct exynos_dp_device *dp)
 		dp->link_train.cr_loop[lane] = 0;
 
 	/* Set link rate and count as you want to establish*/
-	exynos_dp_set_link_bandwidth(dp, dp->link_train.link_rate);
-	exynos_dp_set_lane_count(dp, dp->link_train.lane_count);
+	analogix_dp_set_link_bandwidth(dp, dp->link_train.link_rate);
+	analogix_dp_set_lane_count(dp, dp->link_train.lane_count);
 
 	/* Setup RX configuration */
 	buf[0] = dp->link_train.link_rate;
 	buf[1] = dp->link_train.lane_count;
-	retval = exynos_dp_write_bytes_to_dpcd(dp, DP_LINK_BW_SET,
+	retval = analogix_dp_write_bytes_to_dpcd(dp, DP_LINK_BW_SET,
 				2, buf);
 	if (retval)
 		return retval;
 
 	/* Set TX pre-emphasis to minimum */
 	for (lane = 0; lane < lane_count; lane++)
-		exynos_dp_set_lane_lane_pre_emphasis(dp,
+		analogix_dp_set_lane_lane_pre_emphasis(dp,
 			PRE_EMPHASIS_LEVEL_0, lane);
 
 	/* Wait for PLL lock */
 	pll_tries = 0;
-	while (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
+	while (analogix_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
 		if (pll_tries == DP_TIMEOUT_LOOP_COUNT) {
 			dev_err(dp->dev, "Wait for PLL lock timed out\n");
 			return -ETIMEDOUT;
@@ -331,10 +317,10 @@ static int exynos_dp_link_start(struct exynos_dp_device *dp)
 	}
 
 	/* Set training pattern 1 */
-	exynos_dp_set_training_pattern(dp, TRAINING_PTN1);
+	analogix_dp_set_training_pattern(dp, TRAINING_PTN1);
 
 	/* Set RX training pattern */
-	retval = exynos_dp_write_byte_to_dpcd(dp,
+	retval = analogix_dp_write_byte_to_dpcd(dp,
 			DP_TRAINING_PATTERN_SET,
 			DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_1);
 	if (retval)
@@ -344,13 +330,13 @@ static int exynos_dp_link_start(struct exynos_dp_device *dp)
 		buf[lane] = DP_TRAIN_PRE_EMPH_LEVEL_0 |
 			    DP_TRAIN_VOLTAGE_SWING_LEVEL_0;
 
-	retval = exynos_dp_write_bytes_to_dpcd(dp, DP_TRAINING_LANE0_SET,
+	retval = analogix_dp_write_bytes_to_dpcd(dp, DP_TRAINING_LANE0_SET,
 			lane_count, buf);
 
 	return retval;
 }
 
-static unsigned char exynos_dp_get_lane_status(u8 link_status[2], int lane)
+static unsigned char analogix_dp_get_lane_status(u8 link_status[2], int lane)
 {
 	int shift = (lane & 1) * 4;
 	u8 link_value = link_status[lane>>1];
@@ -358,20 +344,20 @@ static unsigned char exynos_dp_get_lane_status(u8 link_status[2], int lane)
 	return (link_value >> shift) & 0xf;
 }
 
-static int exynos_dp_clock_recovery_ok(u8 link_status[2], int lane_count)
+static int analogix_dp_clock_recovery_ok(u8 link_status[2], int lane_count)
 {
 	int lane;
 	u8 lane_status;
 
 	for (lane = 0; lane < lane_count; lane++) {
-		lane_status = exynos_dp_get_lane_status(link_status, lane);
+		lane_status = analogix_dp_get_lane_status(link_status, lane);
 		if ((lane_status & DP_LANE_CR_DONE) == 0)
 			return -EINVAL;
 	}
 	return 0;
 }
 
-static int exynos_dp_channel_eq_ok(u8 link_status[2], u8 link_align,
+static int analogix_dp_channel_eq_ok(u8 link_status[2], u8 link_align,
 				int lane_count)
 {
 	int lane;
@@ -381,7 +367,7 @@ static int exynos_dp_channel_eq_ok(u8 link_status[2], u8 link_align,
 		return -EINVAL;
 
 	for (lane = 0; lane < lane_count; lane++) {
-		lane_status = exynos_dp_get_lane_status(link_status, lane);
+		lane_status = analogix_dp_get_lane_status(link_status, lane);
 		lane_status &= DP_CHANNEL_EQ_BITS;
 		if (lane_status != DP_CHANNEL_EQ_BITS)
 			return -EINVAL;
@@ -390,7 +376,7 @@ static int exynos_dp_channel_eq_ok(u8 link_status[2], u8 link_align,
 	return 0;
 }
 
-static unsigned char exynos_dp_get_adjust_request_voltage(u8 adjust_request[2],
+static unsigned char analogix_dp_get_adjust_request_voltage(u8 adjust_request[2],
 							int lane)
 {
 	int shift = (lane & 1) * 4;
@@ -399,7 +385,7 @@ static unsigned char exynos_dp_get_adjust_request_voltage(u8 adjust_request[2],
 	return (link_value >> shift) & 0x3;
 }
 
-static unsigned char exynos_dp_get_adjust_request_pre_emphasis(
+static unsigned char analogix_dp_get_adjust_request_pre_emphasis(
 					u8 adjust_request[2],
 					int lane)
 {
@@ -409,45 +395,45 @@ static unsigned char exynos_dp_get_adjust_request_pre_emphasis(
 	return ((link_value >> shift) & 0xc) >> 2;
 }
 
-static void exynos_dp_set_lane_link_training(struct exynos_dp_device *dp,
+static void analogix_dp_set_lane_link_training(struct analogix_dp_device *dp,
 					u8 training_lane_set, int lane)
 {
 	switch (lane) {
 	case 0:
-		exynos_dp_set_lane0_link_training(dp, training_lane_set);
+		analogix_dp_set_lane0_link_training(dp, training_lane_set);
 		break;
 	case 1:
-		exynos_dp_set_lane1_link_training(dp, training_lane_set);
+		analogix_dp_set_lane1_link_training(dp, training_lane_set);
 		break;
 
 	case 2:
-		exynos_dp_set_lane2_link_training(dp, training_lane_set);
+		analogix_dp_set_lane2_link_training(dp, training_lane_set);
 		break;
 
 	case 3:
-		exynos_dp_set_lane3_link_training(dp, training_lane_set);
+		analogix_dp_set_lane3_link_training(dp, training_lane_set);
 		break;
 	}
 }
 
-static unsigned int exynos_dp_get_lane_link_training(
-				struct exynos_dp_device *dp,
+static unsigned int analogix_dp_get_lane_link_training(
+				struct analogix_dp_device *dp,
 				int lane)
 {
 	u32 reg;
 
 	switch (lane) {
 	case 0:
-		reg = exynos_dp_get_lane0_link_training(dp);
+		reg = analogix_dp_get_lane0_link_training(dp);
 		break;
 	case 1:
-		reg = exynos_dp_get_lane1_link_training(dp);
+		reg = analogix_dp_get_lane1_link_training(dp);
 		break;
 	case 2:
-		reg = exynos_dp_get_lane2_link_training(dp);
+		reg = analogix_dp_get_lane2_link_training(dp);
 		break;
 	case 3:
-		reg = exynos_dp_get_lane3_link_training(dp);
+		reg = analogix_dp_get_lane3_link_training(dp);
 		break;
 	default:
 		WARN_ON(1);
@@ -457,15 +443,15 @@ static unsigned int exynos_dp_get_lane_link_training(
 	return reg;
 }
 
-static void exynos_dp_reduce_link_rate(struct exynos_dp_device *dp)
+static void analogix_dp_reduce_link_rate(struct analogix_dp_device *dp)
 {
-	exynos_dp_training_pattern_dis(dp);
-	exynos_dp_set_enhanced_mode(dp);
+	analogix_dp_training_pattern_dis(dp);
+	analogix_dp_set_enhanced_mode(dp);
 
 	dp->link_train.lt_state = FAILED;
 }
 
-static void exynos_dp_get_adjust_training_lane(struct exynos_dp_device *dp,
+static void analogix_dp_get_adjust_training_lane(struct analogix_dp_device *dp,
 					u8 adjust_request[2])
 {
 	int lane, lane_count;
@@ -473,9 +459,9 @@ static void exynos_dp_get_adjust_training_lane(struct exynos_dp_device *dp,
 
 	lane_count = dp->link_train.lane_count;
 	for (lane = 0; lane < lane_count; lane++) {
-		voltage_swing = exynos_dp_get_adjust_request_voltage(
+		voltage_swing = analogix_dp_get_adjust_request_voltage(
 						adjust_request, lane);
-		pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis(
+		pre_emphasis = analogix_dp_get_adjust_request_pre_emphasis(
 						adjust_request, lane);
 		training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) |
 				DPCD_PRE_EMPHASIS_SET(pre_emphasis);
@@ -489,7 +475,7 @@ static void exynos_dp_get_adjust_training_lane(struct exynos_dp_device *dp,
 	}
 }
 
-static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
+static int analogix_dp_process_clock_recovery(struct analogix_dp_device *dp)
 {
 	int lane, lane_count, retval;
 	u8 voltage_swing, pre_emphasis, training_lane;
@@ -499,21 +485,21 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
 
 	lane_count = dp->link_train.lane_count;
 
-	retval =  exynos_dp_read_bytes_from_dpcd(dp,
+	retval =  analogix_dp_read_bytes_from_dpcd(dp,
 			DP_LANE0_1_STATUS, 2, link_status);
 	if (retval)
 		return retval;
 
-	retval =  exynos_dp_read_bytes_from_dpcd(dp,
+	retval =  analogix_dp_read_bytes_from_dpcd(dp,
 			DP_ADJUST_REQUEST_LANE0_1, 2, adjust_request);
 	if (retval)
 		return retval;
 
-	if (exynos_dp_clock_recovery_ok(link_status, lane_count) == 0) {
+	if (analogix_dp_clock_recovery_ok(link_status, lane_count) == 0) {
 		/* set training pattern 2 for EQ */
-		exynos_dp_set_training_pattern(dp, TRAINING_PTN2);
+		analogix_dp_set_training_pattern(dp, TRAINING_PTN2);
 
-		retval = exynos_dp_write_byte_to_dpcd(dp,
+		retval = analogix_dp_write_byte_to_dpcd(dp,
 				DP_TRAINING_PATTERN_SET,
 				DP_LINK_SCRAMBLING_DISABLE |
 				DP_TRAINING_PATTERN_2);
@@ -524,11 +510,11 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
 		dp->link_train.lt_state = EQUALIZER_TRAINING;
 	} else {
 		for (lane = 0; lane < lane_count; lane++) {
-			training_lane = exynos_dp_get_lane_link_training(
+			training_lane = analogix_dp_get_lane_link_training(
 							dp, lane);
-			voltage_swing = exynos_dp_get_adjust_request_voltage(
+			voltage_swing = analogix_dp_get_adjust_request_voltage(
 							adjust_request, lane);
-			pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis(
+			pre_emphasis = analogix_dp_get_adjust_request_pre_emphasis(
 							adjust_request, lane);
 
 			if (DPCD_VOLTAGE_SWING_GET(training_lane) ==
@@ -543,19 +529,19 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
 				dev_err(dp->dev, "CR Max reached (%d,%d,%d)\n",
 					dp->link_train.cr_loop[lane],
 					voltage_swing, pre_emphasis);
-				exynos_dp_reduce_link_rate(dp);
+				analogix_dp_reduce_link_rate(dp);
 				return -EIO;
 			}
 		}
 	}
 
-	exynos_dp_get_adjust_training_lane(dp, adjust_request);
+	analogix_dp_get_adjust_training_lane(dp, adjust_request);
 
 	for (lane = 0; lane < lane_count; lane++)
-		exynos_dp_set_lane_link_training(dp,
+		analogix_dp_set_lane_link_training(dp,
 			dp->link_train.training_lane[lane], lane);
 
-	retval = exynos_dp_write_bytes_to_dpcd(dp,
+	retval = analogix_dp_write_bytes_to_dpcd(dp,
 			DP_TRAINING_LANE0_SET, lane_count,
 			dp->link_train.training_lane);
 	if (retval)
@@ -564,7 +550,7 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
 	return retval;
 }
 
-static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp)
+static int analogix_dp_process_equalizer_training(struct analogix_dp_device *dp)
 {
 	int lane, lane_count, retval;
 	u32 reg;
@@ -574,46 +560,46 @@ static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp)
 
 	lane_count = dp->link_train.lane_count;
 
-	retval = exynos_dp_read_bytes_from_dpcd(dp,
+	retval = analogix_dp_read_bytes_from_dpcd(dp,
 			DP_LANE0_1_STATUS, 2, link_status);
 	if (retval)
 		return retval;
 
-	if (exynos_dp_clock_recovery_ok(link_status, lane_count)) {
-		exynos_dp_reduce_link_rate(dp);
+	if (analogix_dp_clock_recovery_ok(link_status, lane_count)) {
+		analogix_dp_reduce_link_rate(dp);
 		return -EIO;
 	}
 
-	retval = exynos_dp_read_bytes_from_dpcd(dp,
+	retval = analogix_dp_read_bytes_from_dpcd(dp,
 			DP_ADJUST_REQUEST_LANE0_1, 2, adjust_request);
 	if (retval)
 		return retval;
 
-	retval = exynos_dp_read_byte_from_dpcd(dp,
+	retval = analogix_dp_read_byte_from_dpcd(dp,
 			DP_LANE_ALIGN_STATUS_UPDATED, &link_align);
 	if (retval)
 		return retval;
 
-	exynos_dp_get_adjust_training_lane(dp, adjust_request);
+	analogix_dp_get_adjust_training_lane(dp, adjust_request);
 
-	if (!exynos_dp_channel_eq_ok(link_status, link_align, lane_count)) {
+	if (!analogix_dp_channel_eq_ok(link_status, link_align, lane_count)) {
 		/* traing pattern Set to Normal */
-		exynos_dp_training_pattern_dis(dp);
+		analogix_dp_training_pattern_dis(dp);
 
 		dev_info(dp->dev, "Link Training success!\n");
 
-		exynos_dp_get_link_bandwidth(dp, &reg);
+		analogix_dp_get_link_bandwidth(dp, &reg);
 		dp->link_train.link_rate = reg;
 		dev_dbg(dp->dev, "final bandwidth = %.2x\n",
 			dp->link_train.link_rate);
 
-		exynos_dp_get_lane_count(dp, &reg);
+		analogix_dp_get_lane_count(dp, &reg);
 		dp->link_train.lane_count = reg;
 		dev_dbg(dp->dev, "final lane count = %.2x\n",
 			dp->link_train.lane_count);
 
 		/* set enhanced mode if available */
-		exynos_dp_set_enhanced_mode(dp);
+		analogix_dp_set_enhanced_mode(dp);
 		dp->link_train.lt_state = FINISHED;
 
 		return 0;
@@ -624,21 +610,21 @@ static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp)
 
 	if (dp->link_train.eq_loop > MAX_EQ_LOOP) {
 		dev_err(dp->dev, "EQ Max loop\n");
-		exynos_dp_reduce_link_rate(dp);
+		analogix_dp_reduce_link_rate(dp);
 		return -EIO;
 	}
 
 	for (lane = 0; lane < lane_count; lane++)
-		exynos_dp_set_lane_link_training(dp,
+		analogix_dp_set_lane_link_training(dp,
 			dp->link_train.training_lane[lane], lane);
 
-	retval = exynos_dp_write_bytes_to_dpcd(dp, DP_TRAINING_LANE0_SET,
+	retval = analogix_dp_write_bytes_to_dpcd(dp, DP_TRAINING_LANE0_SET,
 			lane_count, dp->link_train.training_lane);
 
 	return retval;
 }
 
-static void exynos_dp_get_max_rx_bandwidth(struct exynos_dp_device *dp,
+static void analogix_dp_get_max_rx_bandwidth(struct analogix_dp_device *dp,
 					u8 *bandwidth)
 {
 	u8 data;
@@ -647,11 +633,11 @@ static void exynos_dp_get_max_rx_bandwidth(struct exynos_dp_device *dp,
 	 * For DP rev.1.1, Maximum link rate of Main Link lanes
 	 * 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps
 	 */
-	exynos_dp_read_byte_from_dpcd(dp, DP_MAX_LINK_RATE, &data);
+	analogix_dp_read_byte_from_dpcd(dp, DP_MAX_LINK_RATE, &data);
 	*bandwidth = data;
 }
 
-static void exynos_dp_get_max_rx_lane_count(struct exynos_dp_device *dp,
+static void analogix_dp_get_max_rx_lane_count(struct analogix_dp_device *dp,
 					u8 *lane_count)
 {
 	u8 data;
@@ -660,11 +646,11 @@ static void exynos_dp_get_max_rx_lane_count(struct exynos_dp_device *dp,
 	 * For DP rev.1.1, Maximum number of Main Link lanes
 	 * 0x01 = 1 lane, 0x02 = 2 lanes, 0x04 = 4 lanes
 	 */
-	exynos_dp_read_byte_from_dpcd(dp, DP_MAX_LANE_COUNT, &data);
+	analogix_dp_read_byte_from_dpcd(dp, DP_MAX_LANE_COUNT, &data);
 	*lane_count = DPCD_MAX_LANE_COUNT(data);
 }
 
-static void exynos_dp_init_training(struct exynos_dp_device *dp,
+static void analogix_dp_init_training(struct analogix_dp_device *dp,
 			enum link_lane_count_type max_lane,
 			enum link_rate_type max_rate)
 {
@@ -672,11 +658,11 @@ static void exynos_dp_init_training(struct exynos_dp_device *dp,
 	 * MACRO_RST must be applied after the PLL_LOCK to avoid
 	 * the DP inter pair skew issue for at least 10 us
 	 */
-	exynos_dp_reset_macro(dp);
+	analogix_dp_reset_macro(dp);
 
 	/* Initialize by reading RX's DPCD */
-	exynos_dp_get_max_rx_bandwidth(dp, &dp->link_train.link_rate);
-	exynos_dp_get_max_rx_lane_count(dp, &dp->link_train.lane_count);
+	analogix_dp_get_max_rx_bandwidth(dp, &dp->link_train.link_rate);
+	analogix_dp_get_max_rx_lane_count(dp, &dp->link_train.lane_count);
 
 	if ((dp->link_train.link_rate != LINK_RATE_1_62GBPS) &&
 	   (dp->link_train.link_rate != LINK_RATE_2_70GBPS)) {
@@ -698,10 +684,10 @@ static void exynos_dp_init_training(struct exynos_dp_device *dp,
 		dp->link_train.link_rate = max_rate;
 
 	/* All DP analog module power up */
-	exynos_dp_set_analog_power_down(dp, POWER_ALL, 0);
+	analogix_dp_set_analog_power_down(dp, POWER_ALL, 0);
 }
 
-static int exynos_dp_sw_link_training(struct exynos_dp_device *dp)
+static int analogix_dp_sw_link_training(struct analogix_dp_device *dp)
 {
 	int retval = 0, training_finished = 0;
 
@@ -711,17 +697,17 @@ static int exynos_dp_sw_link_training(struct exynos_dp_device *dp)
 	while (!retval && !training_finished) {
 		switch (dp->link_train.lt_state) {
 		case START:
-			retval = exynos_dp_link_start(dp);
+			retval = analogix_dp_link_start(dp);
 			if (retval)
 				dev_err(dp->dev, "LT link start failed!\n");
 			break;
 		case CLOCK_RECOVERY:
-			retval = exynos_dp_process_clock_recovery(dp);
+			retval = analogix_dp_process_clock_recovery(dp);
 			if (retval)
 				dev_err(dp->dev, "LT CR failed!\n");
 			break;
 		case EQUALIZER_TRAINING:
-			retval = exynos_dp_process_equalizer_training(dp);
+			retval = analogix_dp_process_equalizer_training(dp);
 			if (retval)
 				dev_err(dp->dev, "LT EQ failed!\n");
 			break;
@@ -738,7 +724,7 @@ static int exynos_dp_sw_link_training(struct exynos_dp_device *dp)
 	return retval;
 }
 
-static int exynos_dp_set_link_train(struct exynos_dp_device *dp,
+static int analogix_dp_set_link_train(struct analogix_dp_device *dp,
 				u32 count,
 				u32 bwtype)
 {
@@ -746,8 +732,8 @@ static int exynos_dp_set_link_train(struct exynos_dp_device *dp,
 	int retval;
 
 	for (i = 0; i < DP_TIMEOUT_LOOP_COUNT; i++) {
-		exynos_dp_init_training(dp, count, bwtype);
-		retval = exynos_dp_sw_link_training(dp);
+		analogix_dp_init_training(dp, count, bwtype);
+		retval = analogix_dp_sw_link_training(dp);
 		if (retval == 0)
 			break;
 
@@ -757,24 +743,24 @@ static int exynos_dp_set_link_train(struct exynos_dp_device *dp,
 	return retval;
 }
 
-static int exynos_dp_config_video(struct exynos_dp_device *dp)
+static int analogix_dp_config_video(struct analogix_dp_device *dp)
 {
 	int retval = 0;
 	int timeout_loop = 0;
 	int done_count = 0;
 
-	exynos_dp_config_video_slave_mode(dp);
+	analogix_dp_config_video_slave_mode(dp);
 
-	exynos_dp_set_video_color_format(dp);
+	analogix_dp_set_video_color_format(dp);
 
-	if (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
+	if (analogix_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
 		dev_err(dp->dev, "PLL is not locked yet.\n");
 		return -EINVAL;
 	}
 
 	for (;;) {
 		timeout_loop++;
-		if (exynos_dp_is_slave_video_stream_clock_on(dp) == 0)
+		if (analogix_dp_is_slave_video_stream_clock_on(dp) == 0)
 			break;
 		if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) {
 			dev_err(dp->dev, "Timeout of video streamclk ok\n");
@@ -785,22 +771,22 @@ static int exynos_dp_config_video(struct exynos_dp_device *dp)
 	}
 
 	/* Set to use the register calculated M/N video */
-	exynos_dp_set_video_cr_mn(dp, CALCULATED_M, 0, 0);
+	analogix_dp_set_video_cr_mn(dp, CALCULATED_M, 0, 0);
 
 	/* For video bist, Video timing must be generated by register */
-	exynos_dp_set_video_timing_mode(dp, VIDEO_TIMING_FROM_CAPTURE);
+	analogix_dp_set_video_timing_mode(dp, VIDEO_TIMING_FROM_CAPTURE);
 
 	/* Disable video mute */
-	exynos_dp_enable_video_mute(dp, 0);
+	analogix_dp_enable_video_mute(dp, 0);
 
 	/* Configure video slave mode */
-	exynos_dp_enable_video_master(dp, 0);
+	analogix_dp_enable_video_master(dp, 0);
 
 	timeout_loop = 0;
 
 	for (;;) {
 		timeout_loop++;
-		if (exynos_dp_is_video_stream_on(dp) == 0) {
+		if (analogix_dp_is_video_stream_on(dp) == 0) {
 			done_count++;
 			if (done_count > 10)
 				break;
@@ -821,47 +807,47 @@ static int exynos_dp_config_video(struct exynos_dp_device *dp)
 	return retval;
 }
 
-static void exynos_dp_enable_scramble(struct exynos_dp_device *dp, bool enable)
+static void analogix_dp_enable_scramble(struct analogix_dp_device *dp, bool enable)
 {
 	u8 data;
 
 	if (enable) {
-		exynos_dp_enable_scrambling(dp);
+		analogix_dp_enable_scrambling(dp);
 
-		exynos_dp_read_byte_from_dpcd(dp,
+		analogix_dp_read_byte_from_dpcd(dp,
 			DP_TRAINING_PATTERN_SET,
 			&data);
-		exynos_dp_write_byte_to_dpcd(dp,
+		analogix_dp_write_byte_to_dpcd(dp,
 			DP_TRAINING_PATTERN_SET,
 			(u8)(data & ~DP_LINK_SCRAMBLING_DISABLE));
 	} else {
-		exynos_dp_disable_scrambling(dp);
+		analogix_dp_disable_scrambling(dp);
 
-		exynos_dp_read_byte_from_dpcd(dp,
+		analogix_dp_read_byte_from_dpcd(dp,
 			DP_TRAINING_PATTERN_SET,
 			&data);
-		exynos_dp_write_byte_to_dpcd(dp,
+		analogix_dp_write_byte_to_dpcd(dp,
 			DP_TRAINING_PATTERN_SET,
 			(u8)(data | DP_LINK_SCRAMBLING_DISABLE));
 	}
 }
 
-static irqreturn_t exynos_dp_irq_handler(int irq, void *arg)
+static irqreturn_t analogix_dp_irq_handler(int irq, void *arg)
 {
-	struct exynos_dp_device *dp = arg;
+	struct analogix_dp_device *dp = arg;
 
 	enum dp_irq_type irq_type;
 
-	irq_type = exynos_dp_get_irq_type(dp);
+	irq_type = analogix_dp_get_irq_type(dp);
 	switch (irq_type) {
 	case DP_IRQ_TYPE_HP_CABLE_IN:
 		dev_dbg(dp->dev, "Received irq - cable in\n");
 		schedule_work(&dp->hotplug_work);
-		exynos_dp_clear_hotplug_interrupts(dp);
+		analogix_dp_clear_hotplug_interrupts(dp);
 		break;
 	case DP_IRQ_TYPE_HP_CABLE_OUT:
 		dev_dbg(dp->dev, "Received irq - cable out\n");
-		exynos_dp_clear_hotplug_interrupts(dp);
+		analogix_dp_clear_hotplug_interrupts(dp);
 		break;
 	case DP_IRQ_TYPE_HP_CHANGE:
 		/*
@@ -870,7 +856,7 @@ static irqreturn_t exynos_dp_irq_handler(int irq, void *arg)
 		 * only handle cable changes.
 		 */
 		dev_dbg(dp->dev, "Received irq - hotplug change; ignoring.\n");
-		exynos_dp_clear_hotplug_interrupts(dp);
+		analogix_dp_clear_hotplug_interrupts(dp);
 		break;
 	default:
 		dev_err(dp->dev, "Received irq - unknown type!\n");
@@ -879,218 +865,203 @@ static irqreturn_t exynos_dp_irq_handler(int irq, void *arg)
 	return IRQ_HANDLED;
 }
 
-static void exynos_dp_hotplug(struct work_struct *work)
+static void analogix_dp_hotplug(struct work_struct *work)
 {
-	struct exynos_dp_device *dp;
+	struct analogix_dp_device *dp;
 
-	dp = container_of(work, struct exynos_dp_device, hotplug_work);
+	dp = container_of(work, struct analogix_dp_device, hotplug_work);
 
 	if (dp->drm_dev)
 		drm_helper_hpd_irq_event(dp->drm_dev);
 }
 
-static void exynos_dp_commit(struct drm_encoder *encoder)
+static void analogix_dp_commit(struct analogix_dp_device *dp)
 {
-	struct exynos_dp_device *dp = encoder_to_dp(encoder);
 	int ret;
 
 	/* Keep the panel disabled while we configure video */
-	if (dp->panel) {
-		if (drm_panel_disable(dp->panel))
+	if (dp->plat_data->panel) {
+		if (drm_panel_disable(dp->plat_data->panel))
 			DRM_ERROR("failed to disable the panel\n");
 	}
 
-	ret = exynos_dp_detect_hpd(dp);
+	ret = analogix_dp_detect_hpd(dp);
 	if (ret) {
 		/* Cable has been disconnected, we're done */
 		return;
 	}
 
-	ret = exynos_dp_handle_edid(dp);
+	ret = analogix_dp_handle_edid(dp);
 	if (ret) {
 		dev_err(dp->dev, "unable to handle edid\n");
 		return;
 	}
 
-	ret = exynos_dp_set_link_train(dp, dp->video_info->lane_count,
+	ret = analogix_dp_set_link_train(dp, dp->video_info->lane_count,
 					dp->video_info->link_rate);
 	if (ret) {
 		dev_err(dp->dev, "unable to do link train\n");
 		return;
 	}
 
-	exynos_dp_enable_scramble(dp, 1);
-	exynos_dp_enable_rx_to_enhanced_mode(dp, 1);
-	exynos_dp_enable_enhanced_mode(dp, 1);
+	analogix_dp_enable_scramble(dp, 1);
+	analogix_dp_enable_rx_to_enhanced_mode(dp, 1);
+	analogix_dp_enable_enhanced_mode(dp, 1);
 
-	exynos_dp_set_lane_count(dp, dp->video_info->lane_count);
-	exynos_dp_set_link_bandwidth(dp, dp->video_info->link_rate);
+	analogix_dp_set_lane_count(dp, dp->video_info->lane_count);
+	analogix_dp_set_link_bandwidth(dp, dp->video_info->link_rate);
 
-	exynos_dp_init_video(dp);
-	ret = exynos_dp_config_video(dp);
+	analogix_dp_init_video(dp);
+	ret = analogix_dp_config_video(dp);
 	if (ret)
 		dev_err(dp->dev, "unable to config video\n");
 
 	/* Safe to enable the panel now */
-	if (dp->panel) {
-		if (drm_panel_enable(dp->panel))
+	if (dp->plat_data->panel) {
+		if (drm_panel_enable(dp->plat_data->panel))
 			DRM_ERROR("failed to enable the panel\n");
 	}
 
 	/* Enable video */
-	exynos_dp_start_video(dp);
+	analogix_dp_start_video(dp);
 }
 
-static enum drm_connector_status exynos_dp_detect(
-				struct drm_connector *connector, bool force)
+int analogix_dp_get_modes(struct drm_connector *connector)
 {
-	return connector_status_connected;
-}
+	struct analogix_dp_device *dp = to_dp(connector);
+	int num_modes = 0;
 
-static void exynos_dp_connector_destroy(struct drm_connector *connector)
-{
-	drm_connector_unregister(connector);
-	drm_connector_cleanup(connector);
-}
+	if (dp->plat_data->panel)
+		num_modes += drm_panel_get_modes(dp->plat_data->panel);
 
-static const struct drm_connector_funcs exynos_dp_connector_funcs = {
-	.dpms = drm_atomic_helper_connector_dpms,
-	.fill_modes = drm_helper_probe_single_connector_modes,
-	.detect = exynos_dp_detect,
-	.destroy = exynos_dp_connector_destroy,
-	.reset = drm_atomic_helper_connector_reset,
-	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
-	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
-};
-
-static int exynos_dp_get_modes(struct drm_connector *connector)
-{
-	struct exynos_dp_device *dp = ctx_from_connector(connector);
-	struct drm_display_mode *mode;
-
-	if (dp->panel)
-		return drm_panel_get_modes(dp->panel);
-
-	mode = drm_mode_create(connector->dev);
-	if (!mode) {
-		DRM_ERROR("failed to create a new display mode.\n");
-		return 0;
-	}
-
-	drm_display_mode_from_videomode(&dp->priv.vm, mode);
-	mode->width_mm = dp->priv.width_mm;
-	mode->height_mm = dp->priv.height_mm;
-	connector->display_info.width_mm = mode->width_mm;
-	connector->display_info.height_mm = mode->height_mm;
-
-	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
-	drm_mode_set_name(mode);
-	drm_mode_probed_add(connector, mode);
+	if (dp->plat_data->get_modes)
+		num_modes += dp->plat_data->get_modes(dp->plat_data);
 
-	return 1;
+	return num_modes;
 }
 
-static struct drm_encoder *exynos_dp_best_encoder(
-			struct drm_connector *connector)
+static struct drm_encoder *
+analogix_dp_best_encoder(struct drm_connector *connector)
 {
-	struct exynos_dp_device *dp = ctx_from_connector(connector);
+	struct analogix_dp_device *dp = to_dp(connector);
 
-	return &dp->encoder;
+	return dp->encoder;
 }
 
-static const struct drm_connector_helper_funcs exynos_dp_connector_helper_funcs = {
-	.get_modes = exynos_dp_get_modes,
-	.best_encoder = exynos_dp_best_encoder,
+static const struct drm_connector_helper_funcs analogix_dp_connector_helper_funcs = {
+	.get_modes = analogix_dp_get_modes,
+	.best_encoder = analogix_dp_best_encoder,
 };
 
-/* returns the number of bridges attached */
-static int exynos_drm_attach_lcd_bridge(struct exynos_dp_device *dp,
-		struct drm_encoder *encoder)
+enum drm_connector_status
+analogix_dp_detect(struct drm_connector *connector, bool force)
 {
-	int ret;
-
-	encoder->bridge->next = dp->ptn_bridge;
-	dp->ptn_bridge->encoder = encoder;
-	ret = drm_bridge_attach(encoder->dev, dp->ptn_bridge);
-	if (ret) {
-		DRM_ERROR("Failed to attach bridge to drm\n");
-		return ret;
-	}
+	return connector_status_connected;
+}
 
-	return 0;
+static void analogix_dp_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
 }
 
-static int exynos_dp_bridge_attach(struct drm_bridge *bridge)
+static const struct drm_connector_funcs analogix_dp_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.detect = analogix_dp_detect,
+	.destroy = analogix_dp_connector_destroy,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int analogix_dp_bridge_attach(struct drm_bridge *bridge)
 {
-	struct exynos_dp_device *dp = bridge->driver_private;
-	struct drm_encoder *encoder = &dp->encoder;
+	struct analogix_dp_device *dp = bridge->driver_private;
+	struct drm_encoder *encoder = dp->encoder;
 	struct drm_connector *connector = &dp->connector;
 	int ret;
 
-	/* Pre-empt DP connector creation if there's a bridge */
-	if (dp->ptn_bridge) {
-		ret = exynos_drm_attach_lcd_bridge(dp, encoder);
-		if (!ret)
-			return 0;
+	if (!bridge->encoder) {
+		DRM_ERROR("Parent encoder object not found");
+		return -ENODEV;
 	}
 
 	connector->polled = DRM_CONNECTOR_POLL_HPD;
 
 	ret = drm_connector_init(dp->drm_dev, connector,
-			&exynos_dp_connector_funcs, DRM_MODE_CONNECTOR_eDP);
+				 &analogix_dp_connector_funcs,
+				 DRM_MODE_CONNECTOR_eDP);
 	if (ret) {
 		DRM_ERROR("Failed to initialize connector with drm\n");
 		return ret;
 	}
 
-	drm_connector_helper_add(connector, &exynos_dp_connector_helper_funcs);
-	drm_connector_register(connector);
+	drm_connector_helper_add(connector,
+				 &analogix_dp_connector_helper_funcs);
 	drm_mode_connector_attach_encoder(connector, encoder);
 
-	if (dp->panel)
-		ret = drm_panel_attach(dp->panel, &dp->connector);
+	/*
+	 * NOTE: the connector registration is implemented in analogix
+	 * platform driver, that to say connector would be exist after
+	 * plat_data->attch return, that's why we record the connector
+	 * point after plat attached.
+	 */
+	 if (dp->plat_data->attach) {
+		 ret = dp->plat_data->attach(dp->plat_data, bridge, connector);
+		 if (ret) {
+			 DRM_ERROR("Failed at platform attch func\n");
+			 return ret;
+		 }
+	}
 
-	return ret;
+	if (dp->plat_data->panel) {
+		ret = drm_panel_attach(dp->plat_data->panel, &dp->connector);
+		if (ret) {
+			DRM_ERROR("Failed to attach panel\n");
+			return ret;
+		}
+	}
+
+	return 0;
 }
 
-static void exynos_dp_bridge_enable(struct drm_bridge *bridge)
+static void analogix_dp_bridge_enable(struct drm_bridge *bridge)
 {
-	struct exynos_dp_device *dp = bridge->driver_private;
-	struct exynos_drm_crtc *crtc = dp_to_crtc(dp);
+	struct analogix_dp_device *dp = bridge->driver_private;
 
 	if (dp->dpms_mode == DRM_MODE_DPMS_ON)
 		return;
 
 	pm_runtime_get_sync(dp->dev);
 
-	if (dp->panel) {
-		if (drm_panel_prepare(dp->panel)) {
+	if (dp->plat_data->panel) {
+		if (drm_panel_prepare(dp->plat_data->panel)) {
 			DRM_ERROR("failed to setup the panel\n");
 			return;
 		}
 	}
 
-	if (crtc->ops->clock_enable)
-		crtc->ops->clock_enable(dp_to_crtc(dp), true);
+	if (dp->plat_data->power_on)
+		dp->plat_data->power_on(dp->plat_data);
 
 	phy_power_on(dp->phy);
-	exynos_dp_init_dp(dp);
+	analogix_dp_init_dp(dp);
 	enable_irq(dp->irq);
-	exynos_dp_commit(&dp->encoder);
+	analogix_dp_commit(dp);
 
 	dp->dpms_mode = DRM_MODE_DPMS_ON;
 }
 
-static void exynos_dp_bridge_disable(struct drm_bridge *bridge)
+static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
 {
-	struct exynos_dp_device *dp = bridge->driver_private;
-	struct exynos_drm_crtc *crtc = dp_to_crtc(dp);
+	struct analogix_dp_device *dp = bridge->driver_private;
 
 	if (dp->dpms_mode != DRM_MODE_DPMS_ON)
 		return;
 
-	if (dp->panel) {
-		if (drm_panel_disable(dp->panel)) {
+	if (dp->plat_data->panel) {
+		if (drm_panel_disable(dp->plat_data->panel)) {
 			DRM_ERROR("failed to disable the panel\n");
 			return;
 		}
@@ -1100,11 +1071,11 @@ static void exynos_dp_bridge_disable(struct drm_bridge *bridge)
 	flush_work(&dp->hotplug_work);
 	phy_power_off(dp->phy);
 
-	if (crtc->ops->clock_enable)
-		crtc->ops->clock_enable(dp_to_crtc(dp), false);
+	if (dp->plat_data->power_off)
+		dp->plat_data->power_off(dp->plat_data);
 
-	if (dp->panel) {
-		if (drm_panel_unprepare(dp->panel))
+	if (dp->plat_data->panel) {
+		if (drm_panel_unprepare(dp->plat_data->panel))
 			DRM_ERROR("failed to turnoff the panel\n");
 	}
 
@@ -1113,23 +1084,22 @@ static void exynos_dp_bridge_disable(struct drm_bridge *bridge)
 	dp->dpms_mode = DRM_MODE_DPMS_OFF;
 }
 
-static void exynos_dp_bridge_nop(struct drm_bridge *bridge)
+static void analogix_dp_bridge_nop(struct drm_bridge *bridge)
 {
 	/* do nothing */
 }
 
-static const struct drm_bridge_funcs exynos_dp_bridge_funcs = {
-	.enable = exynos_dp_bridge_enable,
-	.disable = exynos_dp_bridge_disable,
-	.pre_enable = exynos_dp_bridge_nop,
-	.post_disable = exynos_dp_bridge_nop,
-	.attach = exynos_dp_bridge_attach,
+static const struct drm_bridge_funcs analogix_dp_bridge_funcs = {
+	.enable = analogix_dp_bridge_enable,
+	.disable = analogix_dp_bridge_disable,
+	.pre_enable = analogix_dp_bridge_nop,
+	.post_disable = analogix_dp_bridge_nop,
+	.attach = analogix_dp_bridge_attach,
 };
 
-static int exynos_dp_create_connector(struct drm_encoder *encoder)
+static int analogix_dp_create_bridge(struct drm_device *drm_dev,
+				     struct analogix_dp_device *dp)
 {
-	struct exynos_dp_device *dp = encoder_to_dp(encoder);
-	struct drm_device *drm_dev = dp->drm_dev;
 	struct drm_bridge *bridge;
 	int ret;
 
@@ -1141,10 +1111,10 @@ static int exynos_dp_create_connector(struct drm_encoder *encoder)
 
 	dp->bridge = bridge;
 
-	encoder->bridge = bridge;
+	dp->encoder->bridge = bridge;
 	bridge->driver_private = dp;
-	bridge->encoder = encoder;
-	bridge->funcs = &exynos_dp_bridge_funcs;
+	bridge->encoder = dp->encoder;
+	bridge->funcs = &analogix_dp_bridge_funcs;
 
 	ret = drm_bridge_attach(drm_dev, bridge);
 	if (ret) {
@@ -1155,39 +1125,7 @@ static int exynos_dp_create_connector(struct drm_encoder *encoder)
 	return 0;
 }
 
-static bool exynos_dp_mode_fixup(struct drm_encoder *encoder,
-				 const struct drm_display_mode *mode,
-				 struct drm_display_mode *adjusted_mode)
-{
-	return true;
-}
-
-static void exynos_dp_mode_set(struct drm_encoder *encoder,
-			       struct drm_display_mode *mode,
-			       struct drm_display_mode *adjusted_mode)
-{
-}
-
-static void exynos_dp_enable(struct drm_encoder *encoder)
-{
-}
-
-static void exynos_dp_disable(struct drm_encoder *encoder)
-{
-}
-
-static const struct drm_encoder_helper_funcs exynos_dp_encoder_helper_funcs = {
-	.mode_fixup = exynos_dp_mode_fixup,
-	.mode_set = exynos_dp_mode_set,
-	.enable = exynos_dp_enable,
-	.disable = exynos_dp_disable,
-};
-
-static const struct drm_encoder_funcs exynos_dp_encoder_funcs = {
-	.destroy = drm_encoder_cleanup,
-};
-
-static struct video_info *exynos_dp_dt_parse_pdata(struct device *dev)
+static struct video_info *analogix_dp_dt_parse_pdata(struct device *dev)
 {
 	struct device_node *dp_node = dev->of_node;
 	struct video_info *dp_video_config;
@@ -1245,33 +1183,37 @@ static struct video_info *exynos_dp_dt_parse_pdata(struct device *dev)
 	return dp_video_config;
 }
 
-static int exynos_dp_dt_parse_panel(struct exynos_dp_device *dp)
+int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev,
+		     struct analogix_dp_plat_data *plat_data)
 {
+	struct platform_device *pdev = to_platform_device(dev);
+	struct analogix_dp_device *dp;
+	struct resource *res;
+	unsigned int irq_flags;
 	int ret;
 
-	ret = of_get_videomode(dp->dev->of_node, &dp->priv.vm,
-			OF_USE_NATIVE_MODE);
-	if (ret) {
-		DRM_ERROR("failed: of_get_videomode() : %d\n", ret);
-		return ret;
+	if (!plat_data) {
+		dev_err(dev, "Invalided input plat_data\n");
+		return -EINVAL;
 	}
-	return 0;
-}
 
-static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
-{
-	struct exynos_dp_device *dp = dev_get_drvdata(dev);
-	struct platform_device *pdev = to_platform_device(dev);
-	struct drm_device *drm_dev = data;
-	struct drm_encoder *encoder = &dp->encoder;
-	struct resource *res;
-	unsigned int irq_flags;
-	int pipe, ret = 0;
+	dp = devm_kzalloc(dev, sizeof(struct analogix_dp_device), GFP_KERNEL);
+	if (!dp)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, dp);
 
 	dp->dev = &pdev->dev;
 	dp->dpms_mode = DRM_MODE_DPMS_OFF;
 
-	dp->video_info = exynos_dp_dt_parse_pdata(&pdev->dev);
+	/*
+	 * platform dp driver need containor_of the plat_data to get
+	 * the driver private data, so we need to store the point of
+	 * plat_data, not the context of plat_data.
+	 */
+	dp->plat_data = plat_data;
+
+	dp->video_info = analogix_dp_dt_parse_pdata(&pdev->dev);
 	if (IS_ERR(dp->video_info))
 		return PTR_ERR(dp->video_info);
 
@@ -1291,12 +1233,6 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
 		}
 	}
 
-	if (!dp->panel && !dp->ptn_bridge) {
-		ret = exynos_dp_dt_parse_panel(dp);
-		if (ret)
-			return ret;
-	}
-
 	dp->clock = devm_clk_get(&pdev->dev, "dp");
 	if (IS_ERR(dp->clock)) {
 		dev_err(&pdev->dev, "failed to get clock\n");
@@ -1311,7 +1247,10 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
 	if (IS_ERR(dp->reg_base))
 		return PTR_ERR(dp->reg_base);
 
-	dp->hpd_gpio = of_get_named_gpio(dev->of_node, "samsung,hpd-gpio", 0);
+	dp->hpd_gpio = of_get_named_gpio(dev->of_node, "hpd-gpios", 0);
+	if (!gpio_is_valid(dp->hpd_gpio))
+		dp->hpd_gpio = of_get_named_gpio(dev->of_node,
+						 "samsung,hpd-gpio", 0);
 
 	if (gpio_is_valid(dp->hpd_gpio)) {
 		/*
@@ -1340,143 +1279,60 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
 		return -ENODEV;
 	}
 
-	INIT_WORK(&dp->hotplug_work, exynos_dp_hotplug);
+	INIT_WORK(&dp->hotplug_work, analogix_dp_hotplug);
 
-	ret = devm_request_irq(&pdev->dev, dp->irq, exynos_dp_irq_handler,
-			irq_flags, "exynos-dp", dp);
+	pm_runtime_enable(dev);
+
+	ret = devm_request_irq(&pdev->dev, dp->irq, analogix_dp_irq_handler,
+			irq_flags, "analogix-dp", dp);
 	if (ret) {
 		dev_err(&pdev->dev, "failed to request irq\n");
-		return ret;
+		goto err_disable_pm_runtime;
 	}
 	disable_irq(dp->irq);
 
 	dp->drm_dev = drm_dev;
+	dp->encoder = dp->plat_data->encoder;
 
-	pipe = exynos_drm_crtc_get_pipe_from_type(drm_dev,
-						  EXYNOS_DISPLAY_TYPE_LCD);
-	if (pipe < 0)
-		return pipe;
-
-	encoder->possible_crtcs = 1 << pipe;
-
-	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
-
-	drm_encoder_init(drm_dev, encoder, &exynos_dp_encoder_funcs,
-			 DRM_MODE_ENCODER_TMDS, NULL);
-
-	drm_encoder_helper_add(encoder, &exynos_dp_encoder_helper_funcs);
-
-	ret = exynos_dp_create_connector(encoder);
+	ret = analogix_dp_create_bridge(drm_dev, dp);
 	if (ret) {
-		DRM_ERROR("failed to create connector ret = %d\n", ret);
-		drm_encoder_cleanup(encoder);
-		return ret;
+		DRM_ERROR("failed to create bridge (%d)\n", ret);
+		drm_encoder_cleanup(dp->encoder);
+		goto err_disable_pm_runtime;
 	}
 
 	return 0;
-}
-
-static void exynos_dp_unbind(struct device *dev, struct device *master,
-				void *data)
-{
-	struct exynos_dp_device *dp = dev_get_drvdata(dev);
-
-	exynos_dp_disable(&dp->encoder);
-}
-
-static const struct component_ops exynos_dp_ops = {
-	.bind	= exynos_dp_bind,
-	.unbind	= exynos_dp_unbind,
-};
-
-static int exynos_dp_probe(struct platform_device *pdev)
-{
-	struct device *dev = &pdev->dev;
-	struct device_node *panel_node = NULL, *bridge_node, *endpoint = NULL;
-	struct exynos_dp_device *dp;
-	int ret;
-
-	dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device),
-				GFP_KERNEL);
-	if (!dp)
-		return -ENOMEM;
-
-	platform_set_drvdata(pdev, dp);
-
-	/* This is for the backward compatibility. */
-	panel_node = of_parse_phandle(dev->of_node, "panel", 0);
-	if (panel_node) {
-		dp->panel = of_drm_find_panel(panel_node);
-		of_node_put(panel_node);
-		if (!dp->panel)
-			return -EPROBE_DEFER;
-	} else {
-		endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
-		if (endpoint) {
-			panel_node = of_graph_get_remote_port_parent(endpoint);
-			if (panel_node) {
-				dp->panel = of_drm_find_panel(panel_node);
-				of_node_put(panel_node);
-				if (!dp->panel)
-					return -EPROBE_DEFER;
-			} else {
-				DRM_ERROR("no port node for panel device.\n");
-				return -EINVAL;
-			}
-		}
-	}
-
-	if (endpoint)
-		goto out;
-
-	endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
-	if (endpoint) {
-		bridge_node = of_graph_get_remote_port_parent(endpoint);
-		if (bridge_node) {
-			dp->ptn_bridge = of_drm_find_bridge(bridge_node);
-			of_node_put(bridge_node);
-			if (!dp->ptn_bridge)
-				return -EPROBE_DEFER;
-		} else
-			return -EPROBE_DEFER;
-	}
-
-out:
-	pm_runtime_enable(dev);
-
-	ret = component_add(&pdev->dev, &exynos_dp_ops);
-	if (ret)
-		goto err_disable_pm_runtime;
-
-	return ret;
 
 err_disable_pm_runtime:
 	pm_runtime_disable(dev);
 
 	return ret;
 }
+EXPORT_SYMBOL_GPL(analogix_dp_bind);
 
-static int exynos_dp_remove(struct platform_device *pdev)
+void analogix_dp_unbind(struct device *dev, struct device *master,
+			void *data)
 {
-	pm_runtime_disable(&pdev->dev);
-	component_del(&pdev->dev, &exynos_dp_ops);
+	struct analogix_dp_device *dp = dev_get_drvdata(dev);
 
-	return 0;
+	analogix_dp_bridge_disable(dp->bridge);
+	pm_runtime_disable(dev);
 }
+EXPORT_SYMBOL_GPL(analogix_dp_unbind);
 
 #ifdef CONFIG_PM
-static int exynos_dp_suspend(struct device *dev)
+int analogix_dp_suspend(struct device *dev)
 {
-	struct exynos_dp_device *dp = dev_get_drvdata(dev);
+	struct analogix_dp_device *dp = dev_get_drvdata(dev);
 
 	clk_disable_unprepare(dp->clock);
-
 	return 0;
 }
+EXPORT_SYMBOL_GPL(analogix_dp_suspend);
 
-static int exynos_dp_resume(struct device *dev)
+int analogix_dp_resume(struct device *dev)
 {
-	struct exynos_dp_device *dp = dev_get_drvdata(dev);
+	struct analogix_dp_device *dp = dev_get_drvdata(dev);
 	int ret;
 
 	ret = clk_prepare_enable(dp->clock);
@@ -1487,29 +1343,9 @@ static int exynos_dp_resume(struct device *dev)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(analogix_dp_resume);
 #endif
 
-static const struct dev_pm_ops exynos_dp_pm_ops = {
-	SET_RUNTIME_PM_OPS(exynos_dp_suspend, exynos_dp_resume, NULL)
-};
-
-static const struct of_device_id exynos_dp_match[] = {
-	{ .compatible = "samsung,exynos5-dp" },
-	{},
-};
-MODULE_DEVICE_TABLE(of, exynos_dp_match);
-
-struct platform_driver dp_driver = {
-	.probe		= exynos_dp_probe,
-	.remove		= exynos_dp_remove,
-	.driver		= {
-		.name	= "exynos-dp",
-		.owner	= THIS_MODULE,
-		.pm	= &exynos_dp_pm_ops,
-		.of_match_table = exynos_dp_match,
-	},
-};
-
 MODULE_AUTHOR("Jingoo Han <jg1.han at samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC DP Driver");
+MODULE_DESCRIPTION("Analogix DP Core Driver");
 MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
new file mode 100644
index 0000000..70724ec
--- /dev/null
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
@@ -0,0 +1,277 @@
+/*
+ * Header file for Analogix DP (Display Port) core interface driver.
+ *
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ * Author: Jingoo Han <jg1.han at samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef _ANALOGIX_DP_CORE_H
+#define _ANALOGIX_DP_CORE_H
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_dp_helper.h>
+
+#define DP_TIMEOUT_LOOP_COUNT		100
+#define MAX_CR_LOOP			5
+#define MAX_EQ_LOOP			5
+
+enum link_rate_type {
+	LINK_RATE_1_62GBPS = 0x06,
+	LINK_RATE_2_70GBPS = 0x0a
+};
+
+enum link_lane_count_type {
+	LANE_COUNT1 = 1,
+	LANE_COUNT2 = 2,
+	LANE_COUNT4 = 4
+};
+
+enum link_training_state {
+	START,
+	CLOCK_RECOVERY,
+	EQUALIZER_TRAINING,
+	FINISHED,
+	FAILED
+};
+
+enum voltage_swing_level {
+	VOLTAGE_LEVEL_0,
+	VOLTAGE_LEVEL_1,
+	VOLTAGE_LEVEL_2,
+	VOLTAGE_LEVEL_3,
+};
+
+enum pre_emphasis_level {
+	PRE_EMPHASIS_LEVEL_0,
+	PRE_EMPHASIS_LEVEL_1,
+	PRE_EMPHASIS_LEVEL_2,
+	PRE_EMPHASIS_LEVEL_3,
+};
+
+enum pattern_set {
+	PRBS7,
+	D10_2,
+	TRAINING_PTN1,
+	TRAINING_PTN2,
+	DP_NONE
+};
+
+enum color_space {
+	COLOR_RGB,
+	COLOR_YCBCR422,
+	COLOR_YCBCR444
+};
+
+enum color_depth {
+	COLOR_6,
+	COLOR_8,
+	COLOR_10,
+	COLOR_12
+};
+
+enum color_coefficient {
+	COLOR_YCBCR601,
+	COLOR_YCBCR709
+};
+
+enum dynamic_range {
+	VESA,
+	CEA
+};
+
+enum pll_status {
+	PLL_UNLOCKED,
+	PLL_LOCKED
+};
+
+enum clock_recovery_m_value_type {
+	CALCULATED_M,
+	REGISTER_M
+};
+
+enum video_timing_recognition_type {
+	VIDEO_TIMING_FROM_CAPTURE,
+	VIDEO_TIMING_FROM_REGISTER
+};
+
+enum analog_power_block {
+	AUX_BLOCK,
+	CH0_BLOCK,
+	CH1_BLOCK,
+	CH2_BLOCK,
+	CH3_BLOCK,
+	ANALOG_TOTAL,
+	POWER_ALL
+};
+
+enum dp_irq_type {
+	DP_IRQ_TYPE_HP_CABLE_IN,
+	DP_IRQ_TYPE_HP_CABLE_OUT,
+	DP_IRQ_TYPE_HP_CHANGE,
+	DP_IRQ_TYPE_UNKNOWN,
+};
+
+struct video_info {
+	char *name;
+
+	bool h_sync_polarity;
+	bool v_sync_polarity;
+	bool interlaced;
+
+	enum color_space color_space;
+	enum dynamic_range dynamic_range;
+	enum color_coefficient ycbcr_coeff;
+	enum color_depth color_depth;
+
+	enum link_rate_type link_rate;
+	enum link_lane_count_type lane_count;
+};
+
+struct link_train {
+	int eq_loop;
+	int cr_loop[4];
+
+	u8 link_rate;
+	u8 lane_count;
+	u8 training_lane[4];
+
+	enum link_training_state lt_state;
+};
+
+struct analogix_dp_device {
+	struct drm_encoder	*encoder;
+	struct drm_connector	connector;
+	struct device		*dev;
+	struct drm_device	*drm_dev;
+	struct drm_bridge	*bridge;
+	struct clk		*clock;
+	unsigned int		irq;
+	void __iomem		*reg_base;
+
+	struct video_info	*video_info;
+	struct link_train	link_train;
+	struct work_struct	hotplug_work;
+	struct phy		*phy;
+	int			dpms_mode;
+	int			hpd_gpio;
+
+	struct analogix_dp_plat_data *plat_data;
+};
+
+/* analogix_dp_reg.c */
+void analogix_dp_enable_video_mute(struct analogix_dp_device *dp, bool enable);
+void analogix_dp_stop_video(struct analogix_dp_device *dp);
+void analogix_dp_lane_swap(struct analogix_dp_device *dp, bool enable);
+void analogix_dp_init_analog_param(struct analogix_dp_device *dp);
+void analogix_dp_init_interrupt(struct analogix_dp_device *dp);
+void analogix_dp_reset(struct analogix_dp_device *dp);
+void analogix_dp_swreset(struct analogix_dp_device *dp);
+void analogix_dp_config_interrupt(struct analogix_dp_device *dp);
+enum pll_status analogix_dp_get_pll_lock_status(struct analogix_dp_device *dp);
+void analogix_dp_set_pll_power_down(struct analogix_dp_device *dp, bool enable);
+void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp,
+				       enum analog_power_block block,
+				       bool enable);
+void analogix_dp_init_analog_func(struct analogix_dp_device *dp);
+void analogix_dp_init_hpd(struct analogix_dp_device *dp);
+enum dp_irq_type analogix_dp_get_irq_type(struct analogix_dp_device *dp);
+void analogix_dp_clear_hotplug_interrupts(struct analogix_dp_device *dp);
+void analogix_dp_reset_aux(struct analogix_dp_device *dp);
+void analogix_dp_init_aux(struct analogix_dp_device *dp);
+int analogix_dp_get_plug_in_status(struct analogix_dp_device *dp);
+void analogix_dp_enable_sw_function(struct analogix_dp_device *dp);
+int analogix_dp_start_aux_transaction(struct analogix_dp_device *dp);
+int analogix_dp_write_byte_to_dpcd(struct analogix_dp_device *dp,
+				unsigned int reg_addr,
+				unsigned char data);
+int analogix_dp_read_byte_from_dpcd(struct analogix_dp_device *dp,
+				unsigned int reg_addr,
+				unsigned char *data);
+int analogix_dp_write_bytes_to_dpcd(struct analogix_dp_device *dp,
+				unsigned int reg_addr,
+				unsigned int count,
+				unsigned char data[]);
+int analogix_dp_read_bytes_from_dpcd(struct analogix_dp_device *dp,
+				unsigned int reg_addr,
+				unsigned int count,
+				unsigned char data[]);
+int analogix_dp_select_i2c_device(struct analogix_dp_device *dp,
+				unsigned int device_addr,
+				unsigned int reg_addr);
+int analogix_dp_read_byte_from_i2c(struct analogix_dp_device *dp,
+				unsigned int device_addr,
+				unsigned int reg_addr,
+				unsigned int *data);
+int analogix_dp_read_bytes_from_i2c(struct analogix_dp_device *dp,
+				unsigned int device_addr,
+				unsigned int reg_addr,
+				unsigned int count,
+				unsigned char edid[]);
+void analogix_dp_set_link_bandwidth(struct analogix_dp_device *dp, u32 bwtype);
+void analogix_dp_get_link_bandwidth(struct analogix_dp_device *dp, u32 *bwtype);
+void analogix_dp_set_lane_count(struct analogix_dp_device *dp, u32 count);
+void analogix_dp_get_lane_count(struct analogix_dp_device *dp, u32 *count);
+void analogix_dp_enable_enhanced_mode(struct analogix_dp_device *dp, bool enable);
+void analogix_dp_set_training_pattern(struct analogix_dp_device *dp,
+				enum pattern_set pattern);
+void analogix_dp_set_lane0_pre_emphasis(struct analogix_dp_device *dp, u32 level);
+void analogix_dp_set_lane1_pre_emphasis(struct analogix_dp_device *dp, u32 level);
+void analogix_dp_set_lane2_pre_emphasis(struct analogix_dp_device *dp, u32 level);
+void analogix_dp_set_lane3_pre_emphasis(struct analogix_dp_device *dp, u32 level);
+void analogix_dp_set_lane0_link_training(struct analogix_dp_device *dp,
+				u32 training_lane);
+void analogix_dp_set_lane1_link_training(struct analogix_dp_device *dp,
+				u32 training_lane);
+void analogix_dp_set_lane2_link_training(struct analogix_dp_device *dp,
+				u32 training_lane);
+void analogix_dp_set_lane3_link_training(struct analogix_dp_device *dp,
+				u32 training_lane);
+u32 analogix_dp_get_lane0_link_training(struct analogix_dp_device *dp);
+u32 analogix_dp_get_lane1_link_training(struct analogix_dp_device *dp);
+u32 analogix_dp_get_lane2_link_training(struct analogix_dp_device *dp);
+u32 analogix_dp_get_lane3_link_training(struct analogix_dp_device *dp);
+void analogix_dp_reset_macro(struct analogix_dp_device *dp);
+void analogix_dp_init_video(struct analogix_dp_device *dp);
+
+void analogix_dp_set_video_color_format(struct analogix_dp_device *dp);
+int analogix_dp_is_slave_video_stream_clock_on(struct analogix_dp_device *dp);
+void analogix_dp_set_video_cr_mn(struct analogix_dp_device *dp,
+			enum clock_recovery_m_value_type type,
+			u32 m_value,
+			u32 n_value);
+void analogix_dp_set_video_timing_mode(struct analogix_dp_device *dp, u32 type);
+void analogix_dp_enable_video_master(struct analogix_dp_device *dp, bool enable);
+void analogix_dp_start_video(struct analogix_dp_device *dp);
+int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp);
+void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp);
+void analogix_dp_enable_scrambling(struct analogix_dp_device *dp);
+void analogix_dp_disable_scrambling(struct analogix_dp_device *dp);
+
+/* I2C EDID Chip ID, Slave Address */
+#define I2C_EDID_DEVICE_ADDR			0x50
+#define I2C_E_EDID_DEVICE_ADDR			0x30
+
+#define EDID_BLOCK_LENGTH			0x80
+#define EDID_HEADER_PATTERN			0x00
+#define EDID_EXTENSION_FLAG			0x7e
+#define EDID_CHECKSUM				0x7f
+
+/* DP_MAX_LANE_COUNT */
+#define DPCD_ENHANCED_FRAME_CAP(x)		(((x) >> 7) & 0x1)
+#define DPCD_MAX_LANE_COUNT(x)			((x) & 0x1f)
+
+/* DP_LANE_COUNT_SET */
+#define DPCD_LANE_COUNT_SET(x)			((x) & 0x1f)
+
+/* DP_TRAINING_LANE0_SET */
+#define DPCD_PRE_EMPHASIS_SET(x)		(((x) & 0x3) << 3)
+#define DPCD_PRE_EMPHASIS_GET(x)		(((x) >> 3) & 0x3)
+#define DPCD_VOLTAGE_SWING_SET(x)		(((x) & 0x3) << 0)
+#define DPCD_VOLTAGE_SWING_GET(x)		(((x) >> 0) & 0x3)
+
+#endif /* _ANALOGIX_DP_CORE_H */
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
new file mode 100644
index 0000000..442cc66
--- /dev/null
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
@@ -0,0 +1,1263 @@
+/*
+ * Analogix DP (Display port) core register interface driver.
+ *
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ * Author: Jingoo Han <jg1.han at samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+
+#include "analogix_dp_core.h"
+#include "analogix_dp_reg.h"
+
+#define COMMON_INT_MASK_1	0
+#define COMMON_INT_MASK_2	0
+#define COMMON_INT_MASK_3	0
+#define COMMON_INT_MASK_4	(HOTPLUG_CHG | HPD_LOST | PLUG)
+#define INT_STA_MASK		INT_HPD
+
+void analogix_dp_enable_video_mute(struct analogix_dp_device *dp, bool enable)
+{
+	u32 reg;
+
+	if (enable) {
+		reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_1);
+		reg |= HDCP_VIDEO_MUTE;
+		writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_1);
+	} else {
+		reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_1);
+		reg &= ~HDCP_VIDEO_MUTE;
+		writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_1);
+	}
+}
+
+void analogix_dp_stop_video(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_1);
+	reg &= ~VIDEO_EN;
+	writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_1);
+}
+
+void analogix_dp_lane_swap(struct analogix_dp_device *dp, bool enable)
+{
+	u32 reg;
+
+	if (enable)
+		reg = LANE3_MAP_LOGIC_LANE_0 | LANE2_MAP_LOGIC_LANE_1 |
+			LANE1_MAP_LOGIC_LANE_2 | LANE0_MAP_LOGIC_LANE_3;
+	else
+		reg = LANE3_MAP_LOGIC_LANE_3 | LANE2_MAP_LOGIC_LANE_2 |
+			LANE1_MAP_LOGIC_LANE_1 | LANE0_MAP_LOGIC_LANE_0;
+
+	writel(reg, dp->reg_base + ANALOGIX_DP_LANE_MAP);
+}
+
+void analogix_dp_init_analog_param(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = TX_TERMINAL_CTRL_50_OHM;
+	writel(reg, dp->reg_base + ANALOGIX_DP_ANALOG_CTL_1);
+
+	reg = SEL_24M | TX_DVDD_BIT_1_0625V;
+	writel(reg, dp->reg_base + ANALOGIX_DP_ANALOG_CTL_2);
+
+	reg = DRIVE_DVDD_BIT_1_0625V | VCO_BIT_600_MICRO;
+	writel(reg, dp->reg_base + ANALOGIX_DP_ANALOG_CTL_3);
+
+	reg = PD_RING_OSC | AUX_TERMINAL_CTRL_50_OHM |
+		TX_CUR1_2X | TX_CUR_16_MA;
+	writel(reg, dp->reg_base + ANALOGIX_DP_PLL_FILTER_CTL_1);
+
+	reg = CH3_AMP_400_MV | CH2_AMP_400_MV |
+		CH1_AMP_400_MV | CH0_AMP_400_MV;
+	writel(reg, dp->reg_base + ANALOGIX_DP_TX_AMP_TUNING_CTL);
+}
+
+void analogix_dp_init_interrupt(struct analogix_dp_device *dp)
+{
+	/* Set interrupt pin assertion polarity as high */
+	writel(INT_POL1 | INT_POL0, dp->reg_base + ANALOGIX_DP_INT_CTL);
+
+	/* Clear pending regisers */
+	writel(0xff, dp->reg_base + ANALOGIX_DP_COMMON_INT_STA_1);
+	writel(0x4f, dp->reg_base + ANALOGIX_DP_COMMON_INT_STA_2);
+	writel(0xe0, dp->reg_base + ANALOGIX_DP_COMMON_INT_STA_3);
+	writel(0xe7, dp->reg_base + ANALOGIX_DP_COMMON_INT_STA_4);
+	writel(0x63, dp->reg_base + ANALOGIX_DP_INT_STA);
+
+	/* 0:mask,1: unmask */
+	writel(0x00, dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_1);
+	writel(0x00, dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_2);
+	writel(0x00, dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_3);
+	writel(0x00, dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_4);
+	writel(0x00, dp->reg_base + ANALOGIX_DP_INT_STA_MASK);
+}
+
+void analogix_dp_reset(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	analogix_dp_stop_video(dp);
+	analogix_dp_enable_video_mute(dp, 0);
+
+	reg = MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N |
+		AUD_FIFO_FUNC_EN_N | AUD_FUNC_EN_N |
+		HDCP_FUNC_EN_N | SW_FUNC_EN_N;
+	writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_1);
+
+	reg = SSC_FUNC_EN_N | AUX_FUNC_EN_N |
+		SERDES_FIFO_FUNC_EN_N |
+		LS_CLK_DOMAIN_FUNC_EN_N;
+	writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_2);
+
+	usleep_range(20, 30);
+
+	analogix_dp_lane_swap(dp, 0);
+
+	writel(0x0, dp->reg_base + ANALOGIX_DP_SYS_CTL_1);
+	writel(0x40, dp->reg_base + ANALOGIX_DP_SYS_CTL_2);
+	writel(0x0, dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
+	writel(0x0, dp->reg_base + ANALOGIX_DP_SYS_CTL_4);
+
+	writel(0x0, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+	writel(0x0, dp->reg_base + ANALOGIX_DP_HDCP_CTL);
+
+	writel(0x5e, dp->reg_base + ANALOGIX_DP_HPD_DEGLITCH_L);
+	writel(0x1a, dp->reg_base + ANALOGIX_DP_HPD_DEGLITCH_H);
+
+	writel(0x10, dp->reg_base + ANALOGIX_DP_LINK_DEBUG_CTL);
+
+	writel(0x0, dp->reg_base + ANALOGIX_DP_PHY_TEST);
+
+	writel(0x0, dp->reg_base + ANALOGIX_DP_VIDEO_FIFO_THRD);
+	writel(0x20, dp->reg_base + ANALOGIX_DP_AUDIO_MARGIN);
+
+	writel(0x4, dp->reg_base + ANALOGIX_DP_M_VID_GEN_FILTER_TH);
+	writel(0x2, dp->reg_base + ANALOGIX_DP_M_AUD_GEN_FILTER_TH);
+
+	writel(0x00000101, dp->reg_base + ANALOGIX_DP_SOC_GENERAL_CTL);
+}
+
+void analogix_dp_swreset(struct analogix_dp_device *dp)
+{
+	writel(RESET_DP_TX, dp->reg_base + ANALOGIX_DP_TX_SW_RESET);
+}
+
+void analogix_dp_config_interrupt(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	/* 0: mask, 1: unmask */
+	reg = COMMON_INT_MASK_1;
+	writel(reg, dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_1);
+
+	reg = COMMON_INT_MASK_2;
+	writel(reg, dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_2);
+
+	reg = COMMON_INT_MASK_3;
+	writel(reg, dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_3);
+
+	reg = COMMON_INT_MASK_4;
+	writel(reg, dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_4);
+
+	reg = INT_STA_MASK;
+	writel(reg, dp->reg_base + ANALOGIX_DP_INT_STA_MASK);
+}
+
+enum pll_status analogix_dp_get_pll_lock_status(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_DEBUG_CTL);
+	if (reg & PLL_LOCK)
+		return PLL_LOCKED;
+	else
+		return PLL_UNLOCKED;
+}
+
+void analogix_dp_set_pll_power_down(struct analogix_dp_device *dp, bool enable)
+{
+	u32 reg;
+
+	if (enable) {
+		reg = readl(dp->reg_base + ANALOGIX_DP_PLL_CTL);
+		reg |= DP_PLL_PD;
+		writel(reg, dp->reg_base + ANALOGIX_DP_PLL_CTL);
+	} else {
+		reg = readl(dp->reg_base + ANALOGIX_DP_PLL_CTL);
+		reg &= ~DP_PLL_PD;
+		writel(reg, dp->reg_base + ANALOGIX_DP_PLL_CTL);
+	}
+}
+
+void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp,
+				enum analog_power_block block,
+				bool enable)
+{
+	u32 reg;
+
+	switch (block) {
+	case AUX_BLOCK:
+		if (enable) {
+			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg |= AUX_PD;
+			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+		} else {
+			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg &= ~AUX_PD;
+			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+		}
+		break;
+	case CH0_BLOCK:
+		if (enable) {
+			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg |= CH0_PD;
+			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+		} else {
+			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg &= ~CH0_PD;
+			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+		}
+		break;
+	case CH1_BLOCK:
+		if (enable) {
+			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg |= CH1_PD;
+			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+		} else {
+			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg &= ~CH1_PD;
+			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+		}
+		break;
+	case CH2_BLOCK:
+		if (enable) {
+			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg |= CH2_PD;
+			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+		} else {
+			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg &= ~CH2_PD;
+			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+		}
+		break;
+	case CH3_BLOCK:
+		if (enable) {
+			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg |= CH3_PD;
+			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+		} else {
+			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg &= ~CH3_PD;
+			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+		}
+		break;
+	case ANALOG_TOTAL:
+		if (enable) {
+			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg |= DP_PHY_PD;
+			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+		} else {
+			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg &= ~DP_PHY_PD;
+			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+		}
+		break;
+	case POWER_ALL:
+		if (enable) {
+			reg = DP_PHY_PD | AUX_PD | CH3_PD | CH2_PD |
+				CH1_PD | CH0_PD;
+			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+		} else {
+			writel(0x00, dp->reg_base + ANALOGIX_DP_PHY_PD);
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+void analogix_dp_init_analog_func(struct analogix_dp_device *dp)
+{
+	u32 reg;
+	int timeout_loop = 0;
+
+	analogix_dp_set_analog_power_down(dp, POWER_ALL, 0);
+
+	reg = PLL_LOCK_CHG;
+	writel(reg, dp->reg_base + ANALOGIX_DP_COMMON_INT_STA_1);
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_DEBUG_CTL);
+	reg &= ~(F_PLL_LOCK | PLL_LOCK_CTRL);
+	writel(reg, dp->reg_base + ANALOGIX_DP_DEBUG_CTL);
+
+	/* Power up PLL */
+	if (analogix_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
+		analogix_dp_set_pll_power_down(dp, 0);
+
+		while (analogix_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
+			timeout_loop++;
+			if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) {
+				dev_err(dp->dev, "failed to get pll lock status\n");
+				return;
+			}
+			usleep_range(10, 20);
+		}
+	}
+
+	/* Enable Serdes FIFO function and Link symbol clock domain module */
+	reg = readl(dp->reg_base + ANALOGIX_DP_FUNC_EN_2);
+	reg &= ~(SERDES_FIFO_FUNC_EN_N | LS_CLK_DOMAIN_FUNC_EN_N
+		| AUX_FUNC_EN_N);
+	writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_2);
+}
+
+void analogix_dp_clear_hotplug_interrupts(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	if (gpio_is_valid(dp->hpd_gpio))
+		return;
+
+	reg = HOTPLUG_CHG | HPD_LOST | PLUG;
+	writel(reg, dp->reg_base + ANALOGIX_DP_COMMON_INT_STA_4);
+
+	reg = INT_HPD;
+	writel(reg, dp->reg_base + ANALOGIX_DP_INT_STA);
+}
+
+void analogix_dp_init_hpd(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	if (gpio_is_valid(dp->hpd_gpio))
+		return;
+
+	analogix_dp_clear_hotplug_interrupts(dp);
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
+	reg &= ~(F_HPD | HPD_CTRL);
+	writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
+}
+
+enum dp_irq_type analogix_dp_get_irq_type(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	if (gpio_is_valid(dp->hpd_gpio)) {
+		reg = gpio_get_value(dp->hpd_gpio);
+		if (reg)
+			return DP_IRQ_TYPE_HP_CABLE_IN;
+		else
+			return DP_IRQ_TYPE_HP_CABLE_OUT;
+	} else {
+		/* Parse hotplug interrupt status register */
+		reg = readl(dp->reg_base + ANALOGIX_DP_COMMON_INT_STA_4);
+
+		if (reg & PLUG)
+			return DP_IRQ_TYPE_HP_CABLE_IN;
+
+		if (reg & HPD_LOST)
+			return DP_IRQ_TYPE_HP_CABLE_OUT;
+
+		if (reg & HOTPLUG_CHG)
+			return DP_IRQ_TYPE_HP_CHANGE;
+
+		return DP_IRQ_TYPE_UNKNOWN;
+	}
+}
+
+void analogix_dp_reset_aux(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	/* Disable AUX channel module */
+	reg = readl(dp->reg_base + ANALOGIX_DP_FUNC_EN_2);
+	reg |= AUX_FUNC_EN_N;
+	writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_2);
+}
+
+void analogix_dp_init_aux(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	/* Clear inerrupts related to AUX channel */
+	reg = RPLY_RECEIV | AUX_ERR;
+	writel(reg, dp->reg_base + ANALOGIX_DP_INT_STA);
+
+	analogix_dp_reset_aux(dp);
+
+	/* Disable AUX transaction H/W retry */
+	reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3) | AUX_HW_RETRY_COUNT_SEL(0)|
+		AUX_HW_RETRY_INTERVAL_600_MICROSECONDS;
+	writel(reg, dp->reg_base + ANALOGIX_DP_AUX_HW_RETRY_CTL);
+
+	/* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */
+	reg = DEFER_CTRL_EN | DEFER_COUNT(1);
+	writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_DEFER_CTL);
+
+	/* Enable AUX channel module */
+	reg = readl(dp->reg_base + ANALOGIX_DP_FUNC_EN_2);
+	reg &= ~AUX_FUNC_EN_N;
+	writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_2);
+}
+
+int analogix_dp_get_plug_in_status(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	if (gpio_is_valid(dp->hpd_gpio)) {
+		if (gpio_get_value(dp->hpd_gpio))
+			return 0;
+	} else {
+		reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
+		if (reg & HPD_STATUS)
+			return 0;
+	}
+
+	return -EINVAL;
+}
+
+void analogix_dp_enable_sw_function(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_FUNC_EN_1);
+	reg &= ~SW_FUNC_EN_N;
+	writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_1);
+}
+
+int analogix_dp_start_aux_transaction(struct analogix_dp_device *dp)
+{
+	int reg;
+	int retval = 0;
+	int timeout_loop = 0;
+
+	/* Enable AUX CH operation */
+	reg = readl(dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_2);
+	reg |= AUX_EN;
+	writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_2);
+
+	/* Is AUX CH command reply received? */
+	reg = readl(dp->reg_base + ANALOGIX_DP_INT_STA);
+	while (!(reg & RPLY_RECEIV)) {
+		timeout_loop++;
+		if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) {
+			dev_err(dp->dev, "AUX CH command reply failed!\n");
+			return -ETIMEDOUT;
+		}
+		reg = readl(dp->reg_base + ANALOGIX_DP_INT_STA);
+		usleep_range(10, 11);
+	}
+
+	/* Clear interrupt source for AUX CH command reply */
+	writel(RPLY_RECEIV, dp->reg_base + ANALOGIX_DP_INT_STA);
+
+	/* Clear interrupt source for AUX CH access error */
+	reg = readl(dp->reg_base + ANALOGIX_DP_INT_STA);
+	if (reg & AUX_ERR) {
+		writel(AUX_ERR, dp->reg_base + ANALOGIX_DP_INT_STA);
+		return -EREMOTEIO;
+	}
+
+	/* Check AUX CH error access status */
+	reg = readl(dp->reg_base + ANALOGIX_DP_AUX_CH_STA);
+	if ((reg & AUX_STATUS_MASK) != 0) {
+		dev_err(dp->dev, "AUX CH error happens: %d\n\n",
+			reg & AUX_STATUS_MASK);
+		return -EREMOTEIO;
+	}
+
+	return retval;
+}
+
+int analogix_dp_write_byte_to_dpcd(struct analogix_dp_device *dp,
+				unsigned int reg_addr,
+				unsigned char data)
+{
+	u32 reg;
+	int i;
+	int retval;
+
+	for (i = 0; i < 3; i++) {
+		/* Clear AUX CH data buffer */
+		reg = BUF_CLR;
+		writel(reg, dp->reg_base + ANALOGIX_DP_BUFFER_DATA_CTL);
+
+		/* Select DPCD device address */
+		reg = AUX_ADDR_7_0(reg_addr);
+		writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_7_0);
+		reg = AUX_ADDR_15_8(reg_addr);
+		writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_15_8);
+		reg = AUX_ADDR_19_16(reg_addr);
+		writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_19_16);
+
+		/* Write data buffer */
+		reg = (unsigned int)data;
+		writel(reg, dp->reg_base + ANALOGIX_DP_BUF_DATA_0);
+
+		/*
+		 * Set DisplayPort transaction and write 1 byte
+		 * If bit 3 is 1, DisplayPort transaction.
+		 * If Bit 3 is 0, I2C transaction.
+		 */
+		reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE;
+		writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_1);
+
+		/* Start AUX transaction */
+		retval = analogix_dp_start_aux_transaction(dp);
+		if (retval == 0)
+			break;
+		else
+			dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
+				__func__);
+	}
+
+	return retval;
+}
+
+int analogix_dp_read_byte_from_dpcd(struct analogix_dp_device *dp,
+				unsigned int reg_addr,
+				unsigned char *data)
+{
+	u32 reg;
+	int i;
+	int retval;
+
+	for (i = 0; i < 3; i++) {
+		/* Clear AUX CH data buffer */
+		reg = BUF_CLR;
+		writel(reg, dp->reg_base + ANALOGIX_DP_BUFFER_DATA_CTL);
+
+		/* Select DPCD device address */
+		reg = AUX_ADDR_7_0(reg_addr);
+		writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_7_0);
+		reg = AUX_ADDR_15_8(reg_addr);
+		writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_15_8);
+		reg = AUX_ADDR_19_16(reg_addr);
+		writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_19_16);
+
+		/*
+		 * Set DisplayPort transaction and read 1 byte
+		 * If bit 3 is 1, DisplayPort transaction.
+		 * If Bit 3 is 0, I2C transaction.
+		 */
+		reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ;
+		writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_1);
+
+		/* Start AUX transaction */
+		retval = analogix_dp_start_aux_transaction(dp);
+		if (retval == 0)
+			break;
+		else
+			dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
+				__func__);
+	}
+
+	/* Read data buffer */
+	reg = readl(dp->reg_base + ANALOGIX_DP_BUF_DATA_0);
+	*data = (unsigned char)(reg & 0xff);
+
+	return retval;
+}
+
+int analogix_dp_write_bytes_to_dpcd(struct analogix_dp_device *dp,
+				unsigned int reg_addr,
+				unsigned int count,
+				unsigned char data[])
+{
+	u32 reg;
+	unsigned int start_offset;
+	unsigned int cur_data_count;
+	unsigned int cur_data_idx;
+	int i;
+	int retval = 0;
+
+	/* Clear AUX CH data buffer */
+	reg = BUF_CLR;
+	writel(reg, dp->reg_base + ANALOGIX_DP_BUFFER_DATA_CTL);
+
+	start_offset = 0;
+	while (start_offset < count) {
+		/* Buffer size of AUX CH is 16 * 4bytes */
+		if ((count - start_offset) > 16)
+			cur_data_count = 16;
+		else
+			cur_data_count = count - start_offset;
+
+		for (i = 0; i < 3; i++) {
+			/* Select DPCD device address */
+			reg = AUX_ADDR_7_0(reg_addr + start_offset);
+			writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_7_0);
+			reg = AUX_ADDR_15_8(reg_addr + start_offset);
+			writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_15_8);
+			reg = AUX_ADDR_19_16(reg_addr + start_offset);
+			writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_19_16);
+
+			for (cur_data_idx = 0; cur_data_idx < cur_data_count;
+			     cur_data_idx++) {
+				reg = data[start_offset + cur_data_idx];
+				writel(reg, dp->reg_base + ANALOGIX_DP_BUF_DATA_0
+							  + 4 * cur_data_idx);
+			}
+
+			/*
+			 * Set DisplayPort transaction and write
+			 * If bit 3 is 1, DisplayPort transaction.
+			 * If Bit 3 is 0, I2C transaction.
+			 */
+			reg = AUX_LENGTH(cur_data_count) |
+				AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE;
+			writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_1);
+
+			/* Start AUX transaction */
+			retval = analogix_dp_start_aux_transaction(dp);
+			if (retval == 0)
+				break;
+			else
+				dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
+					__func__);
+		}
+
+		start_offset += cur_data_count;
+	}
+
+	return retval;
+}
+
+int analogix_dp_read_bytes_from_dpcd(struct analogix_dp_device *dp,
+				unsigned int reg_addr,
+				unsigned int count,
+				unsigned char data[])
+{
+	u32 reg;
+	unsigned int start_offset;
+	unsigned int cur_data_count;
+	unsigned int cur_data_idx;
+	int i;
+	int retval = 0;
+
+	/* Clear AUX CH data buffer */
+	reg = BUF_CLR;
+	writel(reg, dp->reg_base + ANALOGIX_DP_BUFFER_DATA_CTL);
+
+	start_offset = 0;
+	while (start_offset < count) {
+		/* Buffer size of AUX CH is 16 * 4bytes */
+		if ((count - start_offset) > 16)
+			cur_data_count = 16;
+		else
+			cur_data_count = count - start_offset;
+
+		/* AUX CH Request Transaction process */
+		for (i = 0; i < 3; i++) {
+			/* Select DPCD device address */
+			reg = AUX_ADDR_7_0(reg_addr + start_offset);
+			writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_7_0);
+			reg = AUX_ADDR_15_8(reg_addr + start_offset);
+			writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_15_8);
+			reg = AUX_ADDR_19_16(reg_addr + start_offset);
+			writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_19_16);
+
+			/*
+			 * Set DisplayPort transaction and read
+			 * If bit 3 is 1, DisplayPort transaction.
+			 * If Bit 3 is 0, I2C transaction.
+			 */
+			reg = AUX_LENGTH(cur_data_count) |
+				AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ;
+			writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_1);
+
+			/* Start AUX transaction */
+			retval = analogix_dp_start_aux_transaction(dp);
+			if (retval == 0)
+				break;
+			else
+				dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
+					__func__);
+		}
+
+		for (cur_data_idx = 0; cur_data_idx < cur_data_count;
+		    cur_data_idx++) {
+			reg = readl(dp->reg_base + ANALOGIX_DP_BUF_DATA_0
+						 + 4 * cur_data_idx);
+			data[start_offset + cur_data_idx] =
+				(unsigned char)reg;
+		}
+
+		start_offset += cur_data_count;
+	}
+
+	return retval;
+}
+
+int analogix_dp_select_i2c_device(struct analogix_dp_device *dp,
+				unsigned int device_addr,
+				unsigned int reg_addr)
+{
+	u32 reg;
+	int retval;
+
+	/* Set EDID device address */
+	reg = device_addr;
+	writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_7_0);
+	writel(0x0, dp->reg_base + ANALOGIX_DP_AUX_ADDR_15_8);
+	writel(0x0, dp->reg_base + ANALOGIX_DP_AUX_ADDR_19_16);
+
+	/* Set offset from base address of EDID device */
+	writel(reg_addr, dp->reg_base + ANALOGIX_DP_BUF_DATA_0);
+
+	/*
+	 * Set I2C transaction and write address
+	 * If bit 3 is 1, DisplayPort transaction.
+	 * If Bit 3 is 0, I2C transaction.
+	 */
+	reg = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_MOT |
+		AUX_TX_COMM_WRITE;
+	writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_1);
+
+	/* Start AUX transaction */
+	retval = analogix_dp_start_aux_transaction(dp);
+	if (retval != 0)
+		dev_dbg(dp->dev, "%s: Aux Transaction fail!\n", __func__);
+
+	return retval;
+}
+
+int analogix_dp_read_byte_from_i2c(struct analogix_dp_device *dp,
+				unsigned int device_addr,
+				unsigned int reg_addr,
+				unsigned int *data)
+{
+	u32 reg;
+	int i;
+	int retval;
+
+	for (i = 0; i < 3; i++) {
+		/* Clear AUX CH data buffer */
+		reg = BUF_CLR;
+		writel(reg, dp->reg_base + ANALOGIX_DP_BUFFER_DATA_CTL);
+
+		/* Select EDID device */
+		retval = analogix_dp_select_i2c_device(dp, device_addr, reg_addr);
+		if (retval != 0)
+			continue;
+
+		/*
+		 * Set I2C transaction and read data
+		 * If bit 3 is 1, DisplayPort transaction.
+		 * If Bit 3 is 0, I2C transaction.
+		 */
+		reg = AUX_TX_COMM_I2C_TRANSACTION |
+			AUX_TX_COMM_READ;
+		writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_1);
+
+		/* Start AUX transaction */
+		retval = analogix_dp_start_aux_transaction(dp);
+		if (retval == 0)
+			break;
+		else
+			dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
+				__func__);
+	}
+
+	/* Read data */
+	if (retval == 0)
+		*data = readl(dp->reg_base + ANALOGIX_DP_BUF_DATA_0);
+
+	return retval;
+}
+
+int analogix_dp_read_bytes_from_i2c(struct analogix_dp_device *dp,
+				unsigned int device_addr,
+				unsigned int reg_addr,
+				unsigned int count,
+				unsigned char edid[])
+{
+	u32 reg;
+	unsigned int i, j;
+	unsigned int cur_data_idx;
+	unsigned int defer = 0;
+	int retval = 0;
+
+	for (i = 0; i < count; i += 16) {
+		for (j = 0; j < 3; j++) {
+			/* Clear AUX CH data buffer */
+			reg = BUF_CLR;
+			writel(reg, dp->reg_base + ANALOGIX_DP_BUFFER_DATA_CTL);
+
+			/* Set normal AUX CH command */
+			reg = readl(dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_2);
+			reg &= ~ADDR_ONLY;
+			writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_2);
+
+			/*
+			 * If Rx sends defer, Tx sends only reads
+			 * request without sending address
+			 */
+			if (!defer)
+				retval = analogix_dp_select_i2c_device(dp,
+						device_addr, reg_addr + i);
+			else
+				defer = 0;
+
+			if (retval == 0) {
+				/*
+				 * Set I2C transaction and write data
+				 * If bit 3 is 1, DisplayPort transaction.
+				 * If Bit 3 is 0, I2C transaction.
+				 */
+				reg = AUX_LENGTH(16) |
+					AUX_TX_COMM_I2C_TRANSACTION |
+					AUX_TX_COMM_READ;
+				writel(reg, dp->reg_base +
+					ANALOGIX_DP_AUX_CH_CTL_1);
+
+				/* Start AUX transaction */
+				retval = analogix_dp_start_aux_transaction(dp);
+				if (retval == 0)
+					break;
+				else
+					dev_dbg(dp->dev,
+						"%s: Aux Transaction fail!\n",
+						__func__);
+			}
+			/* Check if Rx sends defer */
+			reg = readl(dp->reg_base + ANALOGIX_DP_AUX_RX_COMM);
+			if (reg == AUX_RX_COMM_AUX_DEFER ||
+				reg == AUX_RX_COMM_I2C_DEFER) {
+				dev_err(dp->dev, "Defer: %d\n\n", reg);
+				defer = 1;
+			}
+		}
+
+		for (cur_data_idx = 0; cur_data_idx < 16; cur_data_idx++) {
+			reg = readl(dp->reg_base + ANALOGIX_DP_BUF_DATA_0
+						 + 4 * cur_data_idx);
+			edid[i + cur_data_idx] = (unsigned char)reg;
+		}
+	}
+
+	return retval;
+}
+
+void analogix_dp_set_link_bandwidth(struct analogix_dp_device *dp, u32 bwtype)
+{
+	u32 reg;
+
+	reg = bwtype;
+	if ((bwtype == LINK_RATE_2_70GBPS) || (bwtype == LINK_RATE_1_62GBPS))
+		writel(reg, dp->reg_base + ANALOGIX_DP_LINK_BW_SET);
+}
+
+void analogix_dp_get_link_bandwidth(struct analogix_dp_device *dp, u32 *bwtype)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_LINK_BW_SET);
+	*bwtype = reg;
+}
+
+void analogix_dp_set_lane_count(struct analogix_dp_device *dp, u32 count)
+{
+	u32 reg;
+
+	reg = count;
+	writel(reg, dp->reg_base + ANALOGIX_DP_LANE_COUNT_SET);
+}
+
+void analogix_dp_get_lane_count(struct analogix_dp_device *dp, u32 *count)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_LANE_COUNT_SET);
+	*count = reg;
+}
+
+void analogix_dp_enable_enhanced_mode(struct analogix_dp_device *dp, bool enable)
+{
+	u32 reg;
+
+	if (enable) {
+		reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_4);
+		reg |= ENHANCED;
+		writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_4);
+	} else {
+		reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_4);
+		reg &= ~ENHANCED;
+		writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_4);
+	}
+}
+
+void analogix_dp_set_training_pattern(struct analogix_dp_device *dp,
+				enum pattern_set pattern)
+{
+	u32 reg;
+
+	switch (pattern) {
+	case PRBS7:
+		reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_PRBS7;
+		writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
+		break;
+	case D10_2:
+		reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_D10_2;
+		writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
+		break;
+	case TRAINING_PTN1:
+		reg = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN1;
+		writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
+		break;
+	case TRAINING_PTN2:
+		reg = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN2;
+		writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
+		break;
+	case DP_NONE:
+		reg = SCRAMBLING_ENABLE |
+			LINK_QUAL_PATTERN_SET_DISABLE |
+			SW_TRAINING_PATTERN_SET_NORMAL;
+		writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
+		break;
+	default:
+		break;
+	}
+}
+
+void analogix_dp_set_lane0_pre_emphasis(struct analogix_dp_device *dp, u32 level)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_LN0_LINK_TRAINING_CTL);
+	reg &= ~PRE_EMPHASIS_SET_MASK;
+	reg |= level << PRE_EMPHASIS_SET_SHIFT;
+	writel(reg, dp->reg_base + ANALOGIX_DP_LN0_LINK_TRAINING_CTL);
+}
+
+void analogix_dp_set_lane1_pre_emphasis(struct analogix_dp_device *dp, u32 level)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_LN1_LINK_TRAINING_CTL);
+	reg &= ~PRE_EMPHASIS_SET_MASK;
+	reg |= level << PRE_EMPHASIS_SET_SHIFT;
+	writel(reg, dp->reg_base + ANALOGIX_DP_LN1_LINK_TRAINING_CTL);
+}
+
+void analogix_dp_set_lane2_pre_emphasis(struct analogix_dp_device *dp, u32 level)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_LN2_LINK_TRAINING_CTL);
+	reg &= ~PRE_EMPHASIS_SET_MASK;
+	reg |= level << PRE_EMPHASIS_SET_SHIFT;
+	writel(reg, dp->reg_base + ANALOGIX_DP_LN2_LINK_TRAINING_CTL);
+}
+
+void analogix_dp_set_lane3_pre_emphasis(struct analogix_dp_device *dp, u32 level)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_LN3_LINK_TRAINING_CTL);
+	reg &= ~PRE_EMPHASIS_SET_MASK;
+	reg |= level << PRE_EMPHASIS_SET_SHIFT;
+	writel(reg, dp->reg_base + ANALOGIX_DP_LN3_LINK_TRAINING_CTL);
+}
+
+void analogix_dp_set_lane0_link_training(struct analogix_dp_device *dp,
+					u32 training_lane)
+{
+	u32 reg;
+
+	reg = training_lane;
+	writel(reg, dp->reg_base + ANALOGIX_DP_LN0_LINK_TRAINING_CTL);
+}
+
+void analogix_dp_set_lane1_link_training(struct analogix_dp_device *dp,
+					u32 training_lane)
+{
+	u32 reg;
+
+	reg = training_lane;
+	writel(reg, dp->reg_base + ANALOGIX_DP_LN1_LINK_TRAINING_CTL);
+}
+
+void analogix_dp_set_lane2_link_training(struct analogix_dp_device *dp,
+					 u32 training_lane)
+{
+	u32 reg;
+
+	reg = training_lane;
+	writel(reg, dp->reg_base + ANALOGIX_DP_LN2_LINK_TRAINING_CTL);
+}
+
+void analogix_dp_set_lane3_link_training(struct analogix_dp_device *dp,
+					u32 training_lane)
+{
+	u32 reg;
+
+	reg = training_lane;
+	writel(reg, dp->reg_base + ANALOGIX_DP_LN3_LINK_TRAINING_CTL);
+}
+
+u32 analogix_dp_get_lane0_link_training(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_LN0_LINK_TRAINING_CTL);
+	return reg;
+}
+
+u32 analogix_dp_get_lane1_link_training(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_LN1_LINK_TRAINING_CTL);
+	return reg;
+}
+
+u32 analogix_dp_get_lane2_link_training(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_LN2_LINK_TRAINING_CTL);
+	return reg;
+}
+
+u32 analogix_dp_get_lane3_link_training(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_LN3_LINK_TRAINING_CTL);
+	return reg;
+}
+
+void analogix_dp_reset_macro(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_PHY_TEST);
+	reg |= MACRO_RST;
+	writel(reg, dp->reg_base + ANALOGIX_DP_PHY_TEST);
+
+	/* 10 us is the minimum reset time. */
+	usleep_range(10, 20);
+
+	reg &= ~MACRO_RST;
+	writel(reg, dp->reg_base + ANALOGIX_DP_PHY_TEST);
+}
+
+void analogix_dp_init_video(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = VSYNC_DET | VID_FORMAT_CHG | VID_CLK_CHG;
+	writel(reg, dp->reg_base + ANALOGIX_DP_COMMON_INT_STA_1);
+
+	reg = 0x0;
+	writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_1);
+
+	reg = CHA_CRI(4) | CHA_CTRL;
+	writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_2);
+
+	reg = 0x0;
+	writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
+
+	reg = VID_HRES_TH(2) | VID_VRES_TH(0);
+	writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_8);
+}
+
+void analogix_dp_set_video_color_format(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	/* Configure the input color depth, color space, dynamic range */
+	reg = (dp->video_info->dynamic_range << IN_D_RANGE_SHIFT) |
+		(dp->video_info->color_depth << IN_BPC_SHIFT) |
+		(dp->video_info->color_space << IN_COLOR_F_SHIFT);
+	writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_2);
+
+	/* Set Input Color YCbCr Coefficients to ITU601 or ITU709 */
+	reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
+	reg &= ~IN_YC_COEFFI_MASK;
+	if (dp->video_info->ycbcr_coeff)
+		reg |= IN_YC_COEFFI_ITU709;
+	else
+		reg |= IN_YC_COEFFI_ITU601;
+	writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
+}
+
+int analogix_dp_is_slave_video_stream_clock_on(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_1);
+	writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_1);
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_1);
+
+	if (!(reg & DET_STA)) {
+		dev_dbg(dp->dev, "Input stream clock not detected.\n");
+		return -EINVAL;
+	}
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_2);
+	writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_2);
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_2);
+	dev_dbg(dp->dev, "wait SYS_CTL_2.\n");
+
+	if (reg & CHA_STA) {
+		dev_dbg(dp->dev, "Input stream clk is changing\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+void analogix_dp_set_video_cr_mn(struct analogix_dp_device *dp,
+		enum clock_recovery_m_value_type type,
+		u32 m_value,
+		u32 n_value)
+{
+	u32 reg;
+
+	if (type == REGISTER_M) {
+		reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_4);
+		reg |= FIX_M_VID;
+		writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_4);
+		reg = m_value & 0xff;
+		writel(reg, dp->reg_base + ANALOGIX_DP_M_VID_0);
+		reg = (m_value >> 8) & 0xff;
+		writel(reg, dp->reg_base + ANALOGIX_DP_M_VID_1);
+		reg = (m_value >> 16) & 0xff;
+		writel(reg, dp->reg_base + ANALOGIX_DP_M_VID_2);
+
+		reg = n_value & 0xff;
+		writel(reg, dp->reg_base + ANALOGIX_DP_N_VID_0);
+		reg = (n_value >> 8) & 0xff;
+		writel(reg, dp->reg_base + ANALOGIX_DP_N_VID_1);
+		reg = (n_value >> 16) & 0xff;
+		writel(reg, dp->reg_base + ANALOGIX_DP_N_VID_2);
+	} else  {
+		reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_4);
+		reg &= ~FIX_M_VID;
+		writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_4);
+
+		writel(0x00, dp->reg_base + ANALOGIX_DP_N_VID_0);
+		writel(0x80, dp->reg_base + ANALOGIX_DP_N_VID_1);
+		writel(0x00, dp->reg_base + ANALOGIX_DP_N_VID_2);
+	}
+}
+
+void analogix_dp_set_video_timing_mode(struct analogix_dp_device *dp, u32 type)
+{
+	u32 reg;
+
+	if (type == VIDEO_TIMING_FROM_CAPTURE) {
+		reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+		reg &= ~FORMAT_SEL;
+		writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+	} else {
+		reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+		reg |= FORMAT_SEL;
+		writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+	}
+}
+
+void analogix_dp_enable_video_master(struct analogix_dp_device *dp, bool enable)
+{
+	u32 reg;
+
+	if (enable) {
+		reg = readl(dp->reg_base + ANALOGIX_DP_SOC_GENERAL_CTL);
+		reg &= ~VIDEO_MODE_MASK;
+		reg |= VIDEO_MASTER_MODE_EN | VIDEO_MODE_MASTER_MODE;
+		writel(reg, dp->reg_base + ANALOGIX_DP_SOC_GENERAL_CTL);
+	} else {
+		reg = readl(dp->reg_base + ANALOGIX_DP_SOC_GENERAL_CTL);
+		reg &= ~VIDEO_MODE_MASK;
+		reg |= VIDEO_MODE_SLAVE_MODE;
+		writel(reg, dp->reg_base + ANALOGIX_DP_SOC_GENERAL_CTL);
+	}
+}
+
+void analogix_dp_start_video(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_1);
+	reg |= VIDEO_EN;
+	writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_1);
+}
+
+int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
+	writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
+	if (!(reg & STRM_VALID)) {
+		dev_dbg(dp->dev, "Input video stream is not detected.\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_FUNC_EN_1);
+	reg &= ~(MASTER_VID_FUNC_EN_N|SLAVE_VID_FUNC_EN_N);
+	reg |= MASTER_VID_FUNC_EN_N;
+	writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_1);
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+	reg &= ~INTERACE_SCAN_CFG;
+	reg |= (dp->video_info->interlaced << 2);
+	writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+	reg &= ~VSYNC_POLARITY_CFG;
+	reg |= (dp->video_info->v_sync_polarity << 1);
+	writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+	reg &= ~HSYNC_POLARITY_CFG;
+	reg |= (dp->video_info->h_sync_polarity << 0);
+	writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+
+	reg = AUDIO_MODE_SPDIF_MODE | VIDEO_MODE_SLAVE_MODE;
+	writel(reg, dp->reg_base + ANALOGIX_DP_SOC_GENERAL_CTL);
+}
+
+void analogix_dp_enable_scrambling(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
+	reg &= ~SCRAMBLING_DISABLE;
+	writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
+}
+
+void analogix_dp_disable_scrambling(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
+	reg |= SCRAMBLING_DISABLE;
+	writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
+}
diff --git a/drivers/gpu/drm/exynos/exynos_dp_reg.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
similarity index 64%
rename from drivers/gpu/drm/exynos/exynos_dp_reg.h
rename to drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
index 2e9bd0e..738db4c 100644
--- a/drivers/gpu/drm/exynos/exynos_dp_reg.h
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
@@ -1,5 +1,5 @@
 /*
- * Register definition file for Samsung DP driver
+ * Register definition file for Analogix DP core driver
  *
  * Copyright (C) 2012 Samsung Electronics Co., Ltd.
  * Author: Jingoo Han <jg1.han at samsung.com>
@@ -9,96 +9,96 @@
  * published by the Free Software Foundation.
  */
 
-#ifndef _EXYNOS_DP_REG_H
-#define _EXYNOS_DP_REG_H
-
-#define EXYNOS_DP_TX_SW_RESET			0x14
-#define EXYNOS_DP_FUNC_EN_1			0x18
-#define EXYNOS_DP_FUNC_EN_2			0x1C
-#define EXYNOS_DP_VIDEO_CTL_1			0x20
-#define EXYNOS_DP_VIDEO_CTL_2			0x24
-#define EXYNOS_DP_VIDEO_CTL_3			0x28
-
-#define EXYNOS_DP_VIDEO_CTL_8			0x3C
-#define EXYNOS_DP_VIDEO_CTL_10			0x44
-
-#define EXYNOS_DP_LANE_MAP			0x35C
-
-#define EXYNOS_DP_ANALOG_CTL_1			0x370
-#define EXYNOS_DP_ANALOG_CTL_2			0x374
-#define EXYNOS_DP_ANALOG_CTL_3			0x378
-#define EXYNOS_DP_PLL_FILTER_CTL_1		0x37C
-#define EXYNOS_DP_TX_AMP_TUNING_CTL		0x380
-
-#define EXYNOS_DP_AUX_HW_RETRY_CTL		0x390
-
-#define EXYNOS_DP_COMMON_INT_STA_1		0x3C4
-#define EXYNOS_DP_COMMON_INT_STA_2		0x3C8
-#define EXYNOS_DP_COMMON_INT_STA_3		0x3CC
-#define EXYNOS_DP_COMMON_INT_STA_4		0x3D0
-#define EXYNOS_DP_INT_STA			0x3DC
-#define EXYNOS_DP_COMMON_INT_MASK_1		0x3E0
-#define EXYNOS_DP_COMMON_INT_MASK_2		0x3E4
-#define EXYNOS_DP_COMMON_INT_MASK_3		0x3E8
-#define EXYNOS_DP_COMMON_INT_MASK_4		0x3EC
-#define EXYNOS_DP_INT_STA_MASK			0x3F8
-#define EXYNOS_DP_INT_CTL			0x3FC
-
-#define EXYNOS_DP_SYS_CTL_1			0x600
-#define EXYNOS_DP_SYS_CTL_2			0x604
-#define EXYNOS_DP_SYS_CTL_3			0x608
-#define EXYNOS_DP_SYS_CTL_4			0x60C
-
-#define EXYNOS_DP_PKT_SEND_CTL			0x640
-#define EXYNOS_DP_HDCP_CTL			0x648
-
-#define EXYNOS_DP_LINK_BW_SET			0x680
-#define EXYNOS_DP_LANE_COUNT_SET		0x684
-#define EXYNOS_DP_TRAINING_PTN_SET		0x688
-#define EXYNOS_DP_LN0_LINK_TRAINING_CTL		0x68C
-#define EXYNOS_DP_LN1_LINK_TRAINING_CTL		0x690
-#define EXYNOS_DP_LN2_LINK_TRAINING_CTL		0x694
-#define EXYNOS_DP_LN3_LINK_TRAINING_CTL		0x698
-
-#define EXYNOS_DP_DEBUG_CTL			0x6C0
-#define EXYNOS_DP_HPD_DEGLITCH_L		0x6C4
-#define EXYNOS_DP_HPD_DEGLITCH_H		0x6C8
-#define EXYNOS_DP_LINK_DEBUG_CTL		0x6E0
-
-#define EXYNOS_DP_M_VID_0			0x700
-#define EXYNOS_DP_M_VID_1			0x704
-#define EXYNOS_DP_M_VID_2			0x708
-#define EXYNOS_DP_N_VID_0			0x70C
-#define EXYNOS_DP_N_VID_1			0x710
-#define EXYNOS_DP_N_VID_2			0x714
-
-#define EXYNOS_DP_PLL_CTL			0x71C
-#define EXYNOS_DP_PHY_PD			0x720
-#define EXYNOS_DP_PHY_TEST			0x724
-
-#define EXYNOS_DP_VIDEO_FIFO_THRD		0x730
-#define EXYNOS_DP_AUDIO_MARGIN			0x73C
-
-#define EXYNOS_DP_M_VID_GEN_FILTER_TH		0x764
-#define EXYNOS_DP_M_AUD_GEN_FILTER_TH		0x778
-#define EXYNOS_DP_AUX_CH_STA			0x780
-#define EXYNOS_DP_AUX_CH_DEFER_CTL		0x788
-#define EXYNOS_DP_AUX_RX_COMM			0x78C
-#define EXYNOS_DP_BUFFER_DATA_CTL		0x790
-#define EXYNOS_DP_AUX_CH_CTL_1			0x794
-#define EXYNOS_DP_AUX_ADDR_7_0			0x798
-#define EXYNOS_DP_AUX_ADDR_15_8			0x79C
-#define EXYNOS_DP_AUX_ADDR_19_16		0x7A0
-#define EXYNOS_DP_AUX_CH_CTL_2			0x7A4
-
-#define EXYNOS_DP_BUF_DATA_0			0x7C0
-
-#define EXYNOS_DP_SOC_GENERAL_CTL		0x800
-
-/* EXYNOS_DP_TX_SW_RESET */
+#ifndef _ANALOGIX_DP_REG_H
+#define _ANALOGIX_DP_REG_H
+
+#define ANALOGIX_DP_TX_SW_RESET			0x14
+#define ANALOGIX_DP_FUNC_EN_1			0x18
+#define ANALOGIX_DP_FUNC_EN_2			0x1C
+#define ANALOGIX_DP_VIDEO_CTL_1			0x20
+#define ANALOGIX_DP_VIDEO_CTL_2			0x24
+#define ANALOGIX_DP_VIDEO_CTL_3			0x28
+
+#define ANALOGIX_DP_VIDEO_CTL_8			0x3C
+#define ANALOGIX_DP_VIDEO_CTL_10		0x44
+
+#define ANALOGIX_DP_LANE_MAP			0x35C
+
+#define ANALOGIX_DP_ANALOG_CTL_1		0x370
+#define ANALOGIX_DP_ANALOG_CTL_2		0x374
+#define ANALOGIX_DP_ANALOG_CTL_3		0x378
+#define ANALOGIX_DP_PLL_FILTER_CTL_1		0x37C
+#define ANALOGIX_DP_TX_AMP_TUNING_CTL		0x380
+
+#define ANALOGIX_DP_AUX_HW_RETRY_CTL		0x390
+
+#define ANALOGIX_DP_COMMON_INT_STA_1		0x3C4
+#define ANALOGIX_DP_COMMON_INT_STA_2		0x3C8
+#define ANALOGIX_DP_COMMON_INT_STA_3		0x3CC
+#define ANALOGIX_DP_COMMON_INT_STA_4		0x3D0
+#define ANALOGIX_DP_INT_STA			0x3DC
+#define ANALOGIX_DP_COMMON_INT_MASK_1		0x3E0
+#define ANALOGIX_DP_COMMON_INT_MASK_2		0x3E4
+#define ANALOGIX_DP_COMMON_INT_MASK_3		0x3E8
+#define ANALOGIX_DP_COMMON_INT_MASK_4		0x3EC
+#define ANALOGIX_DP_INT_STA_MASK		0x3F8
+#define ANALOGIX_DP_INT_CTL			0x3FC
+
+#define ANALOGIX_DP_SYS_CTL_1			0x600
+#define ANALOGIX_DP_SYS_CTL_2			0x604
+#define ANALOGIX_DP_SYS_CTL_3			0x608
+#define ANALOGIX_DP_SYS_CTL_4			0x60C
+
+#define ANALOGIX_DP_PKT_SEND_CTL		0x640
+#define ANALOGIX_DP_HDCP_CTL			0x648
+
+#define ANALOGIX_DP_LINK_BW_SET			0x680
+#define ANALOGIX_DP_LANE_COUNT_SET		0x684
+#define ANALOGIX_DP_TRAINING_PTN_SET		0x688
+#define ANALOGIX_DP_LN0_LINK_TRAINING_CTL	0x68C
+#define ANALOGIX_DP_LN1_LINK_TRAINING_CTL	0x690
+#define ANALOGIX_DP_LN2_LINK_TRAINING_CTL	0x694
+#define ANALOGIX_DP_LN3_LINK_TRAINING_CTL	0x698
+
+#define ANALOGIX_DP_DEBUG_CTL			0x6C0
+#define ANALOGIX_DP_HPD_DEGLITCH_L		0x6C4
+#define ANALOGIX_DP_HPD_DEGLITCH_H		0x6C8
+#define ANALOGIX_DP_LINK_DEBUG_CTL		0x6E0
+
+#define ANALOGIX_DP_M_VID_0			0x700
+#define ANALOGIX_DP_M_VID_1			0x704
+#define ANALOGIX_DP_M_VID_2			0x708
+#define ANALOGIX_DP_N_VID_0			0x70C
+#define ANALOGIX_DP_N_VID_1			0x710
+#define ANALOGIX_DP_N_VID_2			0x714
+
+#define ANALOGIX_DP_PLL_CTL			0x71C
+#define ANALOGIX_DP_PHY_PD			0x720
+#define ANALOGIX_DP_PHY_TEST			0x724
+
+#define ANALOGIX_DP_VIDEO_FIFO_THRD		0x730
+#define ANALOGIX_DP_AUDIO_MARGIN		0x73C
+
+#define ANALOGIX_DP_M_VID_GEN_FILTER_TH		0x764
+#define ANALOGIX_DP_M_AUD_GEN_FILTER_TH		0x778
+#define ANALOGIX_DP_AUX_CH_STA			0x780
+#define ANALOGIX_DP_AUX_CH_DEFER_CTL		0x788
+#define ANALOGIX_DP_AUX_RX_COMM			0x78C
+#define ANALOGIX_DP_BUFFER_DATA_CTL		0x790
+#define ANALOGIX_DP_AUX_CH_CTL_1		0x794
+#define ANALOGIX_DP_AUX_ADDR_7_0		0x798
+#define ANALOGIX_DP_AUX_ADDR_15_8		0x79C
+#define ANALOGIX_DP_AUX_ADDR_19_16		0x7A0
+#define ANALOGIX_DP_AUX_CH_CTL_2		0x7A4
+
+#define ANALOGIX_DP_BUF_DATA_0			0x7C0
+
+#define ANALOGIX_DP_SOC_GENERAL_CTL		0x800
+
+/* ANALOGIX_DP_TX_SW_RESET */
 #define RESET_DP_TX				(0x1 << 0)
 
-/* EXYNOS_DP_FUNC_EN_1 */
+/* ANALOGIX_DP_FUNC_EN_1 */
 #define MASTER_VID_FUNC_EN_N			(0x1 << 7)
 #define SLAVE_VID_FUNC_EN_N			(0x1 << 5)
 #define AUD_FIFO_FUNC_EN_N			(0x1 << 4)
@@ -107,17 +107,17 @@
 #define CRC_FUNC_EN_N				(0x1 << 1)
 #define SW_FUNC_EN_N				(0x1 << 0)
 
-/* EXYNOS_DP_FUNC_EN_2 */
+/* ANALOGIX_DP_FUNC_EN_2 */
 #define SSC_FUNC_EN_N				(0x1 << 7)
 #define AUX_FUNC_EN_N				(0x1 << 2)
 #define SERDES_FIFO_FUNC_EN_N			(0x1 << 1)
 #define LS_CLK_DOMAIN_FUNC_EN_N			(0x1 << 0)
 
-/* EXYNOS_DP_VIDEO_CTL_1 */
+/* ANALOGIX_DP_VIDEO_CTL_1 */
 #define VIDEO_EN				(0x1 << 7)
 #define HDCP_VIDEO_MUTE				(0x1 << 6)
 
-/* EXYNOS_DP_VIDEO_CTL_1 */
+/* ANALOGIX_DP_VIDEO_CTL_1 */
 #define IN_D_RANGE_MASK				(0x1 << 7)
 #define IN_D_RANGE_SHIFT			(7)
 #define IN_D_RANGE_CEA				(0x1 << 7)
@@ -134,7 +134,7 @@
 #define IN_COLOR_F_YCBCR422			(0x1 << 0)
 #define IN_COLOR_F_RGB				(0x0 << 0)
 
-/* EXYNOS_DP_VIDEO_CTL_3 */
+/* ANALOGIX_DP_VIDEO_CTL_3 */
 #define IN_YC_COEFFI_MASK			(0x1 << 7)
 #define IN_YC_COEFFI_SHIFT			(7)
 #define IN_YC_COEFFI_ITU709			(0x1 << 7)
@@ -144,17 +144,17 @@
 #define VID_CHK_UPDATE_TYPE_1			(0x1 << 4)
 #define VID_CHK_UPDATE_TYPE_0			(0x0 << 4)
 
-/* EXYNOS_DP_VIDEO_CTL_8 */
+/* ANALOGIX_DP_VIDEO_CTL_8 */
 #define VID_HRES_TH(x)				(((x) & 0xf) << 4)
 #define VID_VRES_TH(x)				(((x) & 0xf) << 0)
 
-/* EXYNOS_DP_VIDEO_CTL_10 */
+/* ANALOGIX_DP_VIDEO_CTL_10 */
 #define FORMAT_SEL				(0x1 << 4)
 #define INTERACE_SCAN_CFG			(0x1 << 2)
 #define VSYNC_POLARITY_CFG			(0x1 << 1)
 #define HSYNC_POLARITY_CFG			(0x1 << 0)
 
-/* EXYNOS_DP_LANE_MAP */
+/* ANALOGIX_DP_LANE_MAP */
 #define LANE3_MAP_LOGIC_LANE_0			(0x0 << 6)
 #define LANE3_MAP_LOGIC_LANE_1			(0x1 << 6)
 #define LANE3_MAP_LOGIC_LANE_2			(0x2 << 6)
@@ -172,30 +172,30 @@
 #define LANE0_MAP_LOGIC_LANE_2			(0x2 << 0)
 #define LANE0_MAP_LOGIC_LANE_3			(0x3 << 0)
 
-/* EXYNOS_DP_ANALOG_CTL_1 */
+/* ANALOGIX_DP_ANALOG_CTL_1 */
 #define TX_TERMINAL_CTRL_50_OHM			(0x1 << 4)
 
-/* EXYNOS_DP_ANALOG_CTL_2 */
+/* ANALOGIX_DP_ANALOG_CTL_2 */
 #define SEL_24M					(0x1 << 3)
 #define TX_DVDD_BIT_1_0625V			(0x4 << 0)
 
-/* EXYNOS_DP_ANALOG_CTL_3 */
+/* ANALOGIX_DP_ANALOG_CTL_3 */
 #define DRIVE_DVDD_BIT_1_0625V			(0x4 << 5)
 #define VCO_BIT_600_MICRO			(0x5 << 0)
 
-/* EXYNOS_DP_PLL_FILTER_CTL_1 */
+/* ANALOGIX_DP_PLL_FILTER_CTL_1 */
 #define PD_RING_OSC				(0x1 << 6)
 #define AUX_TERMINAL_CTRL_50_OHM		(0x2 << 4)
 #define TX_CUR1_2X				(0x1 << 2)
 #define TX_CUR_16_MA				(0x3 << 0)
 
-/* EXYNOS_DP_TX_AMP_TUNING_CTL */
+/* ANALOGIX_DP_TX_AMP_TUNING_CTL */
 #define CH3_AMP_400_MV				(0x0 << 24)
 #define CH2_AMP_400_MV				(0x0 << 16)
 #define CH1_AMP_400_MV				(0x0 << 8)
 #define CH0_AMP_400_MV				(0x0 << 0)
 
-/* EXYNOS_DP_AUX_HW_RETRY_CTL */
+/* ANALOGIX_DP_AUX_HW_RETRY_CTL */
 #define AUX_BIT_PERIOD_EXPECTED_DELAY(x)	(((x) & 0x7) << 8)
 #define AUX_HW_RETRY_INTERVAL_MASK		(0x3 << 3)
 #define AUX_HW_RETRY_INTERVAL_600_MICROSECONDS	(0x0 << 3)
@@ -204,7 +204,7 @@
 #define AUX_HW_RETRY_INTERVAL_1800_MICROSECONDS	(0x3 << 3)
 #define AUX_HW_RETRY_COUNT_SEL(x)		(((x) & 0x7) << 0)
 
-/* EXYNOS_DP_COMMON_INT_STA_1 */
+/* ANALOGIX_DP_COMMON_INT_STA_1 */
 #define VSYNC_DET				(0x1 << 7)
 #define PLL_LOCK_CHG				(0x1 << 6)
 #define SPDIF_ERR				(0x1 << 5)
@@ -214,19 +214,19 @@
 #define VID_CLK_CHG				(0x1 << 1)
 #define SW_INT					(0x1 << 0)
 
-/* EXYNOS_DP_COMMON_INT_STA_2 */
+/* ANALOGIX_DP_COMMON_INT_STA_2 */
 #define ENC_EN_CHG				(0x1 << 6)
 #define HW_BKSV_RDY				(0x1 << 3)
 #define HW_SHA_DONE				(0x1 << 2)
 #define HW_AUTH_STATE_CHG			(0x1 << 1)
 #define HW_AUTH_DONE				(0x1 << 0)
 
-/* EXYNOS_DP_COMMON_INT_STA_3 */
+/* ANALOGIX_DP_COMMON_INT_STA_3 */
 #define AFIFO_UNDER				(0x1 << 7)
 #define AFIFO_OVER				(0x1 << 6)
 #define R0_CHK_FLAG				(0x1 << 5)
 
-/* EXYNOS_DP_COMMON_INT_STA_4 */
+/* ANALOGIX_DP_COMMON_INT_STA_4 */
 #define PSR_ACTIVE				(0x1 << 7)
 #define PSR_INACTIVE				(0x1 << 6)
 #define SPDIF_BI_PHASE_ERR			(0x1 << 5)
@@ -234,29 +234,29 @@
 #define HPD_LOST				(0x1 << 1)
 #define PLUG					(0x1 << 0)
 
-/* EXYNOS_DP_INT_STA */
+/* ANALOGIX_DP_INT_STA */
 #define INT_HPD					(0x1 << 6)
 #define HW_TRAINING_FINISH			(0x1 << 5)
 #define RPLY_RECEIV				(0x1 << 1)
 #define AUX_ERR					(0x1 << 0)
 
-/* EXYNOS_DP_INT_CTL */
+/* ANALOGIX_DP_INT_CTL */
 #define SOFT_INT_CTRL				(0x1 << 2)
 #define INT_POL1				(0x1 << 1)
 #define INT_POL0				(0x1 << 0)
 
-/* EXYNOS_DP_SYS_CTL_1 */
+/* ANALOGIX_DP_SYS_CTL_1 */
 #define DET_STA					(0x1 << 2)
 #define FORCE_DET				(0x1 << 1)
 #define DET_CTRL				(0x1 << 0)
 
-/* EXYNOS_DP_SYS_CTL_2 */
+/* ANALOGIX_DP_SYS_CTL_2 */
 #define CHA_CRI(x)				(((x) & 0xf) << 4)
 #define CHA_STA					(0x1 << 2)
 #define FORCE_CHA				(0x1 << 1)
 #define CHA_CTRL				(0x1 << 0)
 
-/* EXYNOS_DP_SYS_CTL_3 */
+/* ANALOGIX_DP_SYS_CTL_3 */
 #define HPD_STATUS				(0x1 << 6)
 #define F_HPD					(0x1 << 5)
 #define HPD_CTRL				(0x1 << 4)
@@ -265,13 +265,13 @@
 #define F_VALID					(0x1 << 1)
 #define VALID_CTRL				(0x1 << 0)
 
-/* EXYNOS_DP_SYS_CTL_4 */
+/* ANALOGIX_DP_SYS_CTL_4 */
 #define FIX_M_AUD				(0x1 << 4)
 #define ENHANCED				(0x1 << 3)
 #define FIX_M_VID				(0x1 << 2)
 #define M_VID_UPDATE_CTRL			(0x3 << 0)
 
-/* EXYNOS_DP_TRAINING_PTN_SET */
+/* ANALOGIX_DP_TRAINING_PTN_SET */
 #define SCRAMBLER_TYPE				(0x1 << 9)
 #define HW_LINK_TRAINING_PATTERN		(0x1 << 8)
 #define SCRAMBLING_DISABLE			(0x1 << 5)
@@ -285,24 +285,24 @@
 #define SW_TRAINING_PATTERN_SET_PTN1		(0x1 << 0)
 #define SW_TRAINING_PATTERN_SET_NORMAL		(0x0 << 0)
 
-/* EXYNOS_DP_LN0_LINK_TRAINING_CTL */
+/* ANALOGIX_DP_LN0_LINK_TRAINING_CTL */
 #define PRE_EMPHASIS_SET_MASK			(0x3 << 3)
 #define PRE_EMPHASIS_SET_SHIFT			(3)
 
-/* EXYNOS_DP_DEBUG_CTL */
+/* ANALOGIX_DP_DEBUG_CTL */
 #define PLL_LOCK				(0x1 << 4)
 #define F_PLL_LOCK				(0x1 << 3)
 #define PLL_LOCK_CTRL				(0x1 << 2)
 #define PN_INV					(0x1 << 0)
 
-/* EXYNOS_DP_PLL_CTL */
+/* ANALOGIX_DP_PLL_CTL */
 #define DP_PLL_PD				(0x1 << 7)
 #define DP_PLL_RESET				(0x1 << 6)
 #define DP_PLL_LOOP_BIT_DEFAULT			(0x1 << 4)
 #define DP_PLL_REF_BIT_1_1250V			(0x5 << 0)
 #define DP_PLL_REF_BIT_1_2500V			(0x7 << 0)
 
-/* EXYNOS_DP_PHY_PD */
+/* ANALOGIX_DP_PHY_PD */
 #define DP_PHY_PD				(0x1 << 5)
 #define AUX_PD					(0x1 << 4)
 #define CH3_PD					(0x1 << 3)
@@ -310,28 +310,28 @@
 #define CH1_PD					(0x1 << 1)
 #define CH0_PD					(0x1 << 0)
 
-/* EXYNOS_DP_PHY_TEST */
+/* ANALOGIX_DP_PHY_TEST */
 #define MACRO_RST				(0x1 << 5)
 #define CH1_TEST				(0x1 << 1)
 #define CH0_TEST				(0x1 << 0)
 
-/* EXYNOS_DP_AUX_CH_STA */
+/* ANALOGIX_DP_AUX_CH_STA */
 #define AUX_BUSY				(0x1 << 4)
 #define AUX_STATUS_MASK				(0xf << 0)
 
-/* EXYNOS_DP_AUX_CH_DEFER_CTL */
+/* ANALOGIX_DP_AUX_CH_DEFER_CTL */
 #define DEFER_CTRL_EN				(0x1 << 7)
 #define DEFER_COUNT(x)				(((x) & 0x7f) << 0)
 
-/* EXYNOS_DP_AUX_RX_COMM */
+/* ANALOGIX_DP_AUX_RX_COMM */
 #define AUX_RX_COMM_I2C_DEFER			(0x2 << 2)
 #define AUX_RX_COMM_AUX_DEFER			(0x2 << 0)
 
-/* EXYNOS_DP_BUFFER_DATA_CTL */
+/* ANALOGIX_DP_BUFFER_DATA_CTL */
 #define BUF_CLR					(0x1 << 7)
 #define BUF_DATA_COUNT(x)			(((x) & 0x1f) << 0)
 
-/* EXYNOS_DP_AUX_CH_CTL_1 */
+/* ANALOGIX_DP_AUX_CH_CTL_1 */
 #define AUX_LENGTH(x)				(((x - 1) & 0xf) << 4)
 #define AUX_TX_COMM_MASK			(0xf << 0)
 #define AUX_TX_COMM_DP_TRANSACTION		(0x1 << 3)
@@ -340,20 +340,20 @@
 #define AUX_TX_COMM_WRITE			(0x0 << 0)
 #define AUX_TX_COMM_READ			(0x1 << 0)
 
-/* EXYNOS_DP_AUX_ADDR_7_0 */
+/* ANALOGIX_DP_AUX_ADDR_7_0 */
 #define AUX_ADDR_7_0(x)				(((x) >> 0) & 0xff)
 
-/* EXYNOS_DP_AUX_ADDR_15_8 */
+/* ANALOGIX_DP_AUX_ADDR_15_8 */
 #define AUX_ADDR_15_8(x)			(((x) >> 8) & 0xff)
 
-/* EXYNOS_DP_AUX_ADDR_19_16 */
+/* ANALOGIX_DP_AUX_ADDR_19_16 */
 #define AUX_ADDR_19_16(x)			(((x) >> 16) & 0x0f)
 
-/* EXYNOS_DP_AUX_CH_CTL_2 */
+/* ANALOGIX_DP_AUX_CH_CTL_2 */
 #define ADDR_ONLY				(0x1 << 1)
 #define AUX_EN					(0x1 << 0)
 
-/* EXYNOS_DP_SOC_GENERAL_CTL */
+/* ANALOGIX_DP_SOC_GENERAL_CTL */
 #define AUDIO_MODE_SPDIF_MODE			(0x1 << 8)
 #define AUDIO_MODE_MASTER_MODE			(0x0 << 8)
 #define MASTER_VIDEO_INTERLACE_EN		(0x1 << 4)
@@ -363,4 +363,4 @@
 #define VIDEO_MODE_SLAVE_MODE			(0x1 << 0)
 #define VIDEO_MODE_MASTER_MODE			(0x0 << 0)
 
-#endif /* _EXYNOS_DP_REG_H */
+#endif /* _ANALOGIX_DP_REG_H */
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
index 83efca9..b3e90bd 100644
--- a/drivers/gpu/drm/exynos/Kconfig
+++ b/drivers/gpu/drm/exynos/Kconfig
@@ -71,8 +71,9 @@ config DRM_EXYNOS_DSI
 	  This enables support for Exynos MIPI-DSI device.
 
 config DRM_EXYNOS_DP
-	bool "Display Port"
+	bool "EXYNOS specific extensions for Analogix DP driver"
 	depends on DRM_EXYNOS_FIMD || DRM_EXYNOS7_DECON
+	select DRM_ANALOGIX_DP
 	default DRM_EXYNOS
 	select DRM_PANEL
 	help
diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile
index 6496532..533151b 100644
--- a/drivers/gpu/drm/exynos/Makefile
+++ b/drivers/gpu/drm/exynos/Makefile
@@ -13,7 +13,7 @@ exynosdrm-$(CONFIG_DRM_EXYNOS5433_DECON)	+= exynos5433_drm_decon.o
 exynosdrm-$(CONFIG_DRM_EXYNOS7_DECON)	+= exynos7_drm_decon.o
 exynosdrm-$(CONFIG_DRM_EXYNOS_DPI)	+= exynos_drm_dpi.o
 exynosdrm-$(CONFIG_DRM_EXYNOS_DSI)	+= exynos_drm_dsi.o
-exynosdrm-$(CONFIG_DRM_EXYNOS_DP)	+= exynos_dp_core.o exynos_dp_reg.o
+exynosdrm-$(CONFIG_DRM_EXYNOS_DP)	+= exynos_dp.o
 exynosdrm-$(CONFIG_DRM_EXYNOS_MIXER)	+= exynos_mixer.o
 exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI)	+= exynos_hdmi.o
 exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI)	+= exynos_drm_vidi.o
diff --git a/drivers/gpu/drm/exynos/exynos_dp.c b/drivers/gpu/drm/exynos/exynos_dp.c
new file mode 100644
index 0000000..942d610
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_dp.c
@@ -0,0 +1,328 @@
+/*
+ * Samsung SoC DP (Display Port) interface driver.
+ *
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ * Author: Jingoo Han <jg1.han at samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/of_graph.h>
+#include <video/of_display_timing.h>
+#include <video/of_videomode.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_panel.h>
+
+#include <drm/bridge/analogix_dp.h>
+#include <drm/exynos_drm.h>
+
+#include "exynos_drm_crtc.h"
+
+#define to_dp(nm)	container_of(nm, struct exynos_dp_device, nm)
+
+struct exynos_dp_device {
+	struct drm_encoder         encoder;
+	struct drm_connector       connector;
+	struct drm_bridge          *ptn_bridge;
+	struct drm_device          *drm_dev;
+	struct device              *dev;
+
+	struct exynos_drm_panel_info priv;
+	struct analogix_dp_plat_data plat_data;
+};
+
+int exynos_dp_crtc_clock_enable(struct analogix_dp_plat_data *plat_data,
+				bool enable)
+{
+	struct exynos_dp_device *dp = to_dp(plat_data);
+	struct drm_encoder *encoder = &dp->encoder;
+	struct exynos_drm_crtc *crtc;
+
+	if (!encoder)
+		return -1;
+
+	crtc = to_exynos_crtc(encoder->crtc);
+	if (crtc && crtc->ops && crtc->ops->clock_enable)
+		crtc->ops->clock_enable(crtc, enable);
+
+	return 0;
+}
+
+static int exynos_dp_poweron(struct analogix_dp_plat_data *plat_data)
+{
+	return exynos_dp_crtc_clock_enable(plat_data, true);
+}
+
+static int exynos_dp_poweroff(struct analogix_dp_plat_data *plat_data)
+{
+	return exynos_dp_crtc_clock_enable(plat_data, false);
+}
+
+static int exynos_dp_get_modes(struct analogix_dp_plat_data *plat_data)
+{
+	struct exynos_dp_device *dp = to_dp(plat_data);
+	struct drm_connector *connector = &dp->connector;
+	struct drm_display_mode *mode;
+	int num_modes = 0;
+
+	if (dp->plat_data.panel)
+		return num_modes;
+
+	mode = drm_mode_create(connector->dev);
+	if (!mode) {
+		DRM_ERROR("failed to create a new display mode.\n");
+		return num_modes;
+	}
+
+	drm_display_mode_from_videomode(&dp->priv.vm, mode);
+	mode->width_mm = dp->priv.width_mm;
+	mode->height_mm = dp->priv.height_mm;
+	connector->display_info.width_mm = mode->width_mm;
+	connector->display_info.height_mm = mode->height_mm;
+
+	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+	drm_mode_set_name(mode);
+	drm_mode_probed_add(connector, mode);
+
+	return num_modes + 1;
+}
+
+static int exynos_dp_bridge_attach(struct analogix_dp_plat_data *plat_data,
+				   struct drm_bridge *bridge,
+				   struct drm_connector *connector)
+{
+	struct exynos_dp_device *dp = to_dp(plat_data);
+	struct drm_encoder *encoder = &dp->encoder;
+	int ret;
+
+	drm_connector_register(connector);
+
+	/* Pre-empt DP connector creation if there's a bridge */
+	if (dp->ptn_bridge) {
+		bridge->next = dp->ptn_bridge;
+		dp->ptn_bridge->encoder = encoder;
+		ret = drm_bridge_attach(encoder->dev, dp->ptn_bridge);
+		if (ret) {
+			DRM_ERROR("Failed to attach bridge to drm\n");
+			bridge->next = NULL;
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static bool exynos_dp_mode_fixup(struct drm_encoder *encoder,
+				 const struct drm_display_mode *mode,
+				 struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static void exynos_dp_mode_set(struct drm_encoder *encoder,
+			       struct drm_display_mode *mode,
+			       struct drm_display_mode *adjusted_mode)
+{
+}
+
+static void exynos_dp_nop(struct drm_encoder *encoder)
+{
+	/* do nothing */
+}
+
+static const struct drm_encoder_helper_funcs exynos_dp_encoder_helper_funcs = {
+	.mode_fixup = exynos_dp_mode_fixup,
+	.mode_set = exynos_dp_mode_set,
+	.enable = exynos_dp_nop,
+	.disable = exynos_dp_nop,
+};
+
+static const struct drm_encoder_funcs exynos_dp_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static int exynos_dp_dt_parse_panel(struct exynos_dp_device *dp)
+{
+	int ret;
+
+	ret = of_get_videomode(dp->dev->of_node, &dp->priv.vm,
+			       OF_USE_NATIVE_MODE);
+	if (ret) {
+		DRM_ERROR("failed: of_get_videomode() : %d\n", ret);
+		return ret;
+	}
+	return 0;
+}
+
+static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
+{
+	struct exynos_dp_device *dp = dev_get_drvdata(dev);
+	struct drm_encoder *encoder = &dp->encoder;
+	struct drm_device *drm_dev = data;
+	int pipe, ret;
+
+	/*
+	 * Just like the probe function said, we don't need the
+	 * device drvrate anymore, we should leave the charge to
+	 * analogix dp driver, set the device drvdata to NULL.
+	 */
+	dev_set_drvdata(dev, NULL);
+
+	dp->dev = dev;
+	dp->drm_dev = drm_dev;
+
+	dp->plat_data.dev_type = EXYNOS_DP;
+	dp->plat_data.power_on = exynos_dp_poweron;
+	dp->plat_data.power_off = exynos_dp_poweroff;
+	dp->plat_data.attach = exynos_dp_bridge_attach;
+	dp->plat_data.get_modes = exynos_dp_get_modes;
+
+	if (!dp->plat_data.panel && !dp->ptn_bridge) {
+		ret = exynos_dp_dt_parse_panel(dp);
+		if (ret)
+			return ret;
+	}
+
+	pipe = exynos_drm_crtc_get_pipe_from_type(drm_dev,
+						  EXYNOS_DISPLAY_TYPE_LCD);
+	if (pipe < 0)
+		return pipe;
+
+	encoder->possible_crtcs = 1 << pipe;
+
+	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
+
+	drm_encoder_init(drm_dev, encoder, &exynos_dp_encoder_funcs,
+			 DRM_MODE_ENCODER_TMDS, NULL);
+
+	drm_encoder_helper_add(encoder, &exynos_dp_encoder_helper_funcs);
+
+	dp->plat_data.encoder = encoder;
+
+	return analogix_dp_bind(dev, dp->drm_dev, &dp->plat_data);
+}
+
+static void exynos_dp_unbind(struct device *dev, struct device *master,
+			     void *data)
+{
+	return analogix_dp_unbind(dev, master, data);
+}
+
+static const struct component_ops exynos_dp_ops = {
+	.bind	= exynos_dp_bind,
+	.unbind	= exynos_dp_unbind,
+};
+
+static int exynos_dp_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *panel_node = NULL, *bridge_node, *endpoint = NULL;
+	struct exynos_dp_device *dp;
+
+	dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device),
+			  GFP_KERNEL);
+	if (!dp)
+		return -ENOMEM;
+
+	/*
+	 * We just use the drvdata until driver run into component
+	 * add function, and then we would set drvdata to null, so
+	 * that analogix dp driver would take charge of the drvdata.
+	 */
+	platform_set_drvdata(pdev, dp);
+
+	panel_node = of_parse_phandle(dev->of_node, "panel", 0);
+	if (panel_node) {
+		dp->plat_data.panel = of_drm_find_panel(panel_node);
+		of_node_put(panel_node);
+		if (!dp->plat_data.panel)
+			return -EPROBE_DEFER;
+	} else {
+		endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
+		if (endpoint) {
+			panel_node = of_graph_get_remote_port_parent(endpoint);
+			if (panel_node) {
+				dp->plat_data.panel = of_drm_find_panel(panel_node);
+				of_node_put(panel_node);
+				if (!dp->plat_data.panel)
+					return -EPROBE_DEFER;
+			} else {
+				DRM_ERROR("no port node for panel device.\n");
+				return -EINVAL;
+			}
+		}
+	}
+
+	if (endpoint)
+		goto out;
+
+	endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
+	if (endpoint) {
+		bridge_node = of_graph_get_remote_port_parent(endpoint);
+		if (bridge_node) {
+			dp->ptn_bridge = of_drm_find_bridge(bridge_node);
+			of_node_put(bridge_node);
+			if (!dp->ptn_bridge)
+				return -EPROBE_DEFER;
+		} else
+			return -EPROBE_DEFER;
+	}
+
+out:
+	return component_add(&pdev->dev, &exynos_dp_ops);
+}
+
+static int exynos_dp_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &exynos_dp_ops);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int exynos_dp_suspend(struct device *dev)
+{
+	return analogix_dp_suspend(dev);
+}
+
+static int exynos_dp_resume(struct device *dev)
+{
+	return analogix_dp_resume(dev);
+}
+#endif
+
+static const struct dev_pm_ops exynos_dp_pm_ops = {
+	SET_RUNTIME_PM_OPS(exynos_dp_suspend, exynos_dp_resume, NULL)
+};
+
+static const struct of_device_id exynos_dp_match[] = {
+	{ .compatible = "samsung,exynos5-dp" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, exynos_dp_match);
+
+struct platform_driver dp_driver = {
+	.probe		= exynos_dp_probe,
+	.remove		= exynos_dp_remove,
+	.driver		= {
+		.name	= "exynos-dp",
+		.owner	= THIS_MODULE,
+		.pm	= &exynos_dp_pm_ops,
+		.of_match_table = exynos_dp_match,
+	},
+};
+
+MODULE_AUTHOR("Jingoo Han <jg1.han at samsung.com>");
+MODULE_DESCRIPTION("Samsung Specific Analogix-DP Driver Extension");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.h b/drivers/gpu/drm/exynos/exynos_dp_core.h
deleted file mode 100644
index 66eec4b..0000000
--- a/drivers/gpu/drm/exynos/exynos_dp_core.h
+++ /dev/null
@@ -1,282 +0,0 @@
-/*
- * Header file for Samsung DP (Display Port) interface driver.
- *
- * Copyright (C) 2012 Samsung Electronics Co., Ltd.
- * Author: Jingoo Han <jg1.han at samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#ifndef _EXYNOS_DP_CORE_H
-#define _EXYNOS_DP_CORE_H
-
-#include <drm/drm_crtc.h>
-#include <drm/drm_dp_helper.h>
-#include <drm/exynos_drm.h>
-
-#include "exynos_drm_drv.h"
-
-#define DP_TIMEOUT_LOOP_COUNT 100
-#define MAX_CR_LOOP 5
-#define MAX_EQ_LOOP 5
-
-enum link_rate_type {
-	LINK_RATE_1_62GBPS = 0x06,
-	LINK_RATE_2_70GBPS = 0x0a
-};
-
-enum link_lane_count_type {
-	LANE_COUNT1 = 1,
-	LANE_COUNT2 = 2,
-	LANE_COUNT4 = 4
-};
-
-enum link_training_state {
-	START,
-	CLOCK_RECOVERY,
-	EQUALIZER_TRAINING,
-	FINISHED,
-	FAILED
-};
-
-enum voltage_swing_level {
-	VOLTAGE_LEVEL_0,
-	VOLTAGE_LEVEL_1,
-	VOLTAGE_LEVEL_2,
-	VOLTAGE_LEVEL_3,
-};
-
-enum pre_emphasis_level {
-	PRE_EMPHASIS_LEVEL_0,
-	PRE_EMPHASIS_LEVEL_1,
-	PRE_EMPHASIS_LEVEL_2,
-	PRE_EMPHASIS_LEVEL_3,
-};
-
-enum pattern_set {
-	PRBS7,
-	D10_2,
-	TRAINING_PTN1,
-	TRAINING_PTN2,
-	DP_NONE
-};
-
-enum color_space {
-	COLOR_RGB,
-	COLOR_YCBCR422,
-	COLOR_YCBCR444
-};
-
-enum color_depth {
-	COLOR_6,
-	COLOR_8,
-	COLOR_10,
-	COLOR_12
-};
-
-enum color_coefficient {
-	COLOR_YCBCR601,
-	COLOR_YCBCR709
-};
-
-enum dynamic_range {
-	VESA,
-	CEA
-};
-
-enum pll_status {
-	PLL_UNLOCKED,
-	PLL_LOCKED
-};
-
-enum clock_recovery_m_value_type {
-	CALCULATED_M,
-	REGISTER_M
-};
-
-enum video_timing_recognition_type {
-	VIDEO_TIMING_FROM_CAPTURE,
-	VIDEO_TIMING_FROM_REGISTER
-};
-
-enum analog_power_block {
-	AUX_BLOCK,
-	CH0_BLOCK,
-	CH1_BLOCK,
-	CH2_BLOCK,
-	CH3_BLOCK,
-	ANALOG_TOTAL,
-	POWER_ALL
-};
-
-enum dp_irq_type {
-	DP_IRQ_TYPE_HP_CABLE_IN,
-	DP_IRQ_TYPE_HP_CABLE_OUT,
-	DP_IRQ_TYPE_HP_CHANGE,
-	DP_IRQ_TYPE_UNKNOWN,
-};
-
-struct video_info {
-	char *name;
-
-	bool h_sync_polarity;
-	bool v_sync_polarity;
-	bool interlaced;
-
-	enum color_space color_space;
-	enum dynamic_range dynamic_range;
-	enum color_coefficient ycbcr_coeff;
-	enum color_depth color_depth;
-
-	enum link_rate_type link_rate;
-	enum link_lane_count_type lane_count;
-};
-
-struct link_train {
-	int eq_loop;
-	int cr_loop[4];
-
-	u8 link_rate;
-	u8 lane_count;
-	u8 training_lane[4];
-
-	enum link_training_state lt_state;
-};
-
-struct exynos_dp_device {
-	struct drm_encoder	encoder;
-	struct device		*dev;
-	struct drm_device	*drm_dev;
-	struct drm_connector	connector;
-	struct drm_panel	*panel;
-	struct drm_bridge	*bridge;
-	struct drm_bridge	*ptn_bridge;
-	struct clk		*clock;
-	unsigned int		irq;
-	void __iomem		*reg_base;
-
-	struct video_info	*video_info;
-	struct link_train	link_train;
-	struct work_struct	hotplug_work;
-	struct phy		*phy;
-	int			dpms_mode;
-	int			hpd_gpio;
-
-	struct exynos_drm_panel_info priv;
-};
-
-/* exynos_dp_reg.c */
-void exynos_dp_enable_video_mute(struct exynos_dp_device *dp, bool enable);
-void exynos_dp_stop_video(struct exynos_dp_device *dp);
-void exynos_dp_lane_swap(struct exynos_dp_device *dp, bool enable);
-void exynos_dp_init_analog_param(struct exynos_dp_device *dp);
-void exynos_dp_init_interrupt(struct exynos_dp_device *dp);
-void exynos_dp_reset(struct exynos_dp_device *dp);
-void exynos_dp_swreset(struct exynos_dp_device *dp);
-void exynos_dp_config_interrupt(struct exynos_dp_device *dp);
-enum pll_status exynos_dp_get_pll_lock_status(struct exynos_dp_device *dp);
-void exynos_dp_set_pll_power_down(struct exynos_dp_device *dp, bool enable);
-void exynos_dp_set_analog_power_down(struct exynos_dp_device *dp,
-				enum analog_power_block block,
-				bool enable);
-void exynos_dp_init_analog_func(struct exynos_dp_device *dp);
-void exynos_dp_init_hpd(struct exynos_dp_device *dp);
-enum dp_irq_type exynos_dp_get_irq_type(struct exynos_dp_device *dp);
-void exynos_dp_clear_hotplug_interrupts(struct exynos_dp_device *dp);
-void exynos_dp_reset_aux(struct exynos_dp_device *dp);
-void exynos_dp_init_aux(struct exynos_dp_device *dp);
-int exynos_dp_get_plug_in_status(struct exynos_dp_device *dp);
-void exynos_dp_enable_sw_function(struct exynos_dp_device *dp);
-int exynos_dp_start_aux_transaction(struct exynos_dp_device *dp);
-int exynos_dp_write_byte_to_dpcd(struct exynos_dp_device *dp,
-				unsigned int reg_addr,
-				unsigned char data);
-int exynos_dp_read_byte_from_dpcd(struct exynos_dp_device *dp,
-				unsigned int reg_addr,
-				unsigned char *data);
-int exynos_dp_write_bytes_to_dpcd(struct exynos_dp_device *dp,
-				unsigned int reg_addr,
-				unsigned int count,
-				unsigned char data[]);
-int exynos_dp_read_bytes_from_dpcd(struct exynos_dp_device *dp,
-				unsigned int reg_addr,
-				unsigned int count,
-				unsigned char data[]);
-int exynos_dp_select_i2c_device(struct exynos_dp_device *dp,
-				unsigned int device_addr,
-				unsigned int reg_addr);
-int exynos_dp_read_byte_from_i2c(struct exynos_dp_device *dp,
-				unsigned int device_addr,
-				unsigned int reg_addr,
-				unsigned int *data);
-int exynos_dp_read_bytes_from_i2c(struct exynos_dp_device *dp,
-				unsigned int device_addr,
-				unsigned int reg_addr,
-				unsigned int count,
-				unsigned char edid[]);
-void exynos_dp_set_link_bandwidth(struct exynos_dp_device *dp, u32 bwtype);
-void exynos_dp_get_link_bandwidth(struct exynos_dp_device *dp, u32 *bwtype);
-void exynos_dp_set_lane_count(struct exynos_dp_device *dp, u32 count);
-void exynos_dp_get_lane_count(struct exynos_dp_device *dp, u32 *count);
-void exynos_dp_enable_enhanced_mode(struct exynos_dp_device *dp, bool enable);
-void exynos_dp_set_training_pattern(struct exynos_dp_device *dp,
-				 enum pattern_set pattern);
-void exynos_dp_set_lane0_pre_emphasis(struct exynos_dp_device *dp, u32 level);
-void exynos_dp_set_lane1_pre_emphasis(struct exynos_dp_device *dp, u32 level);
-void exynos_dp_set_lane2_pre_emphasis(struct exynos_dp_device *dp, u32 level);
-void exynos_dp_set_lane3_pre_emphasis(struct exynos_dp_device *dp, u32 level);
-void exynos_dp_set_lane0_link_training(struct exynos_dp_device *dp,
-				u32 training_lane);
-void exynos_dp_set_lane1_link_training(struct exynos_dp_device *dp,
-				u32 training_lane);
-void exynos_dp_set_lane2_link_training(struct exynos_dp_device *dp,
-				u32 training_lane);
-void exynos_dp_set_lane3_link_training(struct exynos_dp_device *dp,
-				u32 training_lane);
-u32 exynos_dp_get_lane0_link_training(struct exynos_dp_device *dp);
-u32 exynos_dp_get_lane1_link_training(struct exynos_dp_device *dp);
-u32 exynos_dp_get_lane2_link_training(struct exynos_dp_device *dp);
-u32 exynos_dp_get_lane3_link_training(struct exynos_dp_device *dp);
-void exynos_dp_reset_macro(struct exynos_dp_device *dp);
-void exynos_dp_init_video(struct exynos_dp_device *dp);
-
-void exynos_dp_set_video_color_format(struct exynos_dp_device *dp);
-int exynos_dp_is_slave_video_stream_clock_on(struct exynos_dp_device *dp);
-void exynos_dp_set_video_cr_mn(struct exynos_dp_device *dp,
-			enum clock_recovery_m_value_type type,
-			u32 m_value,
-			u32 n_value);
-void exynos_dp_set_video_timing_mode(struct exynos_dp_device *dp, u32 type);
-void exynos_dp_enable_video_master(struct exynos_dp_device *dp, bool enable);
-void exynos_dp_start_video(struct exynos_dp_device *dp);
-int exynos_dp_is_video_stream_on(struct exynos_dp_device *dp);
-void exynos_dp_config_video_slave_mode(struct exynos_dp_device *dp);
-void exynos_dp_enable_scrambling(struct exynos_dp_device *dp);
-void exynos_dp_disable_scrambling(struct exynos_dp_device *dp);
-
-/* I2C EDID Chip ID, Slave Address */
-#define I2C_EDID_DEVICE_ADDR			0x50
-#define I2C_E_EDID_DEVICE_ADDR			0x30
-
-#define EDID_BLOCK_LENGTH			0x80
-#define EDID_HEADER_PATTERN			0x00
-#define EDID_EXTENSION_FLAG			0x7e
-#define EDID_CHECKSUM				0x7f
-
-/* DP_MAX_LANE_COUNT */
-#define DPCD_ENHANCED_FRAME_CAP(x)		(((x) >> 7) & 0x1)
-#define DPCD_MAX_LANE_COUNT(x)			((x) & 0x1f)
-
-/* DP_LANE_COUNT_SET */
-#define DPCD_LANE_COUNT_SET(x)			((x) & 0x1f)
-
-/* DP_TRAINING_LANE0_SET */
-#define DPCD_PRE_EMPHASIS_SET(x)		(((x) & 0x3) << 3)
-#define DPCD_PRE_EMPHASIS_GET(x)		(((x) >> 3) & 0x3)
-#define DPCD_VOLTAGE_SWING_SET(x)		(((x) & 0x3) << 0)
-#define DPCD_VOLTAGE_SWING_GET(x)		(((x) >> 0) & 0x3)
-
-#endif /* _EXYNOS_DP_CORE_H */
diff --git a/drivers/gpu/drm/exynos/exynos_dp_reg.c b/drivers/gpu/drm/exynos/exynos_dp_reg.c
deleted file mode 100644
index c1f87a2..0000000
--- a/drivers/gpu/drm/exynos/exynos_dp_reg.c
+++ /dev/null
@@ -1,1263 +0,0 @@
-/*
- * Samsung DP (Display port) register interface driver.
- *
- * Copyright (C) 2012 Samsung Electronics Co., Ltd.
- * Author: Jingoo Han <jg1.han at samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#include <linux/device.h>
-#include <linux/io.h>
-#include <linux/delay.h>
-#include <linux/gpio.h>
-
-#include "exynos_dp_core.h"
-#include "exynos_dp_reg.h"
-
-#define COMMON_INT_MASK_1	0
-#define COMMON_INT_MASK_2	0
-#define COMMON_INT_MASK_3	0
-#define COMMON_INT_MASK_4	(HOTPLUG_CHG | HPD_LOST | PLUG)
-#define INT_STA_MASK		INT_HPD
-
-void exynos_dp_enable_video_mute(struct exynos_dp_device *dp, bool enable)
-{
-	u32 reg;
-
-	if (enable) {
-		reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_1);
-		reg |= HDCP_VIDEO_MUTE;
-		writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_1);
-	} else {
-		reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_1);
-		reg &= ~HDCP_VIDEO_MUTE;
-		writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_1);
-	}
-}
-
-void exynos_dp_stop_video(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_1);
-	reg &= ~VIDEO_EN;
-	writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_1);
-}
-
-void exynos_dp_lane_swap(struct exynos_dp_device *dp, bool enable)
-{
-	u32 reg;
-
-	if (enable)
-		reg = LANE3_MAP_LOGIC_LANE_0 | LANE2_MAP_LOGIC_LANE_1 |
-			LANE1_MAP_LOGIC_LANE_2 | LANE0_MAP_LOGIC_LANE_3;
-	else
-		reg = LANE3_MAP_LOGIC_LANE_3 | LANE2_MAP_LOGIC_LANE_2 |
-			LANE1_MAP_LOGIC_LANE_1 | LANE0_MAP_LOGIC_LANE_0;
-
-	writel(reg, dp->reg_base + EXYNOS_DP_LANE_MAP);
-}
-
-void exynos_dp_init_analog_param(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = TX_TERMINAL_CTRL_50_OHM;
-	writel(reg, dp->reg_base + EXYNOS_DP_ANALOG_CTL_1);
-
-	reg = SEL_24M | TX_DVDD_BIT_1_0625V;
-	writel(reg, dp->reg_base + EXYNOS_DP_ANALOG_CTL_2);
-
-	reg = DRIVE_DVDD_BIT_1_0625V | VCO_BIT_600_MICRO;
-	writel(reg, dp->reg_base + EXYNOS_DP_ANALOG_CTL_3);
-
-	reg = PD_RING_OSC | AUX_TERMINAL_CTRL_50_OHM |
-		TX_CUR1_2X | TX_CUR_16_MA;
-	writel(reg, dp->reg_base + EXYNOS_DP_PLL_FILTER_CTL_1);
-
-	reg = CH3_AMP_400_MV | CH2_AMP_400_MV |
-		CH1_AMP_400_MV | CH0_AMP_400_MV;
-	writel(reg, dp->reg_base + EXYNOS_DP_TX_AMP_TUNING_CTL);
-}
-
-void exynos_dp_init_interrupt(struct exynos_dp_device *dp)
-{
-	/* Set interrupt pin assertion polarity as high */
-	writel(INT_POL1 | INT_POL0, dp->reg_base + EXYNOS_DP_INT_CTL);
-
-	/* Clear pending regisers */
-	writel(0xff, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_1);
-	writel(0x4f, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_2);
-	writel(0xe0, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_3);
-	writel(0xe7, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4);
-	writel(0x63, dp->reg_base + EXYNOS_DP_INT_STA);
-
-	/* 0:mask,1: unmask */
-	writel(0x00, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_1);
-	writel(0x00, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_2);
-	writel(0x00, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_3);
-	writel(0x00, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_4);
-	writel(0x00, dp->reg_base + EXYNOS_DP_INT_STA_MASK);
-}
-
-void exynos_dp_reset(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	exynos_dp_stop_video(dp);
-	exynos_dp_enable_video_mute(dp, 0);
-
-	reg = MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N |
-		AUD_FIFO_FUNC_EN_N | AUD_FUNC_EN_N |
-		HDCP_FUNC_EN_N | SW_FUNC_EN_N;
-	writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_1);
-
-	reg = SSC_FUNC_EN_N | AUX_FUNC_EN_N |
-		SERDES_FIFO_FUNC_EN_N |
-		LS_CLK_DOMAIN_FUNC_EN_N;
-	writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_2);
-
-	usleep_range(20, 30);
-
-	exynos_dp_lane_swap(dp, 0);
-
-	writel(0x0, dp->reg_base + EXYNOS_DP_SYS_CTL_1);
-	writel(0x40, dp->reg_base + EXYNOS_DP_SYS_CTL_2);
-	writel(0x0, dp->reg_base + EXYNOS_DP_SYS_CTL_3);
-	writel(0x0, dp->reg_base + EXYNOS_DP_SYS_CTL_4);
-
-	writel(0x0, dp->reg_base + EXYNOS_DP_PKT_SEND_CTL);
-	writel(0x0, dp->reg_base + EXYNOS_DP_HDCP_CTL);
-
-	writel(0x5e, dp->reg_base + EXYNOS_DP_HPD_DEGLITCH_L);
-	writel(0x1a, dp->reg_base + EXYNOS_DP_HPD_DEGLITCH_H);
-
-	writel(0x10, dp->reg_base + EXYNOS_DP_LINK_DEBUG_CTL);
-
-	writel(0x0, dp->reg_base + EXYNOS_DP_PHY_TEST);
-
-	writel(0x0, dp->reg_base + EXYNOS_DP_VIDEO_FIFO_THRD);
-	writel(0x20, dp->reg_base + EXYNOS_DP_AUDIO_MARGIN);
-
-	writel(0x4, dp->reg_base + EXYNOS_DP_M_VID_GEN_FILTER_TH);
-	writel(0x2, dp->reg_base + EXYNOS_DP_M_AUD_GEN_FILTER_TH);
-
-	writel(0x00000101, dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL);
-}
-
-void exynos_dp_swreset(struct exynos_dp_device *dp)
-{
-	writel(RESET_DP_TX, dp->reg_base + EXYNOS_DP_TX_SW_RESET);
-}
-
-void exynos_dp_config_interrupt(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	/* 0: mask, 1: unmask */
-	reg = COMMON_INT_MASK_1;
-	writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_1);
-
-	reg = COMMON_INT_MASK_2;
-	writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_2);
-
-	reg = COMMON_INT_MASK_3;
-	writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_3);
-
-	reg = COMMON_INT_MASK_4;
-	writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_4);
-
-	reg = INT_STA_MASK;
-	writel(reg, dp->reg_base + EXYNOS_DP_INT_STA_MASK);
-}
-
-enum pll_status exynos_dp_get_pll_lock_status(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_DEBUG_CTL);
-	if (reg & PLL_LOCK)
-		return PLL_LOCKED;
-	else
-		return PLL_UNLOCKED;
-}
-
-void exynos_dp_set_pll_power_down(struct exynos_dp_device *dp, bool enable)
-{
-	u32 reg;
-
-	if (enable) {
-		reg = readl(dp->reg_base + EXYNOS_DP_PLL_CTL);
-		reg |= DP_PLL_PD;
-		writel(reg, dp->reg_base + EXYNOS_DP_PLL_CTL);
-	} else {
-		reg = readl(dp->reg_base + EXYNOS_DP_PLL_CTL);
-		reg &= ~DP_PLL_PD;
-		writel(reg, dp->reg_base + EXYNOS_DP_PLL_CTL);
-	}
-}
-
-void exynos_dp_set_analog_power_down(struct exynos_dp_device *dp,
-				enum analog_power_block block,
-				bool enable)
-{
-	u32 reg;
-
-	switch (block) {
-	case AUX_BLOCK:
-		if (enable) {
-			reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
-			reg |= AUX_PD;
-			writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
-		} else {
-			reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
-			reg &= ~AUX_PD;
-			writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
-		}
-		break;
-	case CH0_BLOCK:
-		if (enable) {
-			reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
-			reg |= CH0_PD;
-			writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
-		} else {
-			reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
-			reg &= ~CH0_PD;
-			writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
-		}
-		break;
-	case CH1_BLOCK:
-		if (enable) {
-			reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
-			reg |= CH1_PD;
-			writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
-		} else {
-			reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
-			reg &= ~CH1_PD;
-			writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
-		}
-		break;
-	case CH2_BLOCK:
-		if (enable) {
-			reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
-			reg |= CH2_PD;
-			writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
-		} else {
-			reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
-			reg &= ~CH2_PD;
-			writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
-		}
-		break;
-	case CH3_BLOCK:
-		if (enable) {
-			reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
-			reg |= CH3_PD;
-			writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
-		} else {
-			reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
-			reg &= ~CH3_PD;
-			writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
-		}
-		break;
-	case ANALOG_TOTAL:
-		if (enable) {
-			reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
-			reg |= DP_PHY_PD;
-			writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
-		} else {
-			reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
-			reg &= ~DP_PHY_PD;
-			writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
-		}
-		break;
-	case POWER_ALL:
-		if (enable) {
-			reg = DP_PHY_PD | AUX_PD | CH3_PD | CH2_PD |
-				CH1_PD | CH0_PD;
-			writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
-		} else {
-			writel(0x00, dp->reg_base + EXYNOS_DP_PHY_PD);
-		}
-		break;
-	default:
-		break;
-	}
-}
-
-void exynos_dp_init_analog_func(struct exynos_dp_device *dp)
-{
-	u32 reg;
-	int timeout_loop = 0;
-
-	exynos_dp_set_analog_power_down(dp, POWER_ALL, 0);
-
-	reg = PLL_LOCK_CHG;
-	writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_1);
-
-	reg = readl(dp->reg_base + EXYNOS_DP_DEBUG_CTL);
-	reg &= ~(F_PLL_LOCK | PLL_LOCK_CTRL);
-	writel(reg, dp->reg_base + EXYNOS_DP_DEBUG_CTL);
-
-	/* Power up PLL */
-	if (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
-		exynos_dp_set_pll_power_down(dp, 0);
-
-		while (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
-			timeout_loop++;
-			if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) {
-				dev_err(dp->dev, "failed to get pll lock status\n");
-				return;
-			}
-			usleep_range(10, 20);
-		}
-	}
-
-	/* Enable Serdes FIFO function and Link symbol clock domain module */
-	reg = readl(dp->reg_base + EXYNOS_DP_FUNC_EN_2);
-	reg &= ~(SERDES_FIFO_FUNC_EN_N | LS_CLK_DOMAIN_FUNC_EN_N
-		| AUX_FUNC_EN_N);
-	writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_2);
-}
-
-void exynos_dp_clear_hotplug_interrupts(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	if (gpio_is_valid(dp->hpd_gpio))
-		return;
-
-	reg = HOTPLUG_CHG | HPD_LOST | PLUG;
-	writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4);
-
-	reg = INT_HPD;
-	writel(reg, dp->reg_base + EXYNOS_DP_INT_STA);
-}
-
-void exynos_dp_init_hpd(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	if (gpio_is_valid(dp->hpd_gpio))
-		return;
-
-	exynos_dp_clear_hotplug_interrupts(dp);
-
-	reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
-	reg &= ~(F_HPD | HPD_CTRL);
-	writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_3);
-}
-
-enum dp_irq_type exynos_dp_get_irq_type(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	if (gpio_is_valid(dp->hpd_gpio)) {
-		reg = gpio_get_value(dp->hpd_gpio);
-		if (reg)
-			return DP_IRQ_TYPE_HP_CABLE_IN;
-		else
-			return DP_IRQ_TYPE_HP_CABLE_OUT;
-	} else {
-		/* Parse hotplug interrupt status register */
-		reg = readl(dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4);
-
-		if (reg & PLUG)
-			return DP_IRQ_TYPE_HP_CABLE_IN;
-
-		if (reg & HPD_LOST)
-			return DP_IRQ_TYPE_HP_CABLE_OUT;
-
-		if (reg & HOTPLUG_CHG)
-			return DP_IRQ_TYPE_HP_CHANGE;
-
-		return DP_IRQ_TYPE_UNKNOWN;
-	}
-}
-
-void exynos_dp_reset_aux(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	/* Disable AUX channel module */
-	reg = readl(dp->reg_base + EXYNOS_DP_FUNC_EN_2);
-	reg |= AUX_FUNC_EN_N;
-	writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_2);
-}
-
-void exynos_dp_init_aux(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	/* Clear inerrupts related to AUX channel */
-	reg = RPLY_RECEIV | AUX_ERR;
-	writel(reg, dp->reg_base + EXYNOS_DP_INT_STA);
-
-	exynos_dp_reset_aux(dp);
-
-	/* Disable AUX transaction H/W retry */
-	reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3) | AUX_HW_RETRY_COUNT_SEL(0)|
-		AUX_HW_RETRY_INTERVAL_600_MICROSECONDS;
-	writel(reg, dp->reg_base + EXYNOS_DP_AUX_HW_RETRY_CTL);
-
-	/* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */
-	reg = DEFER_CTRL_EN | DEFER_COUNT(1);
-	writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_DEFER_CTL);
-
-	/* Enable AUX channel module */
-	reg = readl(dp->reg_base + EXYNOS_DP_FUNC_EN_2);
-	reg &= ~AUX_FUNC_EN_N;
-	writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_2);
-}
-
-int exynos_dp_get_plug_in_status(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	if (gpio_is_valid(dp->hpd_gpio)) {
-		if (gpio_get_value(dp->hpd_gpio))
-			return 0;
-	} else {
-		reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
-		if (reg & HPD_STATUS)
-			return 0;
-	}
-
-	return -EINVAL;
-}
-
-void exynos_dp_enable_sw_function(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_FUNC_EN_1);
-	reg &= ~SW_FUNC_EN_N;
-	writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_1);
-}
-
-int exynos_dp_start_aux_transaction(struct exynos_dp_device *dp)
-{
-	int reg;
-	int retval = 0;
-	int timeout_loop = 0;
-
-	/* Enable AUX CH operation */
-	reg = readl(dp->reg_base + EXYNOS_DP_AUX_CH_CTL_2);
-	reg |= AUX_EN;
-	writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_2);
-
-	/* Is AUX CH command reply received? */
-	reg = readl(dp->reg_base + EXYNOS_DP_INT_STA);
-	while (!(reg & RPLY_RECEIV)) {
-		timeout_loop++;
-		if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) {
-			dev_err(dp->dev, "AUX CH command reply failed!\n");
-			return -ETIMEDOUT;
-		}
-		reg = readl(dp->reg_base + EXYNOS_DP_INT_STA);
-		usleep_range(10, 11);
-	}
-
-	/* Clear interrupt source for AUX CH command reply */
-	writel(RPLY_RECEIV, dp->reg_base + EXYNOS_DP_INT_STA);
-
-	/* Clear interrupt source for AUX CH access error */
-	reg = readl(dp->reg_base + EXYNOS_DP_INT_STA);
-	if (reg & AUX_ERR) {
-		writel(AUX_ERR, dp->reg_base + EXYNOS_DP_INT_STA);
-		return -EREMOTEIO;
-	}
-
-	/* Check AUX CH error access status */
-	reg = readl(dp->reg_base + EXYNOS_DP_AUX_CH_STA);
-	if ((reg & AUX_STATUS_MASK) != 0) {
-		dev_err(dp->dev, "AUX CH error happens: %d\n\n",
-			reg & AUX_STATUS_MASK);
-		return -EREMOTEIO;
-	}
-
-	return retval;
-}
-
-int exynos_dp_write_byte_to_dpcd(struct exynos_dp_device *dp,
-				unsigned int reg_addr,
-				unsigned char data)
-{
-	u32 reg;
-	int i;
-	int retval;
-
-	for (i = 0; i < 3; i++) {
-		/* Clear AUX CH data buffer */
-		reg = BUF_CLR;
-		writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL);
-
-		/* Select DPCD device address */
-		reg = AUX_ADDR_7_0(reg_addr);
-		writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0);
-		reg = AUX_ADDR_15_8(reg_addr);
-		writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_15_8);
-		reg = AUX_ADDR_19_16(reg_addr);
-		writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_19_16);
-
-		/* Write data buffer */
-		reg = (unsigned int)data;
-		writel(reg, dp->reg_base + EXYNOS_DP_BUF_DATA_0);
-
-		/*
-		 * Set DisplayPort transaction and write 1 byte
-		 * If bit 3 is 1, DisplayPort transaction.
-		 * If Bit 3 is 0, I2C transaction.
-		 */
-		reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE;
-		writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1);
-
-		/* Start AUX transaction */
-		retval = exynos_dp_start_aux_transaction(dp);
-		if (retval == 0)
-			break;
-		else
-			dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
-				__func__);
-	}
-
-	return retval;
-}
-
-int exynos_dp_read_byte_from_dpcd(struct exynos_dp_device *dp,
-				unsigned int reg_addr,
-				unsigned char *data)
-{
-	u32 reg;
-	int i;
-	int retval;
-
-	for (i = 0; i < 3; i++) {
-		/* Clear AUX CH data buffer */
-		reg = BUF_CLR;
-		writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL);
-
-		/* Select DPCD device address */
-		reg = AUX_ADDR_7_0(reg_addr);
-		writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0);
-		reg = AUX_ADDR_15_8(reg_addr);
-		writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_15_8);
-		reg = AUX_ADDR_19_16(reg_addr);
-		writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_19_16);
-
-		/*
-		 * Set DisplayPort transaction and read 1 byte
-		 * If bit 3 is 1, DisplayPort transaction.
-		 * If Bit 3 is 0, I2C transaction.
-		 */
-		reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ;
-		writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1);
-
-		/* Start AUX transaction */
-		retval = exynos_dp_start_aux_transaction(dp);
-		if (retval == 0)
-			break;
-		else
-			dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
-				__func__);
-	}
-
-	/* Read data buffer */
-	reg = readl(dp->reg_base + EXYNOS_DP_BUF_DATA_0);
-	*data = (unsigned char)(reg & 0xff);
-
-	return retval;
-}
-
-int exynos_dp_write_bytes_to_dpcd(struct exynos_dp_device *dp,
-				unsigned int reg_addr,
-				unsigned int count,
-				unsigned char data[])
-{
-	u32 reg;
-	unsigned int start_offset;
-	unsigned int cur_data_count;
-	unsigned int cur_data_idx;
-	int i;
-	int retval = 0;
-
-	/* Clear AUX CH data buffer */
-	reg = BUF_CLR;
-	writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL);
-
-	start_offset = 0;
-	while (start_offset < count) {
-		/* Buffer size of AUX CH is 16 * 4bytes */
-		if ((count - start_offset) > 16)
-			cur_data_count = 16;
-		else
-			cur_data_count = count - start_offset;
-
-		for (i = 0; i < 3; i++) {
-			/* Select DPCD device address */
-			reg = AUX_ADDR_7_0(reg_addr + start_offset);
-			writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0);
-			reg = AUX_ADDR_15_8(reg_addr + start_offset);
-			writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_15_8);
-			reg = AUX_ADDR_19_16(reg_addr + start_offset);
-			writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_19_16);
-
-			for (cur_data_idx = 0; cur_data_idx < cur_data_count;
-			     cur_data_idx++) {
-				reg = data[start_offset + cur_data_idx];
-				writel(reg, dp->reg_base + EXYNOS_DP_BUF_DATA_0
-							  + 4 * cur_data_idx);
-			}
-
-			/*
-			 * Set DisplayPort transaction and write
-			 * If bit 3 is 1, DisplayPort transaction.
-			 * If Bit 3 is 0, I2C transaction.
-			 */
-			reg = AUX_LENGTH(cur_data_count) |
-				AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE;
-			writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1);
-
-			/* Start AUX transaction */
-			retval = exynos_dp_start_aux_transaction(dp);
-			if (retval == 0)
-				break;
-			else
-				dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
-					__func__);
-		}
-
-		start_offset += cur_data_count;
-	}
-
-	return retval;
-}
-
-int exynos_dp_read_bytes_from_dpcd(struct exynos_dp_device *dp,
-				unsigned int reg_addr,
-				unsigned int count,
-				unsigned char data[])
-{
-	u32 reg;
-	unsigned int start_offset;
-	unsigned int cur_data_count;
-	unsigned int cur_data_idx;
-	int i;
-	int retval = 0;
-
-	/* Clear AUX CH data buffer */
-	reg = BUF_CLR;
-	writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL);
-
-	start_offset = 0;
-	while (start_offset < count) {
-		/* Buffer size of AUX CH is 16 * 4bytes */
-		if ((count - start_offset) > 16)
-			cur_data_count = 16;
-		else
-			cur_data_count = count - start_offset;
-
-		/* AUX CH Request Transaction process */
-		for (i = 0; i < 3; i++) {
-			/* Select DPCD device address */
-			reg = AUX_ADDR_7_0(reg_addr + start_offset);
-			writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0);
-			reg = AUX_ADDR_15_8(reg_addr + start_offset);
-			writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_15_8);
-			reg = AUX_ADDR_19_16(reg_addr + start_offset);
-			writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_19_16);
-
-			/*
-			 * Set DisplayPort transaction and read
-			 * If bit 3 is 1, DisplayPort transaction.
-			 * If Bit 3 is 0, I2C transaction.
-			 */
-			reg = AUX_LENGTH(cur_data_count) |
-				AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ;
-			writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1);
-
-			/* Start AUX transaction */
-			retval = exynos_dp_start_aux_transaction(dp);
-			if (retval == 0)
-				break;
-			else
-				dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
-					__func__);
-		}
-
-		for (cur_data_idx = 0; cur_data_idx < cur_data_count;
-		    cur_data_idx++) {
-			reg = readl(dp->reg_base + EXYNOS_DP_BUF_DATA_0
-						 + 4 * cur_data_idx);
-			data[start_offset + cur_data_idx] =
-				(unsigned char)reg;
-		}
-
-		start_offset += cur_data_count;
-	}
-
-	return retval;
-}
-
-int exynos_dp_select_i2c_device(struct exynos_dp_device *dp,
-				unsigned int device_addr,
-				unsigned int reg_addr)
-{
-	u32 reg;
-	int retval;
-
-	/* Set EDID device address */
-	reg = device_addr;
-	writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0);
-	writel(0x0, dp->reg_base + EXYNOS_DP_AUX_ADDR_15_8);
-	writel(0x0, dp->reg_base + EXYNOS_DP_AUX_ADDR_19_16);
-
-	/* Set offset from base address of EDID device */
-	writel(reg_addr, dp->reg_base + EXYNOS_DP_BUF_DATA_0);
-
-	/*
-	 * Set I2C transaction and write address
-	 * If bit 3 is 1, DisplayPort transaction.
-	 * If Bit 3 is 0, I2C transaction.
-	 */
-	reg = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_MOT |
-		AUX_TX_COMM_WRITE;
-	writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1);
-
-	/* Start AUX transaction */
-	retval = exynos_dp_start_aux_transaction(dp);
-	if (retval != 0)
-		dev_dbg(dp->dev, "%s: Aux Transaction fail!\n", __func__);
-
-	return retval;
-}
-
-int exynos_dp_read_byte_from_i2c(struct exynos_dp_device *dp,
-				unsigned int device_addr,
-				unsigned int reg_addr,
-				unsigned int *data)
-{
-	u32 reg;
-	int i;
-	int retval;
-
-	for (i = 0; i < 3; i++) {
-		/* Clear AUX CH data buffer */
-		reg = BUF_CLR;
-		writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL);
-
-		/* Select EDID device */
-		retval = exynos_dp_select_i2c_device(dp, device_addr, reg_addr);
-		if (retval != 0)
-			continue;
-
-		/*
-		 * Set I2C transaction and read data
-		 * If bit 3 is 1, DisplayPort transaction.
-		 * If Bit 3 is 0, I2C transaction.
-		 */
-		reg = AUX_TX_COMM_I2C_TRANSACTION |
-			AUX_TX_COMM_READ;
-		writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1);
-
-		/* Start AUX transaction */
-		retval = exynos_dp_start_aux_transaction(dp);
-		if (retval == 0)
-			break;
-		else
-			dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
-				__func__);
-	}
-
-	/* Read data */
-	if (retval == 0)
-		*data = readl(dp->reg_base + EXYNOS_DP_BUF_DATA_0);
-
-	return retval;
-}
-
-int exynos_dp_read_bytes_from_i2c(struct exynos_dp_device *dp,
-				unsigned int device_addr,
-				unsigned int reg_addr,
-				unsigned int count,
-				unsigned char edid[])
-{
-	u32 reg;
-	unsigned int i, j;
-	unsigned int cur_data_idx;
-	unsigned int defer = 0;
-	int retval = 0;
-
-	for (i = 0; i < count; i += 16) {
-		for (j = 0; j < 3; j++) {
-			/* Clear AUX CH data buffer */
-			reg = BUF_CLR;
-			writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL);
-
-			/* Set normal AUX CH command */
-			reg = readl(dp->reg_base + EXYNOS_DP_AUX_CH_CTL_2);
-			reg &= ~ADDR_ONLY;
-			writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_2);
-
-			/*
-			 * If Rx sends defer, Tx sends only reads
-			 * request without sending address
-			 */
-			if (!defer)
-				retval = exynos_dp_select_i2c_device(dp,
-						device_addr, reg_addr + i);
-			else
-				defer = 0;
-
-			if (retval == 0) {
-				/*
-				 * Set I2C transaction and write data
-				 * If bit 3 is 1, DisplayPort transaction.
-				 * If Bit 3 is 0, I2C transaction.
-				 */
-				reg = AUX_LENGTH(16) |
-					AUX_TX_COMM_I2C_TRANSACTION |
-					AUX_TX_COMM_READ;
-				writel(reg, dp->reg_base +
-					EXYNOS_DP_AUX_CH_CTL_1);
-
-				/* Start AUX transaction */
-				retval = exynos_dp_start_aux_transaction(dp);
-				if (retval == 0)
-					break;
-				else
-					dev_dbg(dp->dev,
-						"%s: Aux Transaction fail!\n",
-						__func__);
-			}
-			/* Check if Rx sends defer */
-			reg = readl(dp->reg_base + EXYNOS_DP_AUX_RX_COMM);
-			if (reg == AUX_RX_COMM_AUX_DEFER ||
-				reg == AUX_RX_COMM_I2C_DEFER) {
-				dev_err(dp->dev, "Defer: %d\n\n", reg);
-				defer = 1;
-			}
-		}
-
-		for (cur_data_idx = 0; cur_data_idx < 16; cur_data_idx++) {
-			reg = readl(dp->reg_base + EXYNOS_DP_BUF_DATA_0
-						 + 4 * cur_data_idx);
-			edid[i + cur_data_idx] = (unsigned char)reg;
-		}
-	}
-
-	return retval;
-}
-
-void exynos_dp_set_link_bandwidth(struct exynos_dp_device *dp, u32 bwtype)
-{
-	u32 reg;
-
-	reg = bwtype;
-	if ((bwtype == LINK_RATE_2_70GBPS) || (bwtype == LINK_RATE_1_62GBPS))
-		writel(reg, dp->reg_base + EXYNOS_DP_LINK_BW_SET);
-}
-
-void exynos_dp_get_link_bandwidth(struct exynos_dp_device *dp, u32 *bwtype)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_LINK_BW_SET);
-	*bwtype = reg;
-}
-
-void exynos_dp_set_lane_count(struct exynos_dp_device *dp, u32 count)
-{
-	u32 reg;
-
-	reg = count;
-	writel(reg, dp->reg_base + EXYNOS_DP_LANE_COUNT_SET);
-}
-
-void exynos_dp_get_lane_count(struct exynos_dp_device *dp, u32 *count)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_LANE_COUNT_SET);
-	*count = reg;
-}
-
-void exynos_dp_enable_enhanced_mode(struct exynos_dp_device *dp, bool enable)
-{
-	u32 reg;
-
-	if (enable) {
-		reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_4);
-		reg |= ENHANCED;
-		writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_4);
-	} else {
-		reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_4);
-		reg &= ~ENHANCED;
-		writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_4);
-	}
-}
-
-void exynos_dp_set_training_pattern(struct exynos_dp_device *dp,
-				 enum pattern_set pattern)
-{
-	u32 reg;
-
-	switch (pattern) {
-	case PRBS7:
-		reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_PRBS7;
-		writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET);
-		break;
-	case D10_2:
-		reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_D10_2;
-		writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET);
-		break;
-	case TRAINING_PTN1:
-		reg = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN1;
-		writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET);
-		break;
-	case TRAINING_PTN2:
-		reg = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN2;
-		writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET);
-		break;
-	case DP_NONE:
-		reg = SCRAMBLING_ENABLE |
-			LINK_QUAL_PATTERN_SET_DISABLE |
-			SW_TRAINING_PATTERN_SET_NORMAL;
-		writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET);
-		break;
-	default:
-		break;
-	}
-}
-
-void exynos_dp_set_lane0_pre_emphasis(struct exynos_dp_device *dp, u32 level)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_LN0_LINK_TRAINING_CTL);
-	reg &= ~PRE_EMPHASIS_SET_MASK;
-	reg |= level << PRE_EMPHASIS_SET_SHIFT;
-	writel(reg, dp->reg_base + EXYNOS_DP_LN0_LINK_TRAINING_CTL);
-}
-
-void exynos_dp_set_lane1_pre_emphasis(struct exynos_dp_device *dp, u32 level)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_LN1_LINK_TRAINING_CTL);
-	reg &= ~PRE_EMPHASIS_SET_MASK;
-	reg |= level << PRE_EMPHASIS_SET_SHIFT;
-	writel(reg, dp->reg_base + EXYNOS_DP_LN1_LINK_TRAINING_CTL);
-}
-
-void exynos_dp_set_lane2_pre_emphasis(struct exynos_dp_device *dp, u32 level)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_LN2_LINK_TRAINING_CTL);
-	reg &= ~PRE_EMPHASIS_SET_MASK;
-	reg |= level << PRE_EMPHASIS_SET_SHIFT;
-	writel(reg, dp->reg_base + EXYNOS_DP_LN2_LINK_TRAINING_CTL);
-}
-
-void exynos_dp_set_lane3_pre_emphasis(struct exynos_dp_device *dp, u32 level)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_LN3_LINK_TRAINING_CTL);
-	reg &= ~PRE_EMPHASIS_SET_MASK;
-	reg |= level << PRE_EMPHASIS_SET_SHIFT;
-	writel(reg, dp->reg_base + EXYNOS_DP_LN3_LINK_TRAINING_CTL);
-}
-
-void exynos_dp_set_lane0_link_training(struct exynos_dp_device *dp,
-					u32 training_lane)
-{
-	u32 reg;
-
-	reg = training_lane;
-	writel(reg, dp->reg_base + EXYNOS_DP_LN0_LINK_TRAINING_CTL);
-}
-
-void exynos_dp_set_lane1_link_training(struct exynos_dp_device *dp,
-					u32 training_lane)
-{
-	u32 reg;
-
-	reg = training_lane;
-	writel(reg, dp->reg_base + EXYNOS_DP_LN1_LINK_TRAINING_CTL);
-}
-
-void exynos_dp_set_lane2_link_training(struct exynos_dp_device *dp,
-					u32 training_lane)
-{
-	u32 reg;
-
-	reg = training_lane;
-	writel(reg, dp->reg_base + EXYNOS_DP_LN2_LINK_TRAINING_CTL);
-}
-
-void exynos_dp_set_lane3_link_training(struct exynos_dp_device *dp,
-					u32 training_lane)
-{
-	u32 reg;
-
-	reg = training_lane;
-	writel(reg, dp->reg_base + EXYNOS_DP_LN3_LINK_TRAINING_CTL);
-}
-
-u32 exynos_dp_get_lane0_link_training(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_LN0_LINK_TRAINING_CTL);
-	return reg;
-}
-
-u32 exynos_dp_get_lane1_link_training(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_LN1_LINK_TRAINING_CTL);
-	return reg;
-}
-
-u32 exynos_dp_get_lane2_link_training(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_LN2_LINK_TRAINING_CTL);
-	return reg;
-}
-
-u32 exynos_dp_get_lane3_link_training(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_LN3_LINK_TRAINING_CTL);
-	return reg;
-}
-
-void exynos_dp_reset_macro(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_PHY_TEST);
-	reg |= MACRO_RST;
-	writel(reg, dp->reg_base + EXYNOS_DP_PHY_TEST);
-
-	/* 10 us is the minimum reset time. */
-	usleep_range(10, 20);
-
-	reg &= ~MACRO_RST;
-	writel(reg, dp->reg_base + EXYNOS_DP_PHY_TEST);
-}
-
-void exynos_dp_init_video(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = VSYNC_DET | VID_FORMAT_CHG | VID_CLK_CHG;
-	writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_1);
-
-	reg = 0x0;
-	writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_1);
-
-	reg = CHA_CRI(4) | CHA_CTRL;
-	writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_2);
-
-	reg = 0x0;
-	writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_3);
-
-	reg = VID_HRES_TH(2) | VID_VRES_TH(0);
-	writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_8);
-}
-
-void exynos_dp_set_video_color_format(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	/* Configure the input color depth, color space, dynamic range */
-	reg = (dp->video_info->dynamic_range << IN_D_RANGE_SHIFT) |
-		(dp->video_info->color_depth << IN_BPC_SHIFT) |
-		(dp->video_info->color_space << IN_COLOR_F_SHIFT);
-	writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_2);
-
-	/* Set Input Color YCbCr Coefficients to ITU601 or ITU709 */
-	reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_3);
-	reg &= ~IN_YC_COEFFI_MASK;
-	if (dp->video_info->ycbcr_coeff)
-		reg |= IN_YC_COEFFI_ITU709;
-	else
-		reg |= IN_YC_COEFFI_ITU601;
-	writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_3);
-}
-
-int exynos_dp_is_slave_video_stream_clock_on(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_1);
-	writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_1);
-
-	reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_1);
-
-	if (!(reg & DET_STA)) {
-		dev_dbg(dp->dev, "Input stream clock not detected.\n");
-		return -EINVAL;
-	}
-
-	reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_2);
-	writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_2);
-
-	reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_2);
-	dev_dbg(dp->dev, "wait SYS_CTL_2.\n");
-
-	if (reg & CHA_STA) {
-		dev_dbg(dp->dev, "Input stream clk is changing\n");
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-void exynos_dp_set_video_cr_mn(struct exynos_dp_device *dp,
-		enum clock_recovery_m_value_type type,
-		u32 m_value,
-		u32 n_value)
-{
-	u32 reg;
-
-	if (type == REGISTER_M) {
-		reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_4);
-		reg |= FIX_M_VID;
-		writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_4);
-		reg = m_value & 0xff;
-		writel(reg, dp->reg_base + EXYNOS_DP_M_VID_0);
-		reg = (m_value >> 8) & 0xff;
-		writel(reg, dp->reg_base + EXYNOS_DP_M_VID_1);
-		reg = (m_value >> 16) & 0xff;
-		writel(reg, dp->reg_base + EXYNOS_DP_M_VID_2);
-
-		reg = n_value & 0xff;
-		writel(reg, dp->reg_base + EXYNOS_DP_N_VID_0);
-		reg = (n_value >> 8) & 0xff;
-		writel(reg, dp->reg_base + EXYNOS_DP_N_VID_1);
-		reg = (n_value >> 16) & 0xff;
-		writel(reg, dp->reg_base + EXYNOS_DP_N_VID_2);
-	} else  {
-		reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_4);
-		reg &= ~FIX_M_VID;
-		writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_4);
-
-		writel(0x00, dp->reg_base + EXYNOS_DP_N_VID_0);
-		writel(0x80, dp->reg_base + EXYNOS_DP_N_VID_1);
-		writel(0x00, dp->reg_base + EXYNOS_DP_N_VID_2);
-	}
-}
-
-void exynos_dp_set_video_timing_mode(struct exynos_dp_device *dp, u32 type)
-{
-	u32 reg;
-
-	if (type == VIDEO_TIMING_FROM_CAPTURE) {
-		reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
-		reg &= ~FORMAT_SEL;
-		writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
-	} else {
-		reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
-		reg |= FORMAT_SEL;
-		writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
-	}
-}
-
-void exynos_dp_enable_video_master(struct exynos_dp_device *dp, bool enable)
-{
-	u32 reg;
-
-	if (enable) {
-		reg = readl(dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL);
-		reg &= ~VIDEO_MODE_MASK;
-		reg |= VIDEO_MASTER_MODE_EN | VIDEO_MODE_MASTER_MODE;
-		writel(reg, dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL);
-	} else {
-		reg = readl(dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL);
-		reg &= ~VIDEO_MODE_MASK;
-		reg |= VIDEO_MODE_SLAVE_MODE;
-		writel(reg, dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL);
-	}
-}
-
-void exynos_dp_start_video(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_1);
-	reg |= VIDEO_EN;
-	writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_1);
-}
-
-int exynos_dp_is_video_stream_on(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
-	writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_3);
-
-	reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
-	if (!(reg & STRM_VALID)) {
-		dev_dbg(dp->dev, "Input video stream is not detected.\n");
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-void exynos_dp_config_video_slave_mode(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_FUNC_EN_1);
-	reg &= ~(MASTER_VID_FUNC_EN_N|SLAVE_VID_FUNC_EN_N);
-	reg |= MASTER_VID_FUNC_EN_N;
-	writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_1);
-
-	reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
-	reg &= ~INTERACE_SCAN_CFG;
-	reg |= (dp->video_info->interlaced << 2);
-	writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
-
-	reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
-	reg &= ~VSYNC_POLARITY_CFG;
-	reg |= (dp->video_info->v_sync_polarity << 1);
-	writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
-
-	reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
-	reg &= ~HSYNC_POLARITY_CFG;
-	reg |= (dp->video_info->h_sync_polarity << 0);
-	writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
-
-	reg = AUDIO_MODE_SPDIF_MODE | VIDEO_MODE_SLAVE_MODE;
-	writel(reg, dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL);
-}
-
-void exynos_dp_enable_scrambling(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET);
-	reg &= ~SCRAMBLING_DISABLE;
-	writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET);
-}
-
-void exynos_dp_disable_scrambling(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET);
-	reg |= SCRAMBLING_DISABLE;
-	writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET);
-}
diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h
new file mode 100644
index 0000000..d1188db
--- /dev/null
+++ b/include/drm/bridge/analogix_dp.h
@@ -0,0 +1,40 @@
+/*
+ * Analogix DP (Display Port) Core interface driver.
+ *
+ * Copyright (C) 2015 Rockchip Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+#ifndef _ANALOGIX_DP_H_
+#define _ANALOGIX_DP_H_
+
+#include <drm/drm_crtc.h>
+
+enum analogix_dp_devtype {
+	EXYNOS_DP,
+};
+
+struct analogix_dp_plat_data {
+	enum analogix_dp_devtype dev_type;
+	struct drm_panel *panel;
+	struct drm_encoder *encoder;
+	struct drm_connector *connector;
+
+	int (*power_on)(struct analogix_dp_plat_data *);
+	int (*power_off)(struct analogix_dp_plat_data *);
+	int (*attach)(struct analogix_dp_plat_data *, struct drm_bridge *,
+		      struct drm_connector *);
+	int (*get_modes)(struct analogix_dp_plat_data *);
+};
+
+int analogix_dp_resume(struct device *dev);
+int analogix_dp_suspend(struct device *dev);
+
+int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev,
+		     struct analogix_dp_plat_data *plat_data);
+void analogix_dp_unbind(struct device *dev, struct device *master, void *data);
+
+#endif /* _ANALOGIX_DP_H_ */
-- 
1.9.1




From ykk at rock-chips.com  Tue Jan 19 02:00:43 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Tue, 19 Jan 2016 18:00:43 +0800
Subject: [PATCH v12.1 05/17] dt-bindings: add document for analogix display
 port driver
In-Reply-To: <1450873538-18304-1-git-send-email-ykk@rock-chips.com>
References: <1450873538-18304-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1453197643-13130-1-git-send-email-ykk@rock-chips.com>

Analogix dp driver is split from exynos dp driver, so we just
make an copy of exynos_dp.txt, and then simplify exynos_dp.txt

Beside update some exynos dtsi file with the latest change
according to the devicetree binding documents.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Acked-by: Rob Herring <robh at kernel.org>
---
There is no new changes in v12.1, but just rebase the whole patches on
top of Dave's drm-next branch [0] (Heiko)

Changes in v12.1: None
Changes in v12: None
Changes in v11: None
Changes in v10:
- Add the ack from Rob Herring

Changes in v9: None
Changes in v8:
- Correct the right document path of display-timing.txt (Heiko)
- Correct the misspell of 'from' to 'frm'. (Heiko)

Changes in v7: None
Changes in v6: None
Changes in v5: None
Changes in v4:
- Split all DTS changes, and provide backward compatibility. Mark old
  properties as deprecated but still support them. (Krzysztof)
- Update "analogix,hpd-gpio" to "hpd-gpios" prop name. (Rob)
- Deprecated some properties which could parsed from Edid/Mode/DPCD. (Thierry)
    "analogix,color-space" & "analogix,color-depth"   &
    "analogix,link-rate"   & "analogix,lane-count"    &
    "analogix,ycbcr-coeff" & "analogix,dynamic-range" &
    "vsync-active-high"    & "hsync-active-high"      & "interlaces"

Changes in v3:
- Add devicetree binding documents. (Heiko)
- Remove sync pol & colorimetry properies from the new analogix dp driver
  devicetree binding. (Thierry)
- Update the exist exynos dtsi file with the latest DP DT properies.

Changes in v2: None

 .../bindings/display/bridge/analogix_dp.txt        | 50 ++++++++++++
 .../bindings/display/exynos/exynos_dp.txt          | 92 ++++++----------------
 2 files changed, 76 insertions(+), 66 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/display/bridge/analogix_dp.txt

diff --git a/Documentation/devicetree/bindings/display/bridge/analogix_dp.txt b/Documentation/devicetree/bindings/display/bridge/analogix_dp.txt
new file mode 100644
index 0000000..7659a7a
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/bridge/analogix_dp.txt
@@ -0,0 +1,50 @@
+Analogix Display Port bridge bindings
+
+Required properties for dp-controller:
+	-compatible:
+		platform specific such as:
+		 * "samsung,exynos5-dp"
+		 * "rockchip,rk3288-dp"
+	-reg:
+		physical base address of the controller and length
+		of memory mapped region.
+	-interrupts:
+		interrupt combiner values.
+	-clocks:
+		from common clock binding: handle to dp clock.
+	-clock-names:
+		from common clock binding: Shall be "dp".
+	-interrupt-parent:
+		phandle to Interrupt combiner node.
+	-phys:
+		from general PHY binding: the phandle for the PHY device.
+	-phy-names:
+		from general PHY binding: Should be "dp".
+
+Optional properties for dp-controller:
+	-hpd-gpios:
+		Hotplug detect GPIO.
+		Indicates which GPIO should be used for hotplug detection
+	-port@[X]: SoC specific port nodes with endpoint definitions as defined
+		in Documentation/devicetree/bindings/media/video-interfaces.txt,
+		please refer to the SoC specific binding document:
+		* Documentation/devicetree/bindings/display/exynos/exynos_dp.txt
+		* Documentation/devicetree/bindings/video/analogix_dp-rockchip.txt
+
+
+[1]: Documentation/devicetree/bindings/media/video-interfaces.txt
+-------------------------------------------------------------------------------
+
+Example:
+
+	dp-controller {
+		compatible = "samsung,exynos5-dp";
+		reg = <0x145b0000 0x10000>;
+		interrupts = <10 3>;
+		interrupt-parent = <&combiner>;
+		clocks = <&clock 342>;
+		clock-names = "dp";
+
+		phys = <&dp_phy>;
+		phy-names = "dp";
+	};
diff --git a/Documentation/devicetree/bindings/display/exynos/exynos_dp.txt b/Documentation/devicetree/bindings/display/exynos/exynos_dp.txt
index fe4a7a2..4fa8aca 100644
--- a/Documentation/devicetree/bindings/display/exynos/exynos_dp.txt
+++ b/Documentation/devicetree/bindings/display/exynos/exynos_dp.txt
@@ -1,20 +1,3 @@
-Device-Tree bindings for Samsung Exynos Embedded DisplayPort Transmitter(eDP)
-
-DisplayPort is industry standard to accommodate the growing board adoption
-of digital display technology within the PC and CE industries.
-It consolidates the internal and external connection methods to reduce device
-complexity and cost. It also supports necessary features for important cross
-industry applications and provides performance scalability to enable the next
-generation of displays that feature higher color depths, refresh rates, and
-display resolutions.
-
-eDP (embedded display port) device is compliant with Embedded DisplayPort
-standard as follows,
-- DisplayPort standard 1.1a for Exynos5250 and Exynos5260.
-- DisplayPort standard 1.3 for Exynos5422s and Exynos5800.
-
-eDP resides between FIMD and panel or FIMD and bridge such as LVDS.
-
 The Exynos display port interface should be configured based on
 the type of panel connected to it.
 
@@ -48,26 +31,6 @@ Required properties for dp-controller:
 		from general PHY binding: the phandle for the PHY device.
 	-phy-names:
 		from general PHY binding: Should be "dp".
-	-samsung,color-space:
-		input video data format.
-			COLOR_RGB = 0, COLOR_YCBCR422 = 1, COLOR_YCBCR444 = 2
-	-samsung,dynamic-range:
-		dynamic range for input video data.
-			VESA = 0, CEA = 1
-	-samsung,ycbcr-coeff:
-		YCbCr co-efficients for input video.
-			COLOR_YCBCR601 = 0, COLOR_YCBCR709 = 1
-	-samsung,color-depth:
-		number of bits per colour component.
-			COLOR_6 = 0, COLOR_8 = 1, COLOR_10 = 2, COLOR_12 = 3
-	-samsung,link-rate:
-		link rate supported by the panel.
-			LINK_RATE_1_62GBPS = 0x6, LINK_RATE_2_70GBPS = 0x0A
-	-samsung,lane-count:
-		number of lanes supported by the panel.
-			LANE_COUNT1 = 1, LANE_COUNT2 = 2, LANE_COUNT4 = 4
-	- display-timings: timings for the connected panel as described by
-		Documentation/devicetree/bindings/display/display-timing.txt
 
 Optional properties for dp-controller:
 	-interlaced:
@@ -83,17 +46,30 @@ Optional properties for dp-controller:
 		Hotplug detect GPIO.
 			Indicates which GPIO should be used for hotplug
 			detection
-Video interfaces:
-  Device node can contain video interface port nodes according to [1].
-  The following are properties specific to those nodes:
-
-  endpoint node connected to bridge or panel node:
-   - remote-endpoint: specifies the endpoint in panel or bridge node.
-		      This node is required in all kinds of exynos dp
-		      to represent the connection between dp and bridge
-		      or dp and panel.
-
-[1]: Documentation/devicetree/bindings/media/video-interfaces.txt
+	-video interfaces: Device node can contain video interface port
+			nodes according to [1].
+	- display-timings: timings for the connected panel as described by
+		Documentation/devicetree/bindings/display/panel/display-timing.txt
+
+For the below properties, please refer to Analogix DP binding document:
+ * Documentation/devicetree/bindings/display/bridge/analogix_dp.txt
+	-phys (required)
+	-phy-names (required)
+	-hpd-gpios (optional)
+
+Deprecated properties for DisplayPort:
+-interlaced:            deprecated prop that can parsed from drm_display_mode.
+-vsync-active-high:     deprecated prop that can parsed from drm_display_mode.
+-hsync-active-high:     deprecated prop that can parsed from drm_display_mode.
+-samsung,ycbcr-coeff:   deprecated prop that can parsed from drm_display_mode.
+-samsung,dynamic-range: deprecated prop that can parsed from drm_display_mode.
+-samsung,color-space:   deprecated prop that can parsed from drm_display_info.
+-samsung,color-depth:   deprecated prop that can parsed from drm_display_info.
+-samsung,link-rate:     deprecated prop that can reading from monitor by dpcd method.
+-samsung,lane-count:    deprecated prop that can reading from monitor by dpcd method.
+-samsung,hpd-gpio:      deprecated name for hpd-gpios.
+
+-------------------------------------------------------------------------------
 
 Example:
 
@@ -112,13 +88,6 @@ SOC specific portion:
 
 Board Specific portion:
 	dp-controller {
-		samsung,color-space = <0>;
-		samsung,dynamic-range = <0>;
-		samsung,ycbcr-coeff = <0>;
-		samsung,color-depth = <1>;
-		samsung,link-rate = <0x0a>;
-		samsung,lane-count = <4>;
-
 		display-timings {
 			native-mode = <&lcd_timing>;
 			lcd_timing: 1366x768 {
@@ -135,18 +104,9 @@ Board Specific portion:
 		};
 
 		ports {
-			port {
+			port at 0 {
 				dp_out: endpoint {
-					remote-endpoint = <&dp_in>;
-				};
-			};
-		};
-
-		panel {
-			...
-			port {
-				dp_in: endpoint {
-					remote-endpoint = <&dp_out>;
+					remote-endpoint = <&bridge_in>;
 				};
 			};
 		};
-- 
1.9.1




From ykk at rock-chips.com  Tue Jan 19 02:02:48 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Tue, 19 Jan 2016 18:02:48 +0800
Subject: [PATCH v12.1 06/17] ARM: dts: exynos/dp: remove some properties that
 deprecated by analogix_dp driver
In-Reply-To: <1450873538-18304-1-git-send-email-ykk@rock-chips.com>
References: <1450873538-18304-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1453197768-13253-1-git-send-email-ykk@rock-chips.com>

After exynos_dp have been split the common IP code into analogix_dp driver,
the analogix_dp driver have deprecated some Samsung platform properties which
could be dynamically parsed from EDID/MODE/DPCD message, so this is an update
for Exynos DTS file for dp-controller.

Beside the backward compatibility is fully preserved, so there are no
bisectability break that make this change in a separate patch.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Reviewed-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
Tested-by: Javier Martinez Canillas <javier at osg.samsung.com>
---
There is no new changes in v12.1, but just rebase the whole patches on
top of Dave's drm-next branch [0] (Heiko)

Changes in v12.1: None
Changes in v12: None
Changes in v11: None
Changes in v10: None
Changes in v9: None
Changes in v8: None
Changes in v7: None
Changes in v6:
- Fix Peach Pit hpd property name error:
-       hpd-gpio = <&gpx2 6 0>;
+       hpd-gpios = <&gpx2 6 0>;

Changes in v5:
- Correct the misspell in commit message. (Krzysztof)

Changes in v4:
- Separate all DTS changes to a separate patch. (Krzysztof)

Changes in v3: None
Changes in v2: None

 arch/arm/boot/dts/exynos5250-arndale.dts      | 2 --
 arch/arm/boot/dts/exynos5250-smdk5250.dts     | 2 --
 arch/arm/boot/dts/exynos5250-snow-common.dtsi | 4 +---
 arch/arm/boot/dts/exynos5250-spring.dts       | 4 +---
 arch/arm/boot/dts/exynos5420-peach-pit.dts    | 4 +---
 arch/arm/boot/dts/exynos5420-smdk5420.dts     | 2 --
 arch/arm/boot/dts/exynos5800-peach-pi.dts     | 2 --
 7 files changed, 3 insertions(+), 17 deletions(-)

diff --git a/arch/arm/boot/dts/exynos5250-arndale.dts b/arch/arm/boot/dts/exynos5250-arndale.dts
index c000532..b1790cf 100644
--- a/arch/arm/boot/dts/exynos5250-arndale.dts
+++ b/arch/arm/boot/dts/exynos5250-arndale.dts
@@ -124,8 +124,6 @@
 &dp {
 	status = "okay";
 	samsung,color-space = <0>;
-	samsung,dynamic-range = <0>;
-	samsung,ycbcr-coeff = <0>;
 	samsung,color-depth = <1>;
 	samsung,link-rate = <0x0a>;
 	samsung,lane-count = <4>;
diff --git a/arch/arm/boot/dts/exynos5250-smdk5250.dts b/arch/arm/boot/dts/exynos5250-smdk5250.dts
index 0f5dcd4..f30c2db 100644
--- a/arch/arm/boot/dts/exynos5250-smdk5250.dts
+++ b/arch/arm/boot/dts/exynos5250-smdk5250.dts
@@ -80,8 +80,6 @@
 
 &dp {
 	samsung,color-space = <0>;
-	samsung,dynamic-range = <0>;
-	samsung,ycbcr-coeff = <0>;
 	samsung,color-depth = <1>;
 	samsung,link-rate = <0x0a>;
 	samsung,lane-count = <4>;
diff --git a/arch/arm/boot/dts/exynos5250-snow-common.dtsi b/arch/arm/boot/dts/exynos5250-snow-common.dtsi
index 0a7f408..ee94110 100644
--- a/arch/arm/boot/dts/exynos5250-snow-common.dtsi
+++ b/arch/arm/boot/dts/exynos5250-snow-common.dtsi
@@ -236,12 +236,10 @@
 	pinctrl-names = "default";
 	pinctrl-0 = <&dp_hpd>;
 	samsung,color-space = <0>;
-	samsung,dynamic-range = <0>;
-	samsung,ycbcr-coeff = <0>;
 	samsung,color-depth = <1>;
 	samsung,link-rate = <0x0a>;
 	samsung,lane-count = <2>;
-	samsung,hpd-gpio = <&gpx0 7 GPIO_ACTIVE_HIGH>;
+	hpd-gpios = <&gpx0 7 GPIO_ACTIVE_HIGH>;
 
 	ports {
 		port at 0 {
diff --git a/arch/arm/boot/dts/exynos5250-spring.dts b/arch/arm/boot/dts/exynos5250-spring.dts
index c1edd6d..91881d7 100644
--- a/arch/arm/boot/dts/exynos5250-spring.dts
+++ b/arch/arm/boot/dts/exynos5250-spring.dts
@@ -74,12 +74,10 @@
 	pinctrl-names = "default";
 	pinctrl-0 = <&dp_hpd_gpio>;
 	samsung,color-space = <0>;
-	samsung,dynamic-range = <0>;
-	samsung,ycbcr-coeff = <0>;
 	samsung,color-depth = <1>;
 	samsung,link-rate = <0x0a>;
 	samsung,lane-count = <1>;
-	samsung,hpd-gpio = <&gpc3 0 GPIO_ACTIVE_HIGH>;
+	hpd-gpios = <&gpc3 0 GPIO_ACTIVE_HIGH>;
 };
 
 &ehci {
diff --git a/arch/arm/boot/dts/exynos5420-peach-pit.dts b/arch/arm/boot/dts/exynos5420-peach-pit.dts
index 72ba6f0..8baf40a 100644
--- a/arch/arm/boot/dts/exynos5420-peach-pit.dts
+++ b/arch/arm/boot/dts/exynos5420-peach-pit.dts
@@ -148,12 +148,10 @@
 	pinctrl-names = "default";
 	pinctrl-0 = <&dp_hpd_gpio>;
 	samsung,color-space = <0>;
-	samsung,dynamic-range = <0>;
-	samsung,ycbcr-coeff = <0>;
 	samsung,color-depth = <1>;
 	samsung,link-rate = <0x06>;
 	samsung,lane-count = <2>;
-	samsung,hpd-gpio = <&gpx2 6 GPIO_ACTIVE_HIGH>;
+	hpd-gpios = <&gpx2 6 GPIO_ACTIVE_HIGH>;
 
 	ports {
 		port at 0 {
diff --git a/arch/arm/boot/dts/exynos5420-smdk5420.dts b/arch/arm/boot/dts/exynos5420-smdk5420.dts
index ac35aef..f67344f 100644
--- a/arch/arm/boot/dts/exynos5420-smdk5420.dts
+++ b/arch/arm/boot/dts/exynos5420-smdk5420.dts
@@ -93,8 +93,6 @@
 	pinctrl-names = "default";
 	pinctrl-0 = <&dp_hpd>;
 	samsung,color-space = <0>;
-	samsung,dynamic-range = <0>;
-	samsung,ycbcr-coeff = <0>;
 	samsung,color-depth = <1>;
 	samsung,link-rate = <0x0a>;
 	samsung,lane-count = <4>;
diff --git a/arch/arm/boot/dts/exynos5800-peach-pi.dts b/arch/arm/boot/dts/exynos5800-peach-pi.dts
index 1cc2e95..fc232be 100644
--- a/arch/arm/boot/dts/exynos5800-peach-pi.dts
+++ b/arch/arm/boot/dts/exynos5800-peach-pi.dts
@@ -148,8 +148,6 @@
 	pinctrl-names = "default";
 	pinctrl-0 = <&dp_hpd_gpio>;
 	samsung,color-space = <0>;
-	samsung,dynamic-range = <0>;
-	samsung,ycbcr-coeff = <0>;
 	samsung,color-depth = <1>;
 	samsung,link-rate = <0x0a>;
 	samsung,lane-count = <2>;
-- 
1.9.1




From ykk at rock-chips.com  Tue Jan 19 02:04:53 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Tue, 19 Jan 2016 18:04:53 +0800
Subject: [PATCH v12.1 07/17] drm: rockchip: dp: add rockchip platform dp driver
In-Reply-To: <1450873538-18304-1-git-send-email-ykk@rock-chips.com>
References: <1450873538-18304-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1453197893-13413-1-git-send-email-ykk@rock-chips.com>

Rockchip have three clocks for dp controller, we leave pclk_edp
to analogix_dp driver control, and keep the sclk_edp_24m and
sclk_edp in platform driver.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Tested-by: Javier Martinez Canillas <javier at osg.samsung.com>
---
Rebase the whole patches on top of Dave's drm-next branch [0] (Heiko)

Changes in v12.1:
- Use .enable instead of preprare/commit in encoder_helper_funcs (Heiko)

Changes in v12: None
Changes in v11: None
Changes in v10:
- Correct the ROCKCHIP_ANALOGIX_DP indentation in Kconfig to tabs here (Heiko)

Changes in v9: None
Changes in v8: None
Changes in v7: None
Changes in v6: None
Changes in v5:
- Remove the empty line at the end of document, and correct the endpoint
  numbers in the example DT node, and remove the regulator iomux setting
  in driver code while using the pinctl in devicetree instead. (Heiko)
- Add device type declared, cause the previous "platform device type
  support (v4 11/16)" already merge into (v5 02/14).
- Implement connector registration code. (Thierry)

Changes in v4:
- Remove some deprecated DT properties in rockchip dp document.

Changes in v3:
- Leave "sclk_edp_24m" to rockchip dp phy driver which name to "24m",
  and leave "sclk_edp" to analogix dp core driver which name to "dp",
  and leave "pclk_edp" to rockchip dp platform driver which name to
  "pclk". (Thierry & Heiko)
- Add devicetree binding document. (Heiko)
- Remove "rockchip,panel" DT property, take use of remote point to get panel
  node. (Heiko)
- Add the new function point dp_platdata->get_modes() init.

Changes in v2:
- Get panel node with remote-endpoint method, and create devicetree binding
  for driver. (Heiko)
- Remove the clock enable/disbale with "sclk_edp" & "sclk_edp_24m",
  leave those clock to rockchip dp phy driver.

 drivers/gpu/drm/rockchip/Kconfig                |   9 +
 drivers/gpu/drm/rockchip/Makefile               |   1 +
 drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 384 ++++++++++++++++++++++++
 include/drm/bridge/analogix_dp.h                |   1 +
 4 files changed, 395 insertions(+)
 create mode 100644 drivers/gpu/drm/rockchip/analogix_dp-rockchip.c

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 8573985..9fe4e32 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -35,3 +35,12 @@ config ROCKCHIP_DW_MIPI_DSI
 	 for the Synopsys DesignWare HDMI driver. If you want to
 	 enable MIPI DSI on RK3288 based SoC, you should selet this
 	 option.
+
+config ROCKCHIP_ANALOGIX_DP
+	tristate "Rockchip specific extensions for Analogix DP driver"
+	depends on DRM_ROCKCHIP
+	select DRM_ANALOGIX_DP
+	help
+	  This selects support for Rockchip SoC specific extensions
+	  for the Analogix Core DP driver. If you want to enable DP
+	  on RK3288 based SoC, you should selet this option.
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index d1dc0f7..40d4c94 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -7,6 +7,7 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
 
 obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
 obj-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
+obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
 
 obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_drm_vop.o \
 				rockchip_vop_reg.o
diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
new file mode 100644
index 0000000..acedc98
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
@@ -0,0 +1,384 @@
+/*
+ * Rockchip SoC DP (Display Port) interface driver.
+ *
+ * Copyright (C) Fuzhou Rockchip Electronics Co., Ltd.
+ * Author: Andy Yan <andy.yan at rock-chips.com>
+ *         Yakir Yang <ykk at rock-chips.com>
+ *         Jeff Chen <jeff.chen at rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/component.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of_graph.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/clk.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+
+#include <video/of_videomode.h>
+#include <video/videomode.h>
+
+#include <drm/bridge/analogix_dp.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_vop.h"
+
+#define to_dp(nm)	container_of(nm, struct rockchip_dp_device, nm)
+
+/* dp grf register offset */
+#define GRF_SOC_CON6                            0x025c
+#define GRF_EDP_LCD_SEL_MASK                    BIT(5)
+#define GRF_EDP_SEL_VOP_LIT                     BIT(5)
+#define GRF_EDP_SEL_VOP_BIG                     0
+
+struct rockchip_dp_device {
+	struct drm_device        *drm_dev;
+	struct device            *dev;
+	struct drm_encoder       encoder;
+	struct drm_display_mode  mode;
+
+	struct clk               *pclk;
+	struct regmap            *grf;
+	struct reset_control     *rst;
+
+	struct analogix_dp_plat_data plat_data;
+};
+
+static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
+{
+	reset_control_assert(dp->rst);
+	usleep_range(10, 20);
+	reset_control_deassert(dp->rst);
+
+	return 0;
+}
+
+static int rockchip_dp_poweron(struct analogix_dp_plat_data *plat_data)
+{
+	struct rockchip_dp_device *dp = to_dp(plat_data);
+	int ret;
+
+	ret = clk_prepare_enable(dp->pclk);
+	if (ret < 0) {
+		dev_err(dp->dev, "failed to enable pclk %d\n", ret);
+		return ret;
+	}
+
+	ret = rockchip_dp_pre_init(dp);
+	if (ret < 0) {
+		dev_err(dp->dev, "failed to dp pre init %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int rockchip_dp_powerdown(struct analogix_dp_plat_data *plat_data)
+{
+	struct rockchip_dp_device *dp = to_dp(plat_data);
+
+	clk_disable_unprepare(dp->pclk);
+
+	return 0;
+}
+
+static bool
+rockchip_dp_drm_encoder_mode_fixup(struct drm_encoder *encoder,
+				   const struct drm_display_mode *mode,
+				   struct drm_display_mode *adjusted_mode)
+{
+	/* do nothing */
+	return true;
+}
+
+static void rockchip_dp_drm_encoder_mode_set(struct drm_encoder *encoder,
+					     struct drm_display_mode *mode,
+					     struct drm_display_mode *adjusted)
+{
+	/* do nothing */
+}
+
+static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder)
+{
+	struct rockchip_dp_device *dp = to_dp(encoder);
+	int ret;
+	u32 val;
+
+	/*
+	 * FIXME(Yakir): driver should configure the CRTC output video
+	 * mode with the display information which indicated the monitor
+	 * support colorimetry.
+	 *
+	 * But don't know why the CRTC driver seems could only output the
+	 * RGBaaa rightly. For example, if connect the "innolux,n116bge"
+	 * eDP screen, EDID would indicated that screen only accepted the
+	 * 6bpc mode. But if I configure CRTC to RGB666 output, then eDP
+	 * screen would show a blue picture (RGB888 show a green picture).
+	 * But if I configure CTRC to RGBaaa, and eDP driver still keep
+	 * RGB666 input video mode, then screen would works prefect.
+	 */
+	ret = rockchip_drm_crtc_mode_config(encoder->crtc,
+					    DRM_MODE_CONNECTOR_eDP,
+					    ROCKCHIP_OUT_MODE_AAAA);
+	if (ret < 0) {
+		dev_err(dp->dev, "Could not set crtc mode config (%d)\n", ret);
+		return;
+	}
+
+	ret = rockchip_drm_encoder_get_mux_id(dp->dev->of_node, encoder);
+	if (ret < 0)
+		return;
+
+	if (ret)
+		val = GRF_EDP_SEL_VOP_LIT | (GRF_EDP_LCD_SEL_MASK << 16);
+	else
+		val = GRF_EDP_SEL_VOP_BIG | (GRF_EDP_LCD_SEL_MASK << 16);
+
+	dev_dbg(dp->dev, "vop %s output to dp\n", (ret) ? "LIT" : "BIG");
+
+	ret = regmap_write(dp->grf, GRF_SOC_CON6, val);
+	if (ret != 0) {
+		dev_err(dp->dev, "Could not write to GRF: %d\n", ret);
+		return;
+	}
+}
+
+static void rockchip_dp_drm_encoder_nop(struct drm_encoder *encoder)
+{
+	/* do nothing */
+}
+
+static struct drm_encoder_helper_funcs rockchip_dp_encoder_helper_funcs = {
+	.mode_fixup = rockchip_dp_drm_encoder_mode_fixup,
+	.mode_set = rockchip_dp_drm_encoder_mode_set,
+	.enable = rockchip_dp_drm_encoder_enable,
+	.disable = rockchip_dp_drm_encoder_nop,
+};
+
+static void rockchip_dp_drm_encoder_destroy(struct drm_encoder *encoder)
+{
+	drm_encoder_cleanup(encoder);
+}
+
+static struct drm_encoder_funcs rockchip_dp_encoder_funcs = {
+	.destroy = rockchip_dp_drm_encoder_destroy,
+};
+
+static int rockchip_dp_init(struct rockchip_dp_device *dp)
+{
+	struct device *dev = dp->dev;
+	struct device_node *np = dev->of_node;
+	int ret;
+
+	dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+	if (IS_ERR(dp->grf)) {
+		dev_err(dev, "failed to get rockchip,grf property\n");
+		return PTR_ERR(dp->grf);
+	}
+
+	dp->pclk = devm_clk_get(dev, "pclk");
+	if (IS_ERR(dp->pclk)) {
+		dev_err(dev, "failed to get pclk property\n");
+		return PTR_ERR(dp->pclk);
+	}
+
+	dp->rst = devm_reset_control_get(dev, "dp");
+	if (IS_ERR(dp->rst)) {
+		dev_err(dev, "failed to get dp reset control\n");
+		return PTR_ERR(dp->rst);
+	}
+
+	ret = clk_prepare_enable(dp->pclk);
+	if (ret < 0) {
+		dev_err(dp->dev, "failed to enable pclk %d\n", ret);
+		return ret;
+	}
+
+	ret = rockchip_dp_pre_init(dp);
+	if (ret < 0) {
+		dev_err(dp->dev, "failed to pre init %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int rockchip_dp_drm_create_encoder(struct rockchip_dp_device *dp)
+{
+	struct drm_encoder *encoder = &dp->encoder;
+	struct drm_device *drm_dev = dp->drm_dev;
+	struct device *dev = dp->dev;
+	int ret;
+
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
+							     dev->of_node);
+	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
+
+	ret = drm_encoder_init(drm_dev, encoder, &rockchip_dp_encoder_funcs,
+			       DRM_MODE_ENCODER_TMDS);
+	if (ret) {
+		DRM_ERROR("failed to initialize encoder with drm\n");
+		return ret;
+	}
+
+	drm_encoder_helper_add(encoder, &rockchip_dp_encoder_helper_funcs);
+
+	return 0;
+}
+
+static int rockchip_dp_bind(struct device *dev, struct device *master,
+			    void *data)
+{
+	struct rockchip_dp_device *dp = dev_get_drvdata(dev);
+	struct drm_device *drm_dev = data;
+	int ret;
+
+	/*
+	 * Just like the probe function said, we don't need the
+	 * device drvrate anymore, we should leave the charge to
+	 * analogix dp driver, set the device drvdata to NULL.
+	 */
+	dev_set_drvdata(dev, NULL);
+
+	ret = rockchip_dp_init(dp);
+	if (ret < 0)
+		return ret;
+
+	dp->drm_dev = drm_dev;
+
+	ret = rockchip_dp_drm_create_encoder(dp);
+	if (ret) {
+		DRM_ERROR("failed to create drm encoder\n");
+		return ret;
+	}
+
+	dp->plat_data.encoder = &dp->encoder;
+
+	dp->plat_data.dev_type = RK3288_DP;
+	dp->plat_data.power_on = rockchip_dp_poweron;
+	dp->plat_data.power_off = rockchip_dp_powerdown;
+
+	return analogix_dp_bind(dev, dp->drm_dev, &dp->plat_data);
+}
+
+static void rockchip_dp_unbind(struct device *dev, struct device *master,
+			       void *data)
+{
+	return analogix_dp_unbind(dev, master, data);
+}
+
+static const struct component_ops rockchip_dp_component_ops = {
+	.bind = rockchip_dp_bind,
+	.unbind = rockchip_dp_unbind,
+};
+
+static int rockchip_dp_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *panel_node, *port, *endpoint;
+	struct rockchip_dp_device *dp;
+	struct drm_panel *panel;
+
+	port = of_graph_get_port_by_id(dev->of_node, 1);
+	if (!port) {
+		dev_err(dev, "can't find output port\n");
+		return -EINVAL;
+	}
+
+	endpoint = of_get_child_by_name(port, "endpoint");
+	of_node_put(port);
+	if (!endpoint) {
+		dev_err(dev, "no output endpoint found\n");
+		return -EINVAL;
+	}
+
+	panel_node = of_graph_get_remote_port_parent(endpoint);
+	of_node_put(endpoint);
+	if (!panel_node) {
+		dev_err(dev, "no output node found\n");
+		return -EINVAL;
+	}
+
+	panel = of_drm_find_panel(panel_node);
+	if (!panel) {
+		DRM_ERROR("failed to find panel\n");
+		of_node_put(panel_node);
+		return -EPROBE_DEFER;
+	}
+
+	of_node_put(panel_node);
+
+	dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
+	if (!dp)
+		return -ENOMEM;
+
+	dp->dev = dev;
+
+	dp->plat_data.panel = panel;
+
+	/*
+	 * We just use the drvdata until driver run into component
+	 * add function, and then we would set drvdata to null, so
+	 * that analogix dp driver could take charge of the drvdata.
+	 */
+	platform_set_drvdata(pdev, dp);
+
+	return component_add(dev, &rockchip_dp_component_ops);
+}
+
+static int rockchip_dp_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &rockchip_dp_component_ops);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int rockchip_dp_suspend(struct device *dev)
+{
+	return analogix_dp_suspend(dev);
+}
+
+static int rockchip_dp_resume(struct device *dev)
+{
+	return analogix_dp_resume(dev);
+}
+#endif
+
+static const struct dev_pm_ops rockchip_dp_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(rockchip_dp_suspend, rockchip_dp_resume)
+};
+
+static const struct of_device_id rockchip_dp_dt_ids[] = {
+	{.compatible = "rockchip,rk3288-dp",},
+	{}
+};
+MODULE_DEVICE_TABLE(of, rockchip_dp_dt_ids);
+
+static struct platform_driver rockchip_dp_driver = {
+	.probe = rockchip_dp_probe,
+	.remove = rockchip_dp_remove,
+	.driver = {
+		   .name = "rockchip-dp",
+		   .owner = THIS_MODULE,
+		   .pm = &rockchip_dp_pm_ops,
+		   .of_match_table = of_match_ptr(rockchip_dp_dt_ids),
+	},
+};
+
+module_platform_driver(rockchip_dp_driver);
+
+MODULE_AUTHOR("Yakir Yang <ykk at rock-chips.com>");
+MODULE_AUTHOR("Jeff chen <jeff.chen at rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip Specific Analogix-DP Driver Extension");
+MODULE_LICENSE("GPL v2");
diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h
index d1188db..25afb31 100644
--- a/include/drm/bridge/analogix_dp.h
+++ b/include/drm/bridge/analogix_dp.h
@@ -15,6 +15,7 @@
 
 enum analogix_dp_devtype {
 	EXYNOS_DP,
+	RK3288_DP,
 };
 
 struct analogix_dp_plat_data {
-- 
1.9.1




From ykk at rock-chips.com  Tue Jan 19 02:06:57 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Tue, 19 Jan 2016 18:06:57 +0800
Subject: [PATCH v12.1 13/17] drm: bridge: analogix/dp: try force hpd after
 plug in lookup failed
In-Reply-To: <1450873538-18304-1-git-send-email-ykk@rock-chips.com>
References: <1450873538-18304-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1453198017-13461-1-git-send-email-ykk@rock-chips.com>

Some edp screen do not have hpd signal, so we can't just return
failed when hpd plug in detect failed.

This is an hardware property, so we need add a devicetree property
"analogix,need-force-hpd" to indicate this sutiation.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Acked-by: Rob Herring <robh at kernel.org>
Tested-by: Javier Martinez Canillas <javier at osg.samsung.com>
---
There is no new changes in v12.1, but just rebase the whole patches on
top of Dave's drm-next branch [0] (Heiko)

Changes in v12.1: None
Changes in v12: None
Changes in v11:
- Rename the "analogix,need-force-hpd" to common 'force-hpd' (Rob)
- Add the ack from Rob Herring

Changes in v10: None
Changes in v9: None
Changes in v8: None
Changes in v7: None
Changes in v6: None
Changes in v5: None
Changes in v4: None
Changes in v3:
- Add "analogix,need-force-hpd" to indicate whether driver need foce
  hpd when hpd detect failed.

Changes in v2: None

 .../bindings/display/bridge/analogix_dp.txt        |  4 ++-
 .../bindings/display/exynos/exynos_dp.txt          |  1 +
 .../display/rockchip/analogix_dp-rockchip.txt      |  1 +
 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 35 ++++++++++++++++++----
 drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  2 ++
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  |  9 ++++++
 6 files changed, 46 insertions(+), 6 deletions(-)

diff --git a/Documentation/devicetree/bindings/display/bridge/analogix_dp.txt b/Documentation/devicetree/bindings/display/bridge/analogix_dp.txt
index 7659a7a..4f2ba8c 100644
--- a/Documentation/devicetree/bindings/display/bridge/analogix_dp.txt
+++ b/Documentation/devicetree/bindings/display/bridge/analogix_dp.txt
@@ -22,6 +22,9 @@ Required properties for dp-controller:
 		from general PHY binding: Should be "dp".
 
 Optional properties for dp-controller:
+	-force-hpd:
+		Indicate driver need force hpd when hpd detect failed, this
+		is used for some eDP screen which don't have hpd signal.
 	-hpd-gpios:
 		Hotplug detect GPIO.
 		Indicates which GPIO should be used for hotplug detection
@@ -31,7 +34,6 @@ Optional properties for dp-controller:
 		* Documentation/devicetree/bindings/display/exynos/exynos_dp.txt
 		* Documentation/devicetree/bindings/video/analogix_dp-rockchip.txt
 
-
 [1]: Documentation/devicetree/bindings/media/video-interfaces.txt
 -------------------------------------------------------------------------------
 
diff --git a/Documentation/devicetree/bindings/display/exynos/exynos_dp.txt b/Documentation/devicetree/bindings/display/exynos/exynos_dp.txt
index 4fa8aca..ade5d8e 100644
--- a/Documentation/devicetree/bindings/display/exynos/exynos_dp.txt
+++ b/Documentation/devicetree/bindings/display/exynos/exynos_dp.txt
@@ -56,6 +56,7 @@ For the below properties, please refer to Analogix DP binding document:
 	-phys (required)
 	-phy-names (required)
 	-hpd-gpios (optional)
+	 force-hpd (optional)
 
 Deprecated properties for DisplayPort:
 -interlaced:            deprecated prop that can parsed from drm_display_mode.
diff --git a/Documentation/devicetree/bindings/display/rockchip/analogix_dp-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/analogix_dp-rockchip.txt
index 04d99e3..e832ff9 100644
--- a/Documentation/devicetree/bindings/display/rockchip/analogix_dp-rockchip.txt
+++ b/Documentation/devicetree/bindings/display/rockchip/analogix_dp-rockchip.txt
@@ -32,6 +32,7 @@ For the below properties, please refer to Analogix DP binding document:
 - phys (required)
 - phy-names (required)
 - hpd-gpios (optional)
+- force-hpd (optional)
 -------------------------------------------------------------------------------
 
 Example:
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index a62e8d0..13986b7 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -62,15 +62,38 @@ static int analogix_dp_detect_hpd(struct analogix_dp_device *dp)
 {
 	int timeout_loop = 0;
 
-	while (analogix_dp_get_plug_in_status(dp) != 0) {
+	while (timeout_loop < DP_TIMEOUT_LOOP_COUNT) {
+		if (analogix_dp_get_plug_in_status(dp) == 0)
+			return 0;
+
 		timeout_loop++;
-		if (timeout_loop > DP_TIMEOUT_LOOP_COUNT) {
-			dev_err(dp->dev, "failed to get hpd plug status\n");
-			return -ETIMEDOUT;
-		}
 		usleep_range(10, 11);
 	}
 
+	/*
+	 * Some edp screen do not have hpd signal, so we can't just
+	 * return failed when hpd plug in detect failed, DT property
+	 * "force-hpd" would indicate whether driver need this.
+	 */
+	if (!dp->force_hpd)
+		return -ETIMEDOUT;
+
+	/*
+	 * The eDP TRM indicate that if HPD_STATUS(RO) is 0, AUX CH
+	 * will not work, so we need to give a force hpd action to
+	 * set HPD_STATUS manually.
+	 */
+	dev_dbg(dp->dev, "failed to get hpd plug status, try to force hpd\n");
+
+	analogix_dp_force_hpd(dp);
+
+	if (analogix_dp_get_plug_in_status(dp) != 0) {
+		dev_err(dp->dev, "failed to get hpd plug in status\n");
+		return -EINVAL;
+	}
+
+	dev_dbg(dp->dev, "success to get plug in status after force hpd\n");
+
 	return 0;
 }
 
@@ -1291,6 +1314,8 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev,
 	if (IS_ERR(dp->reg_base))
 		return PTR_ERR(dp->reg_base);
 
+	dp->force_hpd = of_property_read_bool(dev->of_node, "force-hpd");
+
 	dp->hpd_gpio = of_get_named_gpio(dev->of_node, "hpd-gpios", 0);
 	if (!gpio_is_valid(dp->hpd_gpio))
 		dp->hpd_gpio = of_get_named_gpio(dev->of_node,
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
index 6cc53d4..a8cba64 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
@@ -154,6 +154,7 @@ struct analogix_dp_device {
 	struct phy		*phy;
 	int			dpms_mode;
 	int			hpd_gpio;
+	bool                    force_hpd;
 
 	struct analogix_dp_plat_data *plat_data;
 };
@@ -174,6 +175,7 @@ void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp,
 				       bool enable);
 void analogix_dp_init_analog_func(struct analogix_dp_device *dp);
 void analogix_dp_init_hpd(struct analogix_dp_device *dp);
+void analogix_dp_force_hpd(struct analogix_dp_device *dp);
 enum dp_irq_type analogix_dp_get_irq_type(struct analogix_dp_device *dp);
 void analogix_dp_clear_hotplug_interrupts(struct analogix_dp_device *dp);
 void analogix_dp_reset_aux(struct analogix_dp_device *dp);
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
index 1e24b37..cba3ffd 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
@@ -365,6 +365,15 @@ void analogix_dp_init_hpd(struct analogix_dp_device *dp)
 	writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
 }
 
+void analogix_dp_force_hpd(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
+	reg = (F_HPD | HPD_CTRL);
+	writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
+}
+
 enum dp_irq_type analogix_dp_get_irq_type(struct analogix_dp_device *dp)
 {
 	u32 reg;
-- 
1.9.1




From john at metanate.com  Tue Jan 19 02:46:57 2016
From: john at metanate.com (John Keeping)
Date: Tue, 19 Jan 2016 10:46:57 +0000
Subject: [PATCH v2 0/3] drm/rockchip: fix cursor performance with atomic
In-Reply-To: <cover.1452782114.git.john@metanate.com>
References: <cover.1452782114.git.john@metanate.com>
Message-ID: <cover.1453200263.git.john@metanate.com>

The first two patches are unchanged since v1 but the comment in the
third has been expanded following Thierry's comments.

John Keeping (3):
  drm/atomic-helper: Export framebuffer_changed()
  drm/rockchip: don't wait for vblank if fb hasn't changed
  drm/rockchip: explain why we can't wait_for_vblanks

 drivers/gpu/drm/drm_atomic_helper.c        | 24 ++++++++++++++++++++----
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 23 +++++++++++++++++++++--
 include/drm/drm_atomic_helper.h            |  4 ++++
 3 files changed, 45 insertions(+), 6 deletions(-)

-- 
2.7.0.226.gfe986fe



From john at metanate.com  Tue Jan 19 02:46:59 2016
From: john at metanate.com (John Keeping)
Date: Tue, 19 Jan 2016 10:46:59 +0000
Subject: [PATCH v2 2/3] drm/rockchip: don't wait for vblank if fb hasn't
 changed
In-Reply-To: <cover.1453200263.git.john@metanate.com>
References: <cover.1453200263.git.john@metanate.com>
Message-ID: <6865056b134065bcc8fff798314945e2ba5318cf.1453200263.git.john@metanate.com>

As commented in drm_atomic_helper_wait_for_vblanks(), userspace relies
on cursor ioctls being unsynced.  Converting the rockchip driver to
atomic has significantly impacted cursor performance by making every
cursor update wait for vblank.

By skipping the vblank sync when the framebuffer has not changed (as is
done in drm_atomic_helper_wait_for_vblanks()) we can avoid this for the
common case of moving the cursor and only need to delay the cursor ioctl
when the cursor icon changes.

We cannot add the check on legacy_cursor_update since that results in
the cursor bo being unreferenced while the hardware may still be reading
it.  Fully supporting unsynced cursor updates is left for the future
when the atomic helper framework supports async updates.

Signed-off-by: John Keeping <john at metanate.com>
Tested-by: Heiko Stuebner <heiko at sntech.de>
---
Unchanged since v1.

 drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
index f784488..679d23a 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -178,7 +178,7 @@ static void rockchip_crtc_wait_for_update(struct drm_crtc *crtc)
 }
 
 static void
-rockchip_atomic_wait_for_complete(struct drm_atomic_state *old_state)
+rockchip_atomic_wait_for_complete(struct drm_device *dev, struct drm_atomic_state *old_state)
 {
 	struct drm_crtc_state *old_crtc_state;
 	struct drm_crtc *crtc;
@@ -194,6 +194,10 @@ rockchip_atomic_wait_for_complete(struct drm_atomic_state *old_state)
 		if (!crtc->state->active)
 			continue;
 
+		if (!drm_atomic_helper_framebuffer_changed(dev,
+				old_state, crtc))
+			continue;
+
 		ret = drm_crtc_vblank_get(crtc);
 		if (ret != 0)
 			continue;
@@ -241,7 +245,7 @@ rockchip_atomic_commit_complete(struct rockchip_atomic_commit *commit)
 
 	drm_atomic_helper_commit_planes(dev, state, true);
 
-	rockchip_atomic_wait_for_complete(state);
+	rockchip_atomic_wait_for_complete(dev, state);
 
 	drm_atomic_helper_cleanup_planes(dev, state);
 
-- 
2.7.0.226.gfe986fe



From john at metanate.com  Tue Jan 19 02:47:00 2016
From: john at metanate.com (John Keeping)
Date: Tue, 19 Jan 2016 10:47:00 +0000
Subject: [PATCH v2 3/3] drm/rockchip: explain why we can't wait_for_vblanks
In-Reply-To: <cover.1453200263.git.john@metanate.com>
References: <cover.1453200263.git.john@metanate.com>
Message-ID: <921a9f91937bebfeef7d7f563339fe6e4bf63f67.1453200263.git.john@metanate.com>

Signed-off-by: John Keeping <john at metanate.com>
---
v2:
  - Add more detail of the particular race that could happen if we used
    drm_atomic_helper_wait_for_vblanks().

 drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
index 679d23a..cf0b7bd 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -177,6 +177,21 @@ static void rockchip_crtc_wait_for_update(struct drm_crtc *crtc)
 		crtc_funcs->wait_for_update(crtc);
 }
 
+/*
+ * We can't use drm_atomic_helper_wait_for_vblanks() because rk3288 and rk3066
+ * have hardware counters for neither vblanks nor scanlines, which results in
+ * a race where:
+ *				| <-- HW vsync irq and reg take effect
+ *	       plane_commit --> |
+ *	get_vblank and wait --> |
+ *				| <-- handle_vblank, vblank->count + 1
+ *		 cleanup_fb --> |
+ *		iommu crash --> |
+ *				| <-- HW vsync irq and reg take effect
+ *
+ * This function is equivalent but uses rockchip_crtc_wait_for_update() instead
+ * of waiting for vblank_count to change.
+ */
 static void
 rockchip_atomic_wait_for_complete(struct drm_device *dev, struct drm_atomic_state *old_state)
 {
-- 
2.7.0.226.gfe986fe



From john at metanate.com  Tue Jan 19 02:46:58 2016
From: john at metanate.com (John Keeping)
Date: Tue, 19 Jan 2016 10:46:58 +0000
Subject: [PATCH v2 1/3] drm/atomic-helper: Export framebuffer_changed()
In-Reply-To: <cover.1453200263.git.john@metanate.com>
References: <cover.1453200263.git.john@metanate.com>
Message-ID: <f89bf1d5b69d52656f7125b7c240ddd525f27930.1453200263.git.john@metanate.com>

The Rockchip driver cannot use drm_atomic_helper_wait_for_vblanks()
because it has hardware counters for neither vblanks nor scanlines.

In order to simplify re-implementing the functionality for this driver,
export the framebuffer_changed() helper so it can be reused.

Signed-off-by: John Keeping <john at metanate.com>
Reviewed-by: Daniel Vetter <daniel.vetter at ffwll.ch>
---
Unchanged since v1.

 drivers/gpu/drm/drm_atomic_helper.c | 24 ++++++++++++++++++++----
 include/drm/drm_atomic_helper.h     |  4 ++++
 2 files changed, 24 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 268d37f..7449293 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -948,9 +948,23 @@ static void wait_for_fences(struct drm_device *dev,
 	}
 }
 
-static bool framebuffer_changed(struct drm_device *dev,
-				struct drm_atomic_state *old_state,
-				struct drm_crtc *crtc)
+/**
+ * drm_atomic_helper_framebuffer_changed - check if framebuffer has changed
+ * @dev: DRM device
+ * @old_state: atomic state object with old state structures
+ * @crtc: DRM crtc
+ *
+ * Checks whether the framebuffer used for this CRTC changes as a result of
+ * the atomic update.  This is useful for drivers which cannot use
+ * drm_atomic_helper_wait_for_vblanks() and need to reimplement its
+ * functionality.
+ *
+ * Returns:
+ * true if the framebuffer changed.
+ */
+bool drm_atomic_helper_framebuffer_changed(struct drm_device *dev,
+					   struct drm_atomic_state *old_state,
+					   struct drm_crtc *crtc)
 {
 	struct drm_plane *plane;
 	struct drm_plane_state *old_plane_state;
@@ -967,6 +981,7 @@ static bool framebuffer_changed(struct drm_device *dev,
 
 	return false;
 }
+EXPORT_SYMBOL(drm_atomic_helper_framebuffer_changed);
 
 /**
  * drm_atomic_helper_wait_for_vblanks - wait for vblank on crtcs
@@ -1001,7 +1016,8 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev,
 		if (old_state->legacy_cursor_update)
 			continue;
 
-		if (!framebuffer_changed(dev, old_state, crtc))
+		if (!drm_atomic_helper_framebuffer_changed(dev,
+				old_state, crtc))
 			continue;
 
 		ret = drm_crtc_vblank_get(crtc);
diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h
index a286cce..74fce78 100644
--- a/include/drm/drm_atomic_helper.h
+++ b/include/drm/drm_atomic_helper.h
@@ -42,6 +42,10 @@ int drm_atomic_helper_commit(struct drm_device *dev,
 			     struct drm_atomic_state *state,
 			     bool async);
 
+bool drm_atomic_helper_framebuffer_changed(struct drm_device *dev,
+					   struct drm_atomic_state *old_state,
+					   struct drm_crtc *crtc);
+
 void drm_atomic_helper_wait_for_vblanks(struct drm_device *dev,
 					struct drm_atomic_state *old_state);
 
-- 
2.7.0.226.gfe986fe



From daniel at ffwll.ch  Tue Jan 19 03:03:34 2016
From: daniel at ffwll.ch (Daniel Vetter)
Date: Tue, 19 Jan 2016 12:03:34 +0100
Subject: [PATCH v2 1/3] drm/atomic-helper: Export framebuffer_changed()
In-Reply-To: <f89bf1d5b69d52656f7125b7c240ddd525f27930.1453200263.git.john@metanate.com>
References: <cover.1452782114.git.john@metanate.com>
 <cover.1453200263.git.john@metanate.com>
 <f89bf1d5b69d52656f7125b7c240ddd525f27930.1453200263.git.john@metanate.com>
Message-ID: <20160119110334.GA19130@phenom.ffwll.local>

On Tue, Jan 19, 2016 at 10:46:58AM +0000, John Keeping wrote:
> The Rockchip driver cannot use drm_atomic_helper_wait_for_vblanks()
> because it has hardware counters for neither vblanks nor scanlines.
> 
> In order to simplify re-implementing the functionality for this driver,
> export the framebuffer_changed() helper so it can be reused.
> 
> Signed-off-by: John Keeping <john at metanate.com>
> Reviewed-by: Daniel Vetter <daniel.vetter at ffwll.ch>

Also ack for merging through rockchip git trees. I discussed this with
Dave Airlie, he's ok with that.
-Daniel

> ---
> Unchanged since v1.
> 
>  drivers/gpu/drm/drm_atomic_helper.c | 24 ++++++++++++++++++++----
>  include/drm/drm_atomic_helper.h     |  4 ++++
>  2 files changed, 24 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> index 268d37f..7449293 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -948,9 +948,23 @@ static void wait_for_fences(struct drm_device *dev,
>  	}
>  }
>  
> -static bool framebuffer_changed(struct drm_device *dev,
> -				struct drm_atomic_state *old_state,
> -				struct drm_crtc *crtc)
> +/**
> + * drm_atomic_helper_framebuffer_changed - check if framebuffer has changed
> + * @dev: DRM device
> + * @old_state: atomic state object with old state structures
> + * @crtc: DRM crtc
> + *
> + * Checks whether the framebuffer used for this CRTC changes as a result of
> + * the atomic update.  This is useful for drivers which cannot use
> + * drm_atomic_helper_wait_for_vblanks() and need to reimplement its
> + * functionality.
> + *
> + * Returns:
> + * true if the framebuffer changed.
> + */
> +bool drm_atomic_helper_framebuffer_changed(struct drm_device *dev,
> +					   struct drm_atomic_state *old_state,
> +					   struct drm_crtc *crtc)
>  {
>  	struct drm_plane *plane;
>  	struct drm_plane_state *old_plane_state;
> @@ -967,6 +981,7 @@ static bool framebuffer_changed(struct drm_device *dev,
>  
>  	return false;
>  }
> +EXPORT_SYMBOL(drm_atomic_helper_framebuffer_changed);
>  
>  /**
>   * drm_atomic_helper_wait_for_vblanks - wait for vblank on crtcs
> @@ -1001,7 +1016,8 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev,
>  		if (old_state->legacy_cursor_update)
>  			continue;
>  
> -		if (!framebuffer_changed(dev, old_state, crtc))
> +		if (!drm_atomic_helper_framebuffer_changed(dev,
> +				old_state, crtc))
>  			continue;
>  
>  		ret = drm_crtc_vblank_get(crtc);
> diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h
> index a286cce..74fce78 100644
> --- a/include/drm/drm_atomic_helper.h
> +++ b/include/drm/drm_atomic_helper.h
> @@ -42,6 +42,10 @@ int drm_atomic_helper_commit(struct drm_device *dev,
>  			     struct drm_atomic_state *state,
>  			     bool async);
>  
> +bool drm_atomic_helper_framebuffer_changed(struct drm_device *dev,
> +					   struct drm_atomic_state *old_state,
> +					   struct drm_crtc *crtc);
> +
>  void drm_atomic_helper_wait_for_vblanks(struct drm_device *dev,
>  					struct drm_atomic_state *old_state);
>  
> -- 
> 2.7.0.226.gfe986fe
> 

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


From tomeu at tomeuvizoso.net  Tue Jan 19 04:02:34 2016
From: tomeu at tomeuvizoso.net (Tomeu Vizoso)
Date: Tue, 19 Jan 2016 13:02:34 +0100
Subject: [PATCH 0/5] clk: rockchip: add full support for HDMI clock on
 rk3288
In-Reply-To: <CAD=FV=Vzec3Qm-kc6zZ4VxSREoZbzVkWbzp-noHYdcEVrLYYSw@mail.gmail.com>
References: <1415087559-19444-1-git-send-email-kever.yang@rock-chips.com>
 <48806799.thBSifdUp8@diego> <54647142.4080800@rock-chips.com>
 <CAD=FV=Vzec3Qm-kc6zZ4VxSREoZbzVkWbzp-noHYdcEVrLYYSw@mail.gmail.com>
Message-ID: <CAAObsKChYN5nmcm5OWnAzNRGDi_QGSoKvN+0FizhjeT-3mmeXQ@mail.gmail.com>

On 13 November 2014 at 23:59, Doug Anderson <dianders at chromium.org> wrote:
> Hi,
>
> On Thu, Nov 13, 2014 at 12:52 AM, Kever Yang <kever.yang at rock-chips.com> wrote:
>> Hi Heiko,
>>
>> On 11/07/2014 05:06 AM, Heiko St?bner wrote:
>>>
>>> Hi Kever,
>>>
>>> Am Dienstag, 4. November 2014, 15:52:34 schrieb Kever Yang:
>>>>
>>>> we are going to make a clock usage solution for rk3288:
>>>> 1. CPLL and GPLL always not change after assign init;
>>>> 2. NPLL default as 500MHz, may used for most scene;
>>>> 3. NPLL may be changed by VOP(HDMI) clock for some special
>>>>     frequency requirement.
>>>>
>>>>      I test it with rk3288 evb on top of Heiko's clk-for-next
>>>
>>> In general I'm not really sure if allowing one component to arbitarily
>>> change
>>> a shared clock wouldn't result in trouble.
>>>
>>> At the moment only dclk_vop0 is included in your series, while the hdmi
>>> controller can connect to both vop0 and vop1.
>>> And as Doug mentioned the gpu also has the npll as one possible source.
>>
>> I think the problem GPU HANGs with 480MHz clock from usbphy has
>> been fixed with my patch to gerrit:
>> https://chromium-review.googlesource.com/#/c/229554/
>>>
>>>
>>> Looking through the clock-tree there are a lot more components possibly
>>> using
>>> (or wanting to use) the npll: of course the VOPs, the edp, hdmi, isp,
>>> hevc,
>>> gpu, tsp uart0 and gmac. So I'm slightly uncomfortable with somehow
>>> reserving
>>> the npll for VOP0 alone.
>>
>> It's true that I customized the usage of npll for VOP0 when we need some
>> very special frequency, but it doesn't means other modules can't use the
>> npll, it will always decided by clock core for module clocks that which
>> parent
>> is the best.
>
> We will just need to be very careful.  As I've mentioned in the past
> we'll need to think about what happens to other clocks that happen to
> be parented by NPLL whenever we change it.
>
> So if we do this:
>
> 1. NPLL happens to be 500MHz.
> 2. We set GPU to be 500MHz and it picks NPLL.
> 3. We change NPLL to a different speed (like 600MHz).
>
> ...I believe in this scenario the GPU would start running at 600MHz
> immediately.  We'd need to add special code to watch out for this and
> pick an alternate clock for the GPU (like the USB 480) before the NPLL
> change.  If NPLL later changes back to 500MHz and the GPU still wanted
> 500MHz, we'd have to decide what to do.
>
> The summary is: it's pretty complicated

Hello,

I have heard that this is still an issue in RK3288 support in mainline
so have given a look and wonder if you have considered having the
consumers of a shared clock (which could be a child clock) use
PRE_RATE_CHANGE notifications to adapt to changes in a parent's rate,
or to abort them if that's not possible.

Alternatively, a consumer can set min and max constraints if it
doesn't want for others to mess with its provider, and let the other
consumer deal with failed rate changes (probably by choosing another
provider).

Have I missed any problems with that?

Thanks,

Tomeu


From paweljarosz3691 at gmail.com  Tue Jan 19 04:09:18 2016
From: paweljarosz3691 at gmail.com (=?UTF-8?q?Pawe=C5=82=20Jarosz?=)
Date: Tue, 19 Jan 2016 13:09:18 +0100
Subject: [PATCH 0/3] Add rk3066 tsadc device tree bindings
Message-ID: <1453205361-27267-1-git-send-email-paweljarosz3691@gmail.com>

This patchset adds devicetree bindings for rockchip rk3066 tsadc with needed dependencies.

Pawe? Jarosz (3):
  clk: rockchip: Add new id for rk3066 tsadc clock
  clk: rockchip: add tsadc clock on rk3066
  ARM: dts: rockchip: add tsadc node

 arch/arm/boot/dts/rk3066a.dtsi                | 11 +++++++++++
 drivers/clk/rockchip/clk-rk3188.c             |  2 +-
 include/dt-bindings/clock/rk3188-cru-common.h |  1 +
 3 files changed, 13 insertions(+), 1 deletion(-)

-- 
1.9.1



From paweljarosz3691 at gmail.com  Tue Jan 19 04:09:20 2016
From: paweljarosz3691 at gmail.com (=?UTF-8?q?Pawe=C5=82=20Jarosz?=)
Date: Tue, 19 Jan 2016 13:09:20 +0100
Subject: [PATCH 2/3] clk: rockchip: add tsadc clock on rk3066
In-Reply-To: <1453205361-27267-1-git-send-email-paweljarosz3691@gmail.com>
References: <1453205361-27267-1-git-send-email-paweljarosz3691@gmail.com>
Message-ID: <1453205361-27267-3-git-send-email-paweljarosz3691@gmail.com>

Set clock id for sclk_tsadc gating clock of tsadc in rk3066

Signed-off-by: Pawe? Jarosz <paweljarosz3691 at gmail.com>
---
 drivers/clk/rockchip/clk-rk3188.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c
index 7f7444c..fda4e77 100644
--- a/drivers/clk/rockchip/clk-rk3188.c
+++ b/drivers/clk/rockchip/clk-rk3188.c
@@ -605,7 +605,7 @@ static struct rockchip_clk_branch rk3066a_clk_branches[] __initdata = {
 	GATE(SCLK_TIMER2, "timer2", "xin24m", 0,
 			RK2928_CLKGATE_CON(3), 2, GFLAGS),
 
-	COMPOSITE_NOMUX(0, "sclk_tsadc", "xin24m", 0,
+	COMPOSITE_NOMUX(SCLK_TSADC, "sclk_tsadc", "xin24m", 0,
 			RK2928_CLKSEL_CON(34), 0, 16, DFLAGS,
 			RK2928_CLKGATE_CON(2), 15, GFLAGS),
 
-- 
1.9.1



From paweljarosz3691 at gmail.com  Tue Jan 19 04:09:19 2016
From: paweljarosz3691 at gmail.com (=?UTF-8?q?Pawe=C5=82=20Jarosz?=)
Date: Tue, 19 Jan 2016 13:09:19 +0100
Subject: [PATCH 1/3] clk: rockchip: Add new id for rk3066 tsadc clock
In-Reply-To: <1453205361-27267-1-git-send-email-paweljarosz3691@gmail.com>
References: <1453205361-27267-1-git-send-email-paweljarosz3691@gmail.com>
Message-ID: <1453205361-27267-2-git-send-email-paweljarosz3691@gmail.com>

This patch adds new id for the sclk supplying the tsadc on rk3066 socs.

Signed-off-by: Pawe? Jarosz <paweljarosz3691 at gmail.com>
---
 include/dt-bindings/clock/rk3188-cru-common.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/dt-bindings/clock/rk3188-cru-common.h b/include/dt-bindings/clock/rk3188-cru-common.h
index 8df77a7..4f53e70 100644
--- a/include/dt-bindings/clock/rk3188-cru-common.h
+++ b/include/dt-bindings/clock/rk3188-cru-common.h
@@ -55,6 +55,7 @@
 #define SCLK_TIMER6		90
 #define SCLK_JTAG		91
 #define SCLK_SMC		92
+#define SCLK_TSADC		93
 
 #define DCLK_LCDC0		190
 #define DCLK_LCDC1		191
-- 
1.9.1



From paweljarosz3691 at gmail.com  Tue Jan 19 04:09:21 2016
From: paweljarosz3691 at gmail.com (=?UTF-8?q?Pawe=C5=82=20Jarosz?=)
Date: Tue, 19 Jan 2016 13:09:21 +0100
Subject: [PATCH 3/3] ARM: dts: rockchip: add tsadc node
In-Reply-To: <1453205361-27267-1-git-send-email-paweljarosz3691@gmail.com>
References: <1453205361-27267-1-git-send-email-paweljarosz3691@gmail.com>
Message-ID: <1453205361-27267-4-git-send-email-paweljarosz3691@gmail.com>

Add the device node for the TSADC found on rk3066.

Signed-off-by: Pawe? Jarosz <paweljarosz3691 at gmail.com>
---
 arch/arm/boot/dts/rk3066a.dtsi | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/arch/arm/boot/dts/rk3066a.dtsi b/arch/arm/boot/dts/rk3066a.dtsi
index 58bac50..adb0258 100644
--- a/arch/arm/boot/dts/rk3066a.dtsi
+++ b/arch/arm/boot/dts/rk3066a.dtsi
@@ -188,6 +188,17 @@
 		clock-names = "timer", "pclk";
 	};
 
+	tsadc: tsadc at 20060000 {
+		compatible = "rockchip,rk3066-tsadc";
+		reg = <0x20060000 0x100>;
+		interrupts = <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
+		#io-channel-cells = <1>;
+		clocks = <&cru SCLK_TSADC>, <&cru PCLK_TSADC>;
+		clock-names = "saradc", "apb_pclk";
+		vref-supply = <&vcc_io>;
+		status = "disabled";
+	};
+
 	usbphy: phy {
 		compatible = "rockchip,rk3066a-usb-phy", "rockchip,rk3288-usb-phy";
 		rockchip,grf = <&grf>;
-- 
1.9.1



From heiko at sntech.de  Tue Jan 19 07:29:34 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Tue, 19 Jan 2016 16:29:34 +0100
Subject: [PATCH v7 4/5] ARM: dts: rockchip: add rk3288 mipi_dsi nodes
In-Reply-To: <1452053038-32098-5-git-send-email-zyw@rock-chips.com>
References: <1452053038-32098-1-git-send-email-zyw@rock-chips.com>
 <1452053038-32098-5-git-send-email-zyw@rock-chips.com>
Message-ID: <1528812.keQSB3ssvU@phil>

Am Mittwoch, 6. Januar 2016, 12:03:56 schrieb Chris Zhong:
> Add a mipi_dsi node, and also add mipi_dsi endpoints to vopb and vopl
> output port nodes.
> 
> Signed-off-by: Chris Zhong <zyw at rock-chips.com>

applied to my dts32 branch for 4.6

after moving it to the correct position.
Please make sure to sort nodes by their register addresses in the future.


Thanks
Heiko


From heiko at sntech.de  Tue Jan 19 11:26:04 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Tue, 19 Jan 2016 20:26:04 +0100
Subject: [PATCH v7 5/5] ARM: dts: rockchip: add support mipi panel
 tv080wum-nl0
In-Reply-To: <1452053038-32098-6-git-send-email-zyw@rock-chips.com>
References: <1452053038-32098-1-git-send-email-zyw@rock-chips.com>
 <1452053038-32098-6-git-send-email-zyw@rock-chips.com>
Message-ID: <1649978.zzK0Pex4sR@phil>

Hi Chris,

Am Mittwoch, 6. Januar 2016, 12:03:57 schrieb Chris Zhong:
> This tv080wum-nl0 panel is a mipi panel, it can use in MIPI_TX socket
> of rk3288 evb board.
> 
> Signed-off-by: Chris Zhong <zyw at rock-chips.com>

[...]

> diff --git a/arch/arm/boot/dts/rk3288-evb-act8846.dts
> b/arch/arm/boot/dts/rk3288-evb-act8846.dts index e662147..7d5475b 100644
> --- a/arch/arm/boot/dts/rk3288-evb-act8846.dts
> +++ b/arch/arm/boot/dts/rk3288-evb-act8846.dts
> @@ -50,6 +50,8 @@
>  		gpio = <&gpio7 3 GPIO_ACTIVE_HIGH>;
>  		pinctrl-names = "default";
>  		pinctrl-0 = <&lcd_en>;
> +		regulator-always-on;
> +		regulator-boot-on;

Is this only needed for the panel itself and can you try to get the panel 
supplies set up correctly instead in that case please?


>  		regulator-name = "vcc_lcd";
>  		vin-supply = <&vcc_io>;
>  	};
> diff --git a/arch/arm/boot/dts/rk3288-evb.dtsi
> b/arch/arm/boot/dts/rk3288-evb.dtsi index 4faabdb..0c91858 100644
> --- a/arch/arm/boot/dts/rk3288-evb.dtsi
> +++ b/arch/arm/boot/dts/rk3288-evb.dtsi
> @@ -47,7 +47,7 @@
>  		reg = <0x0 0x80000000>;
>  	};
> 
> -	backlight {
> +	backlight: backlight {
>  		compatible = "pwm-backlight";
>  		brightness-levels = <
>  			  0   1   2   3   4   5   6   7
> @@ -183,6 +183,16 @@
>  	status = "okay";
>  };
> 
> +&mipi_dsi {
> +	status = "okay";
> +	panel {
> +		compatible ="boe,tv080wum-nl0";
> +		reg = <0>;
> +		backlight = <&backlight>;
> +		status = "okay";

in the evb-schematics I see, vcc_lcd as supply for the panel.

So I'd think the panel node here should have a
	power-supply = <&vcc_lcd>;
instead of having the always-on above?


Thanks
Heiko

> +	};
> +};
> +
>  &sdmmc {
>  	bus-width = <4>;
>  	cap-mmc-highspeed;



From heiko at sntech.de  Tue Jan 19 13:50:35 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Tue, 19 Jan 2016 22:50:35 +0100
Subject: [PATCH 0/2] Add support for wireless on Radxa Rock2
In-Reply-To: <1452344058-27434-1-git-send-email-sjoerd.simons@collabora.co.uk>
References: <1452344058-27434-1-git-send-email-sjoerd.simons@collabora.co.uk>
Message-ID: <2280130.ybTTBxI3rJ@phil>

Am Samstag, 9. Januar 2016, 13:54:16 schrieb Sjoerd Simons:
> First patch improved the modelling of the power supplies on the Rock2
> some a bit and adds the iodomain setup needed for wireless to work.
> Second patch adds the actual updates for the wireless setup on the rock2
> square board.
> 
> With current kernels the probing of the wifi card is unfortunately not
> reliable as the 32k clock is provided by the external RTC module, this
> is fixed by a patch from Heiko still in RFC state[0] to properly defer
> things as needed. Also things only work if the io domains get
> initialized before the sdio bus is probed as their is no explicit
> dependency, fixing that is for a later patchset.

applied both to my dts32 branch for 4.6


Thaks
Heiko


From zyw at rock-chips.com  Tue Jan 19 16:50:33 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Wed, 20 Jan 2016 08:50:33 +0800
Subject: [PATCH v7 4/5] ARM: dts: rockchip: add rk3288 mipi_dsi nodes
In-Reply-To: <1528812.keQSB3ssvU@phil>
References: <1452053038-32098-1-git-send-email-zyw@rock-chips.com>
 <1452053038-32098-5-git-send-email-zyw@rock-chips.com>
 <1528812.keQSB3ssvU@phil>
Message-ID: <569ED9D9.5030401@rock-chips.com>

Hi Heiko

On 01/19/2016 11:29 PM, Heiko Stuebner wrote:
> Am Mittwoch, 6. Januar 2016, 12:03:56 schrieb Chris Zhong:
>> Add a mipi_dsi node, and also add mipi_dsi endpoints to vopb and vopl
>> output port nodes.
>>
>> Signed-off-by: Chris Zhong <zyw at rock-chips.com>
> applied to my dts32 branch for 4.6
>
> after moving it to the correct position.
> Please make sure to sort nodes by their register addresses in the future.
Thanks Heiko. Next time I will pay attention.
>
>
> Thanks
> Heiko
>
>
>



From zyw at rock-chips.com  Tue Jan 19 17:09:03 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Wed, 20 Jan 2016 09:09:03 +0800
Subject: [PATCH v7 5/5] ARM: dts: rockchip: add support mipi panel
 tv080wum-nl0
In-Reply-To: <1649978.zzK0Pex4sR@phil>
References: <1452053038-32098-1-git-send-email-zyw@rock-chips.com>
 <1452053038-32098-6-git-send-email-zyw@rock-chips.com>
 <1649978.zzK0Pex4sR@phil>
Message-ID: <569EDE2F.6060400@rock-chips.com>

Hi Heiko

On 01/20/2016 03:26 AM, Heiko Stuebner wrote:
> Hi Chris,
>
> Am Mittwoch, 6. Januar 2016, 12:03:57 schrieb Chris Zhong:
>> This tv080wum-nl0 panel is a mipi panel, it can use in MIPI_TX socket
>> of rk3288 evb board.
>>
>> Signed-off-by: Chris Zhong <zyw at rock-chips.com>
> [...]
>
>> diff --git a/arch/arm/boot/dts/rk3288-evb-act8846.dts
>> b/arch/arm/boot/dts/rk3288-evb-act8846.dts index e662147..7d5475b 100644
>> --- a/arch/arm/boot/dts/rk3288-evb-act8846.dts
>> +++ b/arch/arm/boot/dts/rk3288-evb-act8846.dts
>> @@ -50,6 +50,8 @@
>>   		gpio = <&gpio7 3 GPIO_ACTIVE_HIGH>;
>>   		pinctrl-names = "default";
>>   		pinctrl-0 = <&lcd_en>;
>> +		regulator-always-on;
>> +		regulator-boot-on;
> Is this only needed for the panel itself and can you try to get the panel
> supplies set up correctly instead in that case please?
It is not only for panel, LCDC_VDD need this supply too.
And rk3288-evb-rk808.dts has set this regulator to always on, so I do 
the same thing in here.
>
>
>>   		regulator-name = "vcc_lcd";
>>   		vin-supply = <&vcc_io>;
>>   	};
>> diff --git a/arch/arm/boot/dts/rk3288-evb.dtsi
>> b/arch/arm/boot/dts/rk3288-evb.dtsi index 4faabdb..0c91858 100644
>> --- a/arch/arm/boot/dts/rk3288-evb.dtsi
>> +++ b/arch/arm/boot/dts/rk3288-evb.dtsi
>> @@ -47,7 +47,7 @@
>>   		reg = <0x0 0x80000000>;
>>   	};
>>
>> -	backlight {
>> +	backlight: backlight {
>>   		compatible = "pwm-backlight";
>>   		brightness-levels = <
>>   			  0   1   2   3   4   5   6   7
>> @@ -183,6 +183,16 @@
>>   	status = "okay";
>>   };
>>
>> +&mipi_dsi {
>> +	status = "okay";
>> +	panel {
>> +		compatible ="boe,tv080wum-nl0";
>> +		reg = <0>;
>> +		backlight = <&backlight>;
>> +		status = "okay";
> in the evb-schematics I see, vcc_lcd as supply for the panel.
>
> So I'd think the panel node here should have a
> 	power-supply = <&vcc_lcd>;
> instead of having the always-on above?
>
>
> Thanks
> Heiko
>
>> +	};
>> +};
>> +
>>   &sdmmc {
>>   	bus-width = <4>;
>>   	cap-mmc-highspeed;
>
>
>



From zhangqing at rock-chips.com  Wed Jan 20 02:28:05 2016
From: zhangqing at rock-chips.com (zhangqing)
Date: Wed, 20 Jan 2016 02:28:05 -0800
Subject: [PATCH v3 0/3] SoC: rockchip: support power domain for RK3368
In-Reply-To: <1452508600-3512-1-git-send-email-zhangqing@rock-chips.com>
References: <1452508600-3512-1-git-send-email-zhangqing@rock-chips.com>
Message-ID: <569F6135.1070902@rock-chips.com>



On 01/11/2016 02:36 AM, zhangqing wrote:
> add pd id in rk3368-power.h
> modify pm_domains.c to support RK3368 SoC
> add RK3368 document for Rockchip power domains
>
> zhangqing (3):
>    dt-bindings: add power-domain header for RK3368 SoCs
>    soc: rockchip: power-domain: Modify power domain driver for rk3368
>    dt-bindings: modify document of Rockchip power domains

Are there any other questions about the three patches.Please review.
>
>   .../bindings/soc/rockchip/power_domain.txt         | 21 ++++++++++++++
>   drivers/soc/rockchip/pm_domains.c                  | 33 ++++++++++++++++++++++
>   include/dt-bindings/power/rk3368-power.h           | 28 ++++++++++++++++++
>   3 files changed, 82 insertions(+)
>   create mode 100644 include/dt-bindings/power/rk3368-power.h
>



From heiko at sntech.de  Wed Jan 20 00:26:45 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Wed, 20 Jan 2016 09:26:45 +0100
Subject: [PATCH v3 0/3] SoC: rockchip: support power domain for RK3368
In-Reply-To: <569F6135.1070902@rock-chips.com>
References: <1452508600-3512-1-git-send-email-zhangqing@rock-chips.com>
 <569F6135.1070902@rock-chips.com>
Message-ID: <3061119.C8moAIEzLL@phil>

Hi Elaine,

Am Mittwoch, 20. Januar 2016, 02:28:05 schrieb zhangqing:
> On 01/11/2016 02:36 AM, zhangqing wrote:
> > add pd id in rk3368-power.h
> > modify pm_domains.c to support RK3368 SoC
> > add RK3368 document for Rockchip power domains
> > 
> > zhangqing (3):
> >    dt-bindings: add power-domain header for RK3368 SoCs
> >    soc: rockchip: power-domain: Modify power domain driver for rk3368
> >    dt-bindings: modify document of Rockchip power domains
> 
> Are there any other questions about the three patches.Please review.

actually the patches look fine. I just need to wait for 4.5-rc1 to be 
released (probably sunday) before I can apply them.


Heiko

> 
> >   .../bindings/soc/rockchip/power_domain.txt         | 21 ++++++++++++++
> >   drivers/soc/rockchip/pm_domains.c                  | 33
> >   ++++++++++++++++++++++ include/dt-bindings/power/rk3368-power.h      
> >       | 28 ++++++++++++++++++ 3 files changed, 82 insertions(+)
> >   create mode 100644 include/dt-bindings/power/rk3368-power.h



From zhengxing at rock-chips.com  Wed Jan 20 00:37:17 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Wed, 20 Jan 2016 16:37:17 +0800
Subject: [PATCH] clk: rockchip: rk3036: Add apll as the critical clock
Message-ID: <1453279037-23971-1-git-send-email-zhengxing@rock-chips.com>

The apll may be closed if there are some child clock nodes below
it when the device startup. Therefore, the apll should be keep
critical.

The apll tree like this:
    pll_apll
       apll
          armclk
             pclk_dbg
             aclk_core_pre
          aclk_hvec
          uart_pll_clk
             uart2_src
                uart2_frac
             uart1_src
                uart1_frac
             uart0_src
                uart0_frac

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
---

 drivers/clk/rockchip/clk-rk3036.c |    1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/clk/rockchip/clk-rk3036.c b/drivers/clk/rockchip/clk-rk3036.c
index ebce980..483913b 100644
--- a/drivers/clk/rockchip/clk-rk3036.c
+++ b/drivers/clk/rockchip/clk-rk3036.c
@@ -425,6 +425,7 @@ static struct rockchip_clk_branch rk3036_clk_branches[] __initdata = {
 };
 
 static const char *const rk3036_critical_clocks[] __initconst = {
+	"apll",
 	"aclk_cpu",
 	"aclk_peri",
 	"hclk_peri",
-- 
1.7.9.5




From zhengxing at rock-chips.com  Wed Jan 20 00:40:01 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Wed, 20 Jan 2016 16:40:01 +0800
Subject: [PATCH] clk: rockchip: Add support for multiple clock providers
Message-ID: <1453279201-24086-1-git-send-email-zhengxing@rock-chips.com>

There are need to support Multi-CRUs probability in future, but
it is not supported on the current Rockchip Clock Framework.

Therefore, this patch add support a provider as the parameter
handler when we call the clock register functions for per CRU.

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
---

 drivers/clk/rockchip/clk-rk3036.c |   15 +++--
 drivers/clk/rockchip/clk-rk3188.c |   38 +++++++-----
 drivers/clk/rockchip/clk-rk3228.c |   13 ++--
 drivers/clk/rockchip/clk-rk3288.c |   15 +++--
 drivers/clk/rockchip/clk-rk3368.c |   17 +++---
 drivers/clk/rockchip/clk.c        |  120 +++++++++++++++++++++++--------------
 drivers/clk/rockchip/clk.h        |   35 ++++++++---
 7 files changed, 162 insertions(+), 91 deletions(-)

diff --git a/drivers/clk/rockchip/clk-rk3036.c b/drivers/clk/rockchip/clk-rk3036.c
index 483913b..050ad13 100644
--- a/drivers/clk/rockchip/clk-rk3036.c
+++ b/drivers/clk/rockchip/clk-rk3036.c
@@ -434,6 +434,7 @@ static const char *const rk3036_critical_clocks[] __initconst = {
 
 static void __init rk3036_clk_init(struct device_node *np)
 {
+	struct rockchip_clk_provider *ctx;
 	void __iomem *reg_base;
 	struct clk *clk;
 
@@ -443,7 +444,9 @@ static void __init rk3036_clk_init(struct device_node *np)
 		return;
 	}
 
-	rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
+	ctx = rockchip_clk_init(reg_base, CLK_NR_CLKS);
+
+	rockchip_clk_common_cru_init(np);
 
 	/* xin12m is created by an cru-internal divider */
 	clk = clk_register_fixed_factor(NULL, "xin12m", "xin24m", 0, 1, 2);
@@ -473,15 +476,15 @@ static void __init rk3036_clk_init(struct device_node *np)
 		pr_warn("%s: could not register clock sclk_macref_out: %ld\n",
 			__func__, PTR_ERR(clk));
 
-	rockchip_clk_register_plls(rk3036_pll_clks,
+	rockchip_clk_register_plls(ctx, rk3036_pll_clks,
 				   ARRAY_SIZE(rk3036_pll_clks),
 				   RK3036_GRF_SOC_STATUS0);
-	rockchip_clk_register_branches(rk3036_clk_branches,
+	rockchip_clk_register_branches(ctx, rk3036_clk_branches,
 				  ARRAY_SIZE(rk3036_clk_branches));
 	rockchip_clk_protect_critical(rk3036_critical_clocks,
 				      ARRAY_SIZE(rk3036_critical_clocks));
 
-	rockchip_clk_register_armclk(ARMCLK, "armclk",
+	rockchip_clk_register_armclk(ctx, ARMCLK, "armclk",
 			mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
 			&rk3036_cpuclk_data, rk3036_cpuclk_rates,
 			ARRAY_SIZE(rk3036_cpuclk_rates));
@@ -489,6 +492,8 @@ static void __init rk3036_clk_init(struct device_node *np)
 	rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0),
 				  ROCKCHIP_SOFTRST_HIWORD_MASK);
 
-	rockchip_register_restart_notifier(RK2928_GLB_SRST_FST, NULL);
+	rockchip_register_restart_notifier(ctx, RK2928_GLB_SRST_FST, NULL);
+
+	rockchip_clk_of_add_provider(np, ctx);
 }
 CLK_OF_DECLARE(rk3036_cru, "rockchip,rk3036-cru", rk3036_clk_init);
diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c
index 7f7444c..2e9388d 100644
--- a/drivers/clk/rockchip/clk-rk3188.c
+++ b/drivers/clk/rockchip/clk-rk3188.c
@@ -750,18 +750,17 @@ static const char *const rk3188_critical_clocks[] __initconst = {
 	"pclk_peri",
 };
 
-static void __init rk3188_common_clk_init(struct device_node *np)
+static struct rockchip_clk_provider *__init rk3188_common_clk_init(struct device_node *np)
 {
+	struct rockchip_clk_provider *ctx;
 	void __iomem *reg_base;
 	struct clk *clk;
 
 	reg_base = of_iomap(np, 0);
-	if (!reg_base) {
-		pr_err("%s: could not map cru region\n", __func__);
-		return;
-	}
+	if (!reg_base)
+		panic("%s: could not map cru region\n", __func__);
 
-	rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
+	ctx = rockchip_clk_init(reg_base, CLK_NR_CLKS);
 
 	/* xin12m is created by an cru-internal divider */
 	clk = clk_register_fixed_factor(NULL, "xin12m", "xin24m", 0, 1, 2);
@@ -774,45 +773,51 @@ static void __init rk3188_common_clk_init(struct device_node *np)
 		pr_warn("%s: could not register clock usb480m: %ld\n",
 			__func__, PTR_ERR(clk));
 
-	rockchip_clk_register_branches(common_clk_branches,
+	rockchip_clk_register_branches(ctx, common_clk_branches,
 				  ARRAY_SIZE(common_clk_branches));
 
 	rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0),
 				  ROCKCHIP_SOFTRST_HIWORD_MASK);
 
-	rockchip_register_restart_notifier(RK2928_GLB_SRST_FST, NULL);
+	rockchip_register_restart_notifier(ctx, RK2928_GLB_SRST_FST, NULL);
+
+	return ctx;
 }
 
 static void __init rk3066a_clk_init(struct device_node *np)
 {
-	rk3188_common_clk_init(np);
-	rockchip_clk_register_plls(rk3066_pll_clks,
+	struct rockchip_clk_provider *ctx;
+
+	ctx = rk3188_common_clk_init(np);
+	rockchip_clk_register_plls(ctx, rk3066_pll_clks,
 				   ARRAY_SIZE(rk3066_pll_clks),
 				   RK3066_GRF_SOC_STATUS);
-	rockchip_clk_register_branches(rk3066a_clk_branches,
+	rockchip_clk_register_branches(ctx, rk3066a_clk_branches,
 				  ARRAY_SIZE(rk3066a_clk_branches));
-	rockchip_clk_register_armclk(ARMCLK, "armclk",
+	rockchip_clk_register_armclk(ctx, ARMCLK, "armclk",
 			mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
 			&rk3066_cpuclk_data, rk3066_cpuclk_rates,
 			ARRAY_SIZE(rk3066_cpuclk_rates));
 	rockchip_clk_protect_critical(rk3188_critical_clocks,
 				      ARRAY_SIZE(rk3188_critical_clocks));
+	rockchip_clk_of_add_provider(np, ctx);
 }
 CLK_OF_DECLARE(rk3066a_cru, "rockchip,rk3066a-cru", rk3066a_clk_init);
 
 static void __init rk3188a_clk_init(struct device_node *np)
 {
+	struct rockchip_clk_provider *ctx;
 	struct clk *clk1, *clk2;
 	unsigned long rate;
 	int ret;
 
-	rk3188_common_clk_init(np);
-	rockchip_clk_register_plls(rk3188_pll_clks,
+	ctx = rk3188_common_clk_init(np);
+	rockchip_clk_register_plls(ctx, rk3188_pll_clks,
 				   ARRAY_SIZE(rk3188_pll_clks),
 				   RK3188_GRF_SOC_STATUS);
-	rockchip_clk_register_branches(rk3188_clk_branches,
+	rockchip_clk_register_branches(ctx, rk3188_clk_branches,
 				  ARRAY_SIZE(rk3188_clk_branches));
-	rockchip_clk_register_armclk(ARMCLK, "armclk",
+	rockchip_clk_register_armclk(ctx, ARMCLK, "armclk",
 				  mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
 				  &rk3188_cpuclk_data, rk3188_cpuclk_rates,
 				  ARRAY_SIZE(rk3188_cpuclk_rates));
@@ -836,6 +841,7 @@ static void __init rk3188a_clk_init(struct device_node *np)
 
 	rockchip_clk_protect_critical(rk3188_critical_clocks,
 				      ARRAY_SIZE(rk3188_critical_clocks));
+	rockchip_clk_of_add_provider(np, ctx);
 }
 CLK_OF_DECLARE(rk3188a_cru, "rockchip,rk3188a-cru", rk3188a_clk_init);
 
diff --git a/drivers/clk/rockchip/clk-rk3228.c b/drivers/clk/rockchip/clk-rk3228.c
index 981a502..635284a 100644
--- a/drivers/clk/rockchip/clk-rk3228.c
+++ b/drivers/clk/rockchip/clk-rk3228.c
@@ -623,6 +623,7 @@ static const char *const rk3228_critical_clocks[] __initconst = {
 
 static void __init rk3228_clk_init(struct device_node *np)
 {
+	struct rockchip_clk_provider *ctx;
 	void __iomem *reg_base;
 	struct clk *clk;
 
@@ -632,7 +633,7 @@ static void __init rk3228_clk_init(struct device_node *np)
 		return;
 	}
 
-	rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
+	ctx = rockchip_clk_init(reg_base, CLK_NR_CLKS);
 
 	/* xin12m is created by an cru-internal divider */
 	clk = clk_register_fixed_factor(NULL, "xin12m", "xin24m", 0, 1, 2);
@@ -657,15 +658,15 @@ static void __init rk3228_clk_init(struct device_node *np)
 		pr_warn("%s: could not register clock hclk_rkvdec_pre: %ld\n",
 			__func__, PTR_ERR(clk));
 
-	rockchip_clk_register_plls(rk3228_pll_clks,
+	rockchip_clk_register_plls(ctx, rk3228_pll_clks,
 				   ARRAY_SIZE(rk3228_pll_clks),
 				   RK3228_GRF_SOC_STATUS0);
-	rockchip_clk_register_branches(rk3228_clk_branches,
+	rockchip_clk_register_branches(ctx, rk3228_clk_branches,
 				  ARRAY_SIZE(rk3228_clk_branches));
 	rockchip_clk_protect_critical(rk3228_critical_clocks,
 				      ARRAY_SIZE(rk3228_critical_clocks));
 
-	rockchip_clk_register_armclk(ARMCLK, "armclk",
+	rockchip_clk_register_armclk(ctx, ARMCLK, "armclk",
 			mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
 			&rk3228_cpuclk_data, rk3228_cpuclk_rates,
 			ARRAY_SIZE(rk3228_cpuclk_rates));
@@ -673,6 +674,8 @@ static void __init rk3228_clk_init(struct device_node *np)
 	rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0),
 				  ROCKCHIP_SOFTRST_HIWORD_MASK);
 
-	rockchip_register_restart_notifier(RK3228_GLB_SRST_FST, NULL);
+	rockchip_register_restart_notifier(ctx, RK3228_GLB_SRST_FST, NULL);
+
+	rockchip_clk_of_add_provider(np, ctx);
 }
 CLK_OF_DECLARE(rk3228_cru, "rockchip,rk3228-cru", rk3228_clk_init);
diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c
index 984fc18..4bf3c21 100644
--- a/drivers/clk/rockchip/clk-rk3288.c
+++ b/drivers/clk/rockchip/clk-rk3288.c
@@ -878,6 +878,7 @@ static struct syscore_ops rk3288_clk_syscore_ops = {
 
 static void __init rk3288_clk_init(struct device_node *np)
 {
+	struct rockchip_clk_provider *ctx;
 	struct clk *clk;
 
 	rk3288_cru_base = of_iomap(np, 0);
@@ -886,7 +887,7 @@ static void __init rk3288_clk_init(struct device_node *np)
 		return;
 	}
 
-	rockchip_clk_init(np, rk3288_cru_base, CLK_NR_CLKS);
+	ctx = rockchip_clk_init(rk3288_cru_base, CLK_NR_CLKS);
 
 	/* xin12m is created by an cru-internal divider */
 	clk = clk_register_fixed_factor(NULL, "xin12m", "xin24m", 0, 1, 2);
@@ -912,17 +913,17 @@ static void __init rk3288_clk_init(struct device_node *np)
 		pr_warn("%s: could not register clock pclk_wdt: %ld\n",
 			__func__, PTR_ERR(clk));
 	else
-		rockchip_clk_add_lookup(clk, PCLK_WDT);
+		rockchip_clk_add_lookup(ctx, clk, PCLK_WDT);
 
-	rockchip_clk_register_plls(rk3288_pll_clks,
+	rockchip_clk_register_plls(ctx, rk3288_pll_clks,
 				   ARRAY_SIZE(rk3288_pll_clks),
 				   RK3288_GRF_SOC_STATUS1);
-	rockchip_clk_register_branches(rk3288_clk_branches,
+	rockchip_clk_register_branches(ctx, rk3288_clk_branches,
 				  ARRAY_SIZE(rk3288_clk_branches));
 	rockchip_clk_protect_critical(rk3288_critical_clocks,
 				      ARRAY_SIZE(rk3288_critical_clocks));
 
-	rockchip_clk_register_armclk(ARMCLK, "armclk",
+	rockchip_clk_register_armclk(ctx, ARMCLK, "armclk",
 			mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
 			&rk3288_cpuclk_data, rk3288_cpuclk_rates,
 			ARRAY_SIZE(rk3288_cpuclk_rates));
@@ -931,8 +932,10 @@ static void __init rk3288_clk_init(struct device_node *np)
 				  rk3288_cru_base + RK3288_SOFTRST_CON(0),
 				  ROCKCHIP_SOFTRST_HIWORD_MASK);
 
-	rockchip_register_restart_notifier(RK3288_GLB_SRST_FST,
+	rockchip_register_restart_notifier(ctx, RK3288_GLB_SRST_FST,
 					   rk3288_clk_shutdown);
 	register_syscore_ops(&rk3288_clk_syscore_ops);
+
+	rockchip_clk_of_add_provider(np, ctx);
 }
 CLK_OF_DECLARE(rk3288_cru, "rockchip,rk3288-cru", rk3288_clk_init);
diff --git a/drivers/clk/rockchip/clk-rk3368.c b/drivers/clk/rockchip/clk-rk3368.c
index be0ede5..864dd29 100644
--- a/drivers/clk/rockchip/clk-rk3368.c
+++ b/drivers/clk/rockchip/clk-rk3368.c
@@ -831,6 +831,7 @@ static const char *const rk3368_critical_clocks[] __initconst = {
 
 static void __init rk3368_clk_init(struct device_node *np)
 {
+	struct rockchip_clk_provider *ctx;
 	void __iomem *reg_base;
 	struct clk *clk;
 
@@ -840,7 +841,7 @@ static void __init rk3368_clk_init(struct device_node *np)
 		return;
 	}
 
-	rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
+	ctx = rockchip_clk_init(reg_base, CLK_NR_CLKS);
 
 	/* xin12m is created by a cru-internal divider */
 	clk = clk_register_fixed_factor(NULL, "xin12m", "xin24m", 0, 1, 2);
@@ -866,22 +867,22 @@ static void __init rk3368_clk_init(struct device_node *np)
 		pr_warn("%s: could not register clock pclk_wdt: %ld\n",
 			__func__, PTR_ERR(clk));
 	else
-		rockchip_clk_add_lookup(clk, PCLK_WDT);
+		rockchip_clk_add_lookup(ctx, clk, PCLK_WDT);
 
-	rockchip_clk_register_plls(rk3368_pll_clks,
+	rockchip_clk_register_plls(ctx, rk3368_pll_clks,
 				   ARRAY_SIZE(rk3368_pll_clks),
 				   RK3368_GRF_SOC_STATUS0);
-	rockchip_clk_register_branches(rk3368_clk_branches,
+	rockchip_clk_register_branches(ctx, rk3368_clk_branches,
 				  ARRAY_SIZE(rk3368_clk_branches));
 	rockchip_clk_protect_critical(rk3368_critical_clocks,
 				      ARRAY_SIZE(rk3368_critical_clocks));
 
-	rockchip_clk_register_armclk(ARMCLKB, "armclkb",
+	rockchip_clk_register_armclk(ctx, ARMCLKB, "armclkb",
 			mux_armclkb_p, ARRAY_SIZE(mux_armclkb_p),
 			&rk3368_cpuclkb_data, rk3368_cpuclkb_rates,
 			ARRAY_SIZE(rk3368_cpuclkb_rates));
 
-	rockchip_clk_register_armclk(ARMCLKL, "armclkl",
+	rockchip_clk_register_armclk(ctx, ARMCLKL, "armclkl",
 			mux_armclkl_p, ARRAY_SIZE(mux_armclkl_p),
 			&rk3368_cpuclkl_data, rk3368_cpuclkl_rates,
 			ARRAY_SIZE(rk3368_cpuclkl_rates));
@@ -889,6 +890,8 @@ static void __init rk3368_clk_init(struct device_node *np)
 	rockchip_register_softrst(np, 15, reg_base + RK3368_SOFTRST_CON(0),
 				  ROCKCHIP_SOFTRST_HIWORD_MASK);
 
-	rockchip_register_restart_notifier(RK3368_GLB_SRST_FST, NULL);
+	rockchip_register_restart_notifier(ctx, RK3368_GLB_SRST_FST, NULL);
+
+	rockchip_clk_of_add_provider(np, ctx);
 }
 CLK_OF_DECLARE(rk3368_cru, "rockchip,rk3368-cru", rk3368_clk_init);
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
index d9a0b5d..fba4667 100644
--- a/drivers/clk/rockchip/clk.c
+++ b/drivers/clk/rockchip/clk.c
@@ -150,7 +150,8 @@ static int rockchip_clk_frac_notifier_cb(struct notifier_block *nb,
 	return notifier_from_errno(ret);
 }
 
-static struct clk *rockchip_clk_register_frac_branch(const char *name,
+static struct clk *rockchip_clk_register_frac_branch(
+		struct rockchip_clk_provider *ctx, const char *name,
 		const char *const *parent_names, u8 num_parents,
 		void __iomem *base, int muxdiv_offset, u8 div_flags,
 		int gate_offset, u8 gate_shift, u8 gate_flags,
@@ -243,7 +244,7 @@ static struct clk *rockchip_clk_register_frac_branch(const char *name,
 		if (IS_ERR(mux_clk))
 			return clk;
 
-		rockchip_clk_add_lookup(mux_clk, child->id);
+		rockchip_clk_add_lookup(ctx, mux_clk, child->id);
 
 		/* notifier on the fraction divider to catch rate changes */
 		if (frac->mux_frac_idx >= 0) {
@@ -260,27 +261,49 @@ static struct clk *rockchip_clk_register_frac_branch(const char *name,
 	return clk;
 }
 
-static DEFINE_SPINLOCK(clk_lock);
-static struct clk **clk_table;
-static void __iomem *reg_base;
-static struct clk_onecell_data clk_data;
 static struct device_node *cru_node;
 static struct regmap *grf;
 
-void __init rockchip_clk_init(struct device_node *np, void __iomem *base,
-			      unsigned long nr_clks)
+struct rockchip_clk_provider *__init rockchip_clk_init(
+			void __iomem *base, unsigned long nr_clks)
 {
-	reg_base = base;
-	cru_node = np;
-	grf = ERR_PTR(-EPROBE_DEFER);
+	struct rockchip_clk_provider *ctx;
+	struct clk **clk_table;
+	int i;
+
+	ctx = kzalloc(sizeof(struct rockchip_clk_provider), GFP_KERNEL);
+	if (!ctx)
+		panic("could not allocate clock provider context.\n");
 
 	clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL);
 	if (!clk_table)
-		pr_err("%s: could not allocate clock lookup table\n", __func__);
+		panic("could not allocate clock lookup table\n");
+
+	for (i = 0; i < nr_clks; ++i)
+		clk_table[i] = ERR_PTR(-ENOENT);
+
+	ctx->reg_base = base;
+	ctx->clk_data.clks = clk_table;
+	ctx->clk_data.clk_num = nr_clks;
+	spin_lock_init(&ctx->lock);
+
+	return ctx;
+}
 
-	clk_data.clks = clk_table;
-	clk_data.clk_num = nr_clks;
-	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+void __init rockchip_clk_common_cru_init(struct device_node *np)
+{
+	cru_node = np;
+	grf = ERR_PTR(-EPROBE_DEFER);
+}
+
+void __init rockchip_clk_of_add_provider(struct device_node *np,
+				struct rockchip_clk_provider *ctx)
+{
+	if (np) {
+		if (of_clk_add_provider(np, of_clk_src_onecell_get,
+					&ctx->clk_data))
+			panic("could not register clk provider\n");
+	}
 }
 
 struct regmap *rockchip_clk_get_grf(void)
@@ -290,13 +313,15 @@ struct regmap *rockchip_clk_get_grf(void)
 	return grf;
 }
 
-void rockchip_clk_add_lookup(struct clk *clk, unsigned int id)
+void rockchip_clk_add_lookup(struct rockchip_clk_provider *ctx,
+			     struct clk *clk, unsigned int id)
 {
-	if (clk_table && id)
-		clk_table[id] = clk;
+	if (ctx->clk_data.clks && id)
+		ctx->clk_data.clks[id] = clk;
 }
 
-void __init rockchip_clk_register_plls(struct rockchip_pll_clock *list,
+void __init rockchip_clk_register_plls(struct rockchip_clk_provider *ctx,
+				struct rockchip_pll_clock *list,
 				unsigned int nr_pll, int grf_lock_offset)
 {
 	struct clk *clk;
@@ -305,21 +330,22 @@ void __init rockchip_clk_register_plls(struct rockchip_pll_clock *list,
 	for (idx = 0; idx < nr_pll; idx++, list++) {
 		clk = rockchip_clk_register_pll(list->type, list->name,
 				list->parent_names, list->num_parents,
-				reg_base, list->con_offset, grf_lock_offset,
+				ctx->reg_base, list->con_offset, grf_lock_offset,
 				list->lock_shift, list->mode_offset,
 				list->mode_shift, list->rate_table,
-				list->pll_flags, &clk_lock);
+				list->pll_flags, &ctx->lock);
 		if (IS_ERR(clk)) {
 			pr_err("%s: failed to register clock %s\n", __func__,
 				list->name);
 			continue;
 		}
 
-		rockchip_clk_add_lookup(clk, list->id);
+		rockchip_clk_add_lookup(ctx, clk, list->id);
 	}
 }
 
 void __init rockchip_clk_register_branches(
+				      struct rockchip_clk_provider *ctx,
 				      struct rockchip_clk_branch *list,
 				      unsigned int nr_clk)
 {
@@ -335,56 +361,56 @@ void __init rockchip_clk_register_branches(
 		case branch_mux:
 			clk = clk_register_mux(NULL, list->name,
 				list->parent_names, list->num_parents,
-				flags, reg_base + list->muxdiv_offset,
+				flags, ctx->reg_base + list->muxdiv_offset,
 				list->mux_shift, list->mux_width,
-				list->mux_flags, &clk_lock);
+				list->mux_flags, &ctx->lock);
 			break;
 		case branch_divider:
 			if (list->div_table)
 				clk = clk_register_divider_table(NULL,
 					list->name, list->parent_names[0],
-					flags, reg_base + list->muxdiv_offset,
+					flags, ctx->reg_base + list->muxdiv_offset,
 					list->div_shift, list->div_width,
 					list->div_flags, list->div_table,
-					&clk_lock);
+					&ctx->lock);
 			else
 				clk = clk_register_divider(NULL, list->name,
 					list->parent_names[0], flags,
-					reg_base + list->muxdiv_offset,
+					ctx->reg_base + list->muxdiv_offset,
 					list->div_shift, list->div_width,
-					list->div_flags, &clk_lock);
+					list->div_flags, &ctx->lock);
 			break;
 		case branch_fraction_divider:
-			clk = rockchip_clk_register_frac_branch(list->name,
+			clk = rockchip_clk_register_frac_branch(ctx, list->name,
 				list->parent_names, list->num_parents,
-				reg_base, list->muxdiv_offset, list->div_flags,
+				ctx->reg_base, list->muxdiv_offset, list->div_flags,
 				list->gate_offset, list->gate_shift,
 				list->gate_flags, flags, list->child,
-				&clk_lock);
+				&ctx->lock);
 			break;
 		case branch_gate:
 			flags |= CLK_SET_RATE_PARENT;
 
 			clk = clk_register_gate(NULL, list->name,
 				list->parent_names[0], flags,
-				reg_base + list->gate_offset,
-				list->gate_shift, list->gate_flags, &clk_lock);
+				ctx->reg_base + list->gate_offset,
+				list->gate_shift, list->gate_flags, &ctx->lock);
 			break;
 		case branch_composite:
 			clk = rockchip_clk_register_branch(list->name,
 				list->parent_names, list->num_parents,
-				reg_base, list->muxdiv_offset, list->mux_shift,
+				ctx->reg_base, list->muxdiv_offset, list->mux_shift,
 				list->mux_width, list->mux_flags,
 				list->div_shift, list->div_width,
 				list->div_flags, list->div_table,
 				list->gate_offset, list->gate_shift,
-				list->gate_flags, flags, &clk_lock);
+				list->gate_flags, flags, &ctx->lock);
 			break;
 		case branch_mmc:
 			clk = rockchip_clk_register_mmc(
 				list->name,
 				list->parent_names, list->num_parents,
-				reg_base + list->muxdiv_offset,
+				ctx->reg_base + list->muxdiv_offset,
 				list->div_shift
 			);
 			break;
@@ -392,8 +418,8 @@ void __init rockchip_clk_register_branches(
 			clk = rockchip_clk_register_inverter(
 				list->name, list->parent_names,
 				list->num_parents,
-				reg_base + list->muxdiv_offset,
-				list->div_shift, list->div_flags, &clk_lock);
+				ctx->reg_base + list->muxdiv_offset,
+				list->div_shift, list->div_flags, &ctx->lock);
 			break;
 		}
 
@@ -410,11 +436,12 @@ void __init rockchip_clk_register_branches(
 			continue;
 		}
 
-		rockchip_clk_add_lookup(clk, list->id);
+		rockchip_clk_add_lookup(ctx, clk, list->id);
 	}
 }
 
-void __init rockchip_clk_register_armclk(unsigned int lookup_id,
+void __init rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx,
+			unsigned int lookup_id,
 			const char *name, const char *const *parent_names,
 			u8 num_parents,
 			const struct rockchip_cpuclk_reg_data *reg_data,
@@ -424,15 +451,15 @@ void __init rockchip_clk_register_armclk(unsigned int lookup_id,
 	struct clk *clk;
 
 	clk = rockchip_clk_register_cpuclk(name, parent_names, num_parents,
-					   reg_data, rates, nrates, reg_base,
-					   &clk_lock);
+					   reg_data, rates, nrates, ctx->reg_base,
+					   &ctx->lock);
 	if (IS_ERR(clk)) {
 		pr_err("%s: failed to register clock %s: %ld\n",
 		       __func__, name, PTR_ERR(clk));
 		return;
 	}
 
-	rockchip_clk_add_lookup(clk, lookup_id);
+	rockchip_clk_add_lookup(ctx, clk, lookup_id);
 }
 
 void __init rockchip_clk_protect_critical(const char *const clocks[],
@@ -449,6 +476,7 @@ void __init rockchip_clk_protect_critical(const char *const clocks[],
 	}
 }
 
+static void __iomem *rst_base;
 static unsigned int reg_restart;
 static void (*cb_restart)(void);
 static int rockchip_restart_notify(struct notifier_block *this,
@@ -457,7 +485,7 @@ static int rockchip_restart_notify(struct notifier_block *this,
 	if (cb_restart)
 		cb_restart();
 
-	writel(0xfdb9, reg_base + reg_restart);
+	writel(0xfdb9, rst_base + reg_restart);
 	return NOTIFY_DONE;
 }
 
@@ -466,10 +494,12 @@ static struct notifier_block rockchip_restart_handler = {
 	.priority = 128,
 };
 
-void __init rockchip_register_restart_notifier(unsigned int reg, void (*cb)(void))
+void __init rockchip_register_restart_notifier(struct rockchip_clk_provider *ctx,
+					       unsigned int reg, void (*cb)(void))
 {
 	int ret;
 
+	rst_base = ctx->reg_base;
 	reg_restart = reg;
 	cb_restart = cb;
 	ret = register_restart_handler(&rockchip_restart_handler);
diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
index ff8bd23..f6552b6 100644
--- a/drivers/clk/rockchip/clk.h
+++ b/drivers/clk/rockchip/clk.h
@@ -27,6 +27,7 @@
 #define CLK_ROCKCHIP_CLK_H
 
 #include <linux/io.h>
+#include <linux/clk-provider.h>
 
 struct clk;
 
@@ -127,6 +128,18 @@ enum rockchip_pll_type {
 	.nb = _nb,						\
 }
 
+/**
+ * struct rockchip_clk_provider: information about clock provider
+ * @reg_base: virtual address for the register base.
+ * @clk_data: holds clock related data like clk* and number of clocks.
+ * @lock: maintains exclusion between callbacks for a given clock-provider.
+ */
+struct rockchip_clk_provider {
+	void __iomem *reg_base;
+	struct clk_onecell_data clk_data;
+	spinlock_t lock;
+};
+
 struct rockchip_pll_rate_table {
 	unsigned long rate;
 	unsigned int nr;
@@ -508,21 +521,29 @@ struct rockchip_clk_branch {
 		.div_flags	= if,				\
 	}
 
-void rockchip_clk_init(struct device_node *np, void __iomem *base,
-		       unsigned long nr_clks);
+struct rockchip_clk_provider *rockchip_clk_init(
+			void __iomem *base, unsigned long nr_clks);
+void rockchip_clk_common_cru_init(struct device_node *np);
+void rockchip_clk_of_add_provider(struct device_node *np,
+				struct rockchip_clk_provider *ctx);
 struct regmap *rockchip_clk_get_grf(void);
-void rockchip_clk_add_lookup(struct clk *clk, unsigned int id);
-void rockchip_clk_register_branches(struct rockchip_clk_branch *clk_list,
+void rockchip_clk_add_lookup(struct rockchip_clk_provider *ctx,
+			     struct clk *clk, unsigned int id);
+void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
+				    struct rockchip_clk_branch *list,
 				    unsigned int nr_clk);
-void rockchip_clk_register_plls(struct rockchip_pll_clock *pll_list,
+void rockchip_clk_register_plls(struct rockchip_clk_provider *ctx,
+				struct rockchip_pll_clock *pll_list,
 				unsigned int nr_pll, int grf_lock_offset);
-void rockchip_clk_register_armclk(unsigned int lookup_id, const char *name,
+void rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx,
+			unsigned int lookup_id, const char *name,
 			const char *const *parent_names, u8 num_parents,
 			const struct rockchip_cpuclk_reg_data *reg_data,
 			const struct rockchip_cpuclk_rate_table *rates,
 			int nrates);
 void rockchip_clk_protect_critical(const char *const clocks[], int nclocks);
-void rockchip_register_restart_notifier(unsigned int reg, void (*cb)(void));
+void rockchip_register_restart_notifier(struct rockchip_clk_provider *ctx,
+					unsigned int reg, void (*cb)(void));
 
 #define ROCKCHIP_SOFTRST_HIWORD_MASK	BIT(0)
 
-- 
1.7.9.5




From zhengxing at rock-chips.com  Wed Jan 20 01:06:49 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Wed, 20 Jan 2016 17:06:49 +0800
Subject: [PATCH v2] clk: rockchip: Add support for multiple clock providers
Message-ID: <1453280809-3120-1-git-send-email-zhengxing@rock-chips.com>

There are need to support Multi-CRUs probability in future, but
it is not supported on the current Rockchip Clock Framework.

Therefore, this patch add support a provider as the parameter
handler when we call the clock register functions for per CRU.

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
---

Changes in v2:
- Fix missed to call rockchip_clk_common_cru_init when SoCs clock init.

 drivers/clk/rockchip/clk-rk3036.c |   15 +++--
 drivers/clk/rockchip/clk-rk3188.c |   40 ++++++++-----
 drivers/clk/rockchip/clk-rk3228.c |   15 +++--
 drivers/clk/rockchip/clk-rk3288.c |   17 ++++--
 drivers/clk/rockchip/clk-rk3368.c |   19 +++---
 drivers/clk/rockchip/clk.c        |  120 +++++++++++++++++++++++--------------
 drivers/clk/rockchip/clk.h        |   35 ++++++++---
 7 files changed, 170 insertions(+), 91 deletions(-)

diff --git a/drivers/clk/rockchip/clk-rk3036.c b/drivers/clk/rockchip/clk-rk3036.c
index 483913b..050ad13 100644
--- a/drivers/clk/rockchip/clk-rk3036.c
+++ b/drivers/clk/rockchip/clk-rk3036.c
@@ -434,6 +434,7 @@ static const char *const rk3036_critical_clocks[] __initconst = {
 
 static void __init rk3036_clk_init(struct device_node *np)
 {
+	struct rockchip_clk_provider *ctx;
 	void __iomem *reg_base;
 	struct clk *clk;
 
@@ -443,7 +444,9 @@ static void __init rk3036_clk_init(struct device_node *np)
 		return;
 	}
 
-	rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
+	ctx = rockchip_clk_init(reg_base, CLK_NR_CLKS);
+
+	rockchip_clk_common_cru_init(np);
 
 	/* xin12m is created by an cru-internal divider */
 	clk = clk_register_fixed_factor(NULL, "xin12m", "xin24m", 0, 1, 2);
@@ -473,15 +476,15 @@ static void __init rk3036_clk_init(struct device_node *np)
 		pr_warn("%s: could not register clock sclk_macref_out: %ld\n",
 			__func__, PTR_ERR(clk));
 
-	rockchip_clk_register_plls(rk3036_pll_clks,
+	rockchip_clk_register_plls(ctx, rk3036_pll_clks,
 				   ARRAY_SIZE(rk3036_pll_clks),
 				   RK3036_GRF_SOC_STATUS0);
-	rockchip_clk_register_branches(rk3036_clk_branches,
+	rockchip_clk_register_branches(ctx, rk3036_clk_branches,
 				  ARRAY_SIZE(rk3036_clk_branches));
 	rockchip_clk_protect_critical(rk3036_critical_clocks,
 				      ARRAY_SIZE(rk3036_critical_clocks));
 
-	rockchip_clk_register_armclk(ARMCLK, "armclk",
+	rockchip_clk_register_armclk(ctx, ARMCLK, "armclk",
 			mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
 			&rk3036_cpuclk_data, rk3036_cpuclk_rates,
 			ARRAY_SIZE(rk3036_cpuclk_rates));
@@ -489,6 +492,8 @@ static void __init rk3036_clk_init(struct device_node *np)
 	rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0),
 				  ROCKCHIP_SOFTRST_HIWORD_MASK);
 
-	rockchip_register_restart_notifier(RK2928_GLB_SRST_FST, NULL);
+	rockchip_register_restart_notifier(ctx, RK2928_GLB_SRST_FST, NULL);
+
+	rockchip_clk_of_add_provider(np, ctx);
 }
 CLK_OF_DECLARE(rk3036_cru, "rockchip,rk3036-cru", rk3036_clk_init);
diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c
index 7f7444c..338a22e 100644
--- a/drivers/clk/rockchip/clk-rk3188.c
+++ b/drivers/clk/rockchip/clk-rk3188.c
@@ -750,18 +750,19 @@ static const char *const rk3188_critical_clocks[] __initconst = {
 	"pclk_peri",
 };
 
-static void __init rk3188_common_clk_init(struct device_node *np)
+static struct rockchip_clk_provider *__init rk3188_common_clk_init(struct device_node *np)
 {
+	struct rockchip_clk_provider *ctx;
 	void __iomem *reg_base;
 	struct clk *clk;
 
 	reg_base = of_iomap(np, 0);
-	if (!reg_base) {
-		pr_err("%s: could not map cru region\n", __func__);
-		return;
-	}
+	if (!reg_base)
+		panic("%s: could not map cru region\n", __func__);
+
+	ctx = rockchip_clk_init(reg_base, CLK_NR_CLKS);
 
-	rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
+	rockchip_clk_common_cru_init(np);
 
 	/* xin12m is created by an cru-internal divider */
 	clk = clk_register_fixed_factor(NULL, "xin12m", "xin24m", 0, 1, 2);
@@ -774,45 +775,51 @@ static void __init rk3188_common_clk_init(struct device_node *np)
 		pr_warn("%s: could not register clock usb480m: %ld\n",
 			__func__, PTR_ERR(clk));
 
-	rockchip_clk_register_branches(common_clk_branches,
+	rockchip_clk_register_branches(ctx, common_clk_branches,
 				  ARRAY_SIZE(common_clk_branches));
 
 	rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0),
 				  ROCKCHIP_SOFTRST_HIWORD_MASK);
 
-	rockchip_register_restart_notifier(RK2928_GLB_SRST_FST, NULL);
+	rockchip_register_restart_notifier(ctx, RK2928_GLB_SRST_FST, NULL);
+
+	return ctx;
 }
 
 static void __init rk3066a_clk_init(struct device_node *np)
 {
-	rk3188_common_clk_init(np);
-	rockchip_clk_register_plls(rk3066_pll_clks,
+	struct rockchip_clk_provider *ctx;
+
+	ctx = rk3188_common_clk_init(np);
+	rockchip_clk_register_plls(ctx, rk3066_pll_clks,
 				   ARRAY_SIZE(rk3066_pll_clks),
 				   RK3066_GRF_SOC_STATUS);
-	rockchip_clk_register_branches(rk3066a_clk_branches,
+	rockchip_clk_register_branches(ctx, rk3066a_clk_branches,
 				  ARRAY_SIZE(rk3066a_clk_branches));
-	rockchip_clk_register_armclk(ARMCLK, "armclk",
+	rockchip_clk_register_armclk(ctx, ARMCLK, "armclk",
 			mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
 			&rk3066_cpuclk_data, rk3066_cpuclk_rates,
 			ARRAY_SIZE(rk3066_cpuclk_rates));
 	rockchip_clk_protect_critical(rk3188_critical_clocks,
 				      ARRAY_SIZE(rk3188_critical_clocks));
+	rockchip_clk_of_add_provider(np, ctx);
 }
 CLK_OF_DECLARE(rk3066a_cru, "rockchip,rk3066a-cru", rk3066a_clk_init);
 
 static void __init rk3188a_clk_init(struct device_node *np)
 {
+	struct rockchip_clk_provider *ctx;
 	struct clk *clk1, *clk2;
 	unsigned long rate;
 	int ret;
 
-	rk3188_common_clk_init(np);
-	rockchip_clk_register_plls(rk3188_pll_clks,
+	ctx = rk3188_common_clk_init(np);
+	rockchip_clk_register_plls(ctx, rk3188_pll_clks,
 				   ARRAY_SIZE(rk3188_pll_clks),
 				   RK3188_GRF_SOC_STATUS);
-	rockchip_clk_register_branches(rk3188_clk_branches,
+	rockchip_clk_register_branches(ctx, rk3188_clk_branches,
 				  ARRAY_SIZE(rk3188_clk_branches));
-	rockchip_clk_register_armclk(ARMCLK, "armclk",
+	rockchip_clk_register_armclk(ctx, ARMCLK, "armclk",
 				  mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
 				  &rk3188_cpuclk_data, rk3188_cpuclk_rates,
 				  ARRAY_SIZE(rk3188_cpuclk_rates));
@@ -836,6 +843,7 @@ static void __init rk3188a_clk_init(struct device_node *np)
 
 	rockchip_clk_protect_critical(rk3188_critical_clocks,
 				      ARRAY_SIZE(rk3188_critical_clocks));
+	rockchip_clk_of_add_provider(np, ctx);
 }
 CLK_OF_DECLARE(rk3188a_cru, "rockchip,rk3188a-cru", rk3188a_clk_init);
 
diff --git a/drivers/clk/rockchip/clk-rk3228.c b/drivers/clk/rockchip/clk-rk3228.c
index 981a502..2457c57 100644
--- a/drivers/clk/rockchip/clk-rk3228.c
+++ b/drivers/clk/rockchip/clk-rk3228.c
@@ -623,6 +623,7 @@ static const char *const rk3228_critical_clocks[] __initconst = {
 
 static void __init rk3228_clk_init(struct device_node *np)
 {
+	struct rockchip_clk_provider *ctx;
 	void __iomem *reg_base;
 	struct clk *clk;
 
@@ -632,7 +633,9 @@ static void __init rk3228_clk_init(struct device_node *np)
 		return;
 	}
 
-	rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
+	ctx = rockchip_clk_init(reg_base, CLK_NR_CLKS);
+
+	rockchip_clk_common_cru_init(np);
 
 	/* xin12m is created by an cru-internal divider */
 	clk = clk_register_fixed_factor(NULL, "xin12m", "xin24m", 0, 1, 2);
@@ -657,15 +660,15 @@ static void __init rk3228_clk_init(struct device_node *np)
 		pr_warn("%s: could not register clock hclk_rkvdec_pre: %ld\n",
 			__func__, PTR_ERR(clk));
 
-	rockchip_clk_register_plls(rk3228_pll_clks,
+	rockchip_clk_register_plls(ctx, rk3228_pll_clks,
 				   ARRAY_SIZE(rk3228_pll_clks),
 				   RK3228_GRF_SOC_STATUS0);
-	rockchip_clk_register_branches(rk3228_clk_branches,
+	rockchip_clk_register_branches(ctx, rk3228_clk_branches,
 				  ARRAY_SIZE(rk3228_clk_branches));
 	rockchip_clk_protect_critical(rk3228_critical_clocks,
 				      ARRAY_SIZE(rk3228_critical_clocks));
 
-	rockchip_clk_register_armclk(ARMCLK, "armclk",
+	rockchip_clk_register_armclk(ctx, ARMCLK, "armclk",
 			mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
 			&rk3228_cpuclk_data, rk3228_cpuclk_rates,
 			ARRAY_SIZE(rk3228_cpuclk_rates));
@@ -673,6 +676,8 @@ static void __init rk3228_clk_init(struct device_node *np)
 	rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0),
 				  ROCKCHIP_SOFTRST_HIWORD_MASK);
 
-	rockchip_register_restart_notifier(RK3228_GLB_SRST_FST, NULL);
+	rockchip_register_restart_notifier(ctx, RK3228_GLB_SRST_FST, NULL);
+
+	rockchip_clk_of_add_provider(np, ctx);
 }
 CLK_OF_DECLARE(rk3228_cru, "rockchip,rk3228-cru", rk3228_clk_init);
diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c
index 984fc18..d2680e8 100644
--- a/drivers/clk/rockchip/clk-rk3288.c
+++ b/drivers/clk/rockchip/clk-rk3288.c
@@ -878,6 +878,7 @@ static struct syscore_ops rk3288_clk_syscore_ops = {
 
 static void __init rk3288_clk_init(struct device_node *np)
 {
+	struct rockchip_clk_provider *ctx;
 	struct clk *clk;
 
 	rk3288_cru_base = of_iomap(np, 0);
@@ -886,7 +887,9 @@ static void __init rk3288_clk_init(struct device_node *np)
 		return;
 	}
 
-	rockchip_clk_init(np, rk3288_cru_base, CLK_NR_CLKS);
+	ctx = rockchip_clk_init(rk3288_cru_base, CLK_NR_CLKS);
+
+	rockchip_clk_common_cru_init(np);
 
 	/* xin12m is created by an cru-internal divider */
 	clk = clk_register_fixed_factor(NULL, "xin12m", "xin24m", 0, 1, 2);
@@ -912,17 +915,17 @@ static void __init rk3288_clk_init(struct device_node *np)
 		pr_warn("%s: could not register clock pclk_wdt: %ld\n",
 			__func__, PTR_ERR(clk));
 	else
-		rockchip_clk_add_lookup(clk, PCLK_WDT);
+		rockchip_clk_add_lookup(ctx, clk, PCLK_WDT);
 
-	rockchip_clk_register_plls(rk3288_pll_clks,
+	rockchip_clk_register_plls(ctx, rk3288_pll_clks,
 				   ARRAY_SIZE(rk3288_pll_clks),
 				   RK3288_GRF_SOC_STATUS1);
-	rockchip_clk_register_branches(rk3288_clk_branches,
+	rockchip_clk_register_branches(ctx, rk3288_clk_branches,
 				  ARRAY_SIZE(rk3288_clk_branches));
 	rockchip_clk_protect_critical(rk3288_critical_clocks,
 				      ARRAY_SIZE(rk3288_critical_clocks));
 
-	rockchip_clk_register_armclk(ARMCLK, "armclk",
+	rockchip_clk_register_armclk(ctx, ARMCLK, "armclk",
 			mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
 			&rk3288_cpuclk_data, rk3288_cpuclk_rates,
 			ARRAY_SIZE(rk3288_cpuclk_rates));
@@ -931,8 +934,10 @@ static void __init rk3288_clk_init(struct device_node *np)
 				  rk3288_cru_base + RK3288_SOFTRST_CON(0),
 				  ROCKCHIP_SOFTRST_HIWORD_MASK);
 
-	rockchip_register_restart_notifier(RK3288_GLB_SRST_FST,
+	rockchip_register_restart_notifier(ctx, RK3288_GLB_SRST_FST,
 					   rk3288_clk_shutdown);
 	register_syscore_ops(&rk3288_clk_syscore_ops);
+
+	rockchip_clk_of_add_provider(np, ctx);
 }
 CLK_OF_DECLARE(rk3288_cru, "rockchip,rk3288-cru", rk3288_clk_init);
diff --git a/drivers/clk/rockchip/clk-rk3368.c b/drivers/clk/rockchip/clk-rk3368.c
index be0ede5..08376bb 100644
--- a/drivers/clk/rockchip/clk-rk3368.c
+++ b/drivers/clk/rockchip/clk-rk3368.c
@@ -831,6 +831,7 @@ static const char *const rk3368_critical_clocks[] __initconst = {
 
 static void __init rk3368_clk_init(struct device_node *np)
 {
+	struct rockchip_clk_provider *ctx;
 	void __iomem *reg_base;
 	struct clk *clk;
 
@@ -840,7 +841,9 @@ static void __init rk3368_clk_init(struct device_node *np)
 		return;
 	}
 
-	rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
+	ctx = rockchip_clk_init(reg_base, CLK_NR_CLKS);
+
+	rockchip_clk_common_cru_init(np);
 
 	/* xin12m is created by a cru-internal divider */
 	clk = clk_register_fixed_factor(NULL, "xin12m", "xin24m", 0, 1, 2);
@@ -866,22 +869,22 @@ static void __init rk3368_clk_init(struct device_node *np)
 		pr_warn("%s: could not register clock pclk_wdt: %ld\n",
 			__func__, PTR_ERR(clk));
 	else
-		rockchip_clk_add_lookup(clk, PCLK_WDT);
+		rockchip_clk_add_lookup(ctx, clk, PCLK_WDT);
 
-	rockchip_clk_register_plls(rk3368_pll_clks,
+	rockchip_clk_register_plls(ctx, rk3368_pll_clks,
 				   ARRAY_SIZE(rk3368_pll_clks),
 				   RK3368_GRF_SOC_STATUS0);
-	rockchip_clk_register_branches(rk3368_clk_branches,
+	rockchip_clk_register_branches(ctx, rk3368_clk_branches,
 				  ARRAY_SIZE(rk3368_clk_branches));
 	rockchip_clk_protect_critical(rk3368_critical_clocks,
 				      ARRAY_SIZE(rk3368_critical_clocks));
 
-	rockchip_clk_register_armclk(ARMCLKB, "armclkb",
+	rockchip_clk_register_armclk(ctx, ARMCLKB, "armclkb",
 			mux_armclkb_p, ARRAY_SIZE(mux_armclkb_p),
 			&rk3368_cpuclkb_data, rk3368_cpuclkb_rates,
 			ARRAY_SIZE(rk3368_cpuclkb_rates));
 
-	rockchip_clk_register_armclk(ARMCLKL, "armclkl",
+	rockchip_clk_register_armclk(ctx, ARMCLKL, "armclkl",
 			mux_armclkl_p, ARRAY_SIZE(mux_armclkl_p),
 			&rk3368_cpuclkl_data, rk3368_cpuclkl_rates,
 			ARRAY_SIZE(rk3368_cpuclkl_rates));
@@ -889,6 +892,8 @@ static void __init rk3368_clk_init(struct device_node *np)
 	rockchip_register_softrst(np, 15, reg_base + RK3368_SOFTRST_CON(0),
 				  ROCKCHIP_SOFTRST_HIWORD_MASK);
 
-	rockchip_register_restart_notifier(RK3368_GLB_SRST_FST, NULL);
+	rockchip_register_restart_notifier(ctx, RK3368_GLB_SRST_FST, NULL);
+
+	rockchip_clk_of_add_provider(np, ctx);
 }
 CLK_OF_DECLARE(rk3368_cru, "rockchip,rk3368-cru", rk3368_clk_init);
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
index d9a0b5d..fba4667 100644
--- a/drivers/clk/rockchip/clk.c
+++ b/drivers/clk/rockchip/clk.c
@@ -150,7 +150,8 @@ static int rockchip_clk_frac_notifier_cb(struct notifier_block *nb,
 	return notifier_from_errno(ret);
 }
 
-static struct clk *rockchip_clk_register_frac_branch(const char *name,
+static struct clk *rockchip_clk_register_frac_branch(
+		struct rockchip_clk_provider *ctx, const char *name,
 		const char *const *parent_names, u8 num_parents,
 		void __iomem *base, int muxdiv_offset, u8 div_flags,
 		int gate_offset, u8 gate_shift, u8 gate_flags,
@@ -243,7 +244,7 @@ static struct clk *rockchip_clk_register_frac_branch(const char *name,
 		if (IS_ERR(mux_clk))
 			return clk;
 
-		rockchip_clk_add_lookup(mux_clk, child->id);
+		rockchip_clk_add_lookup(ctx, mux_clk, child->id);
 
 		/* notifier on the fraction divider to catch rate changes */
 		if (frac->mux_frac_idx >= 0) {
@@ -260,27 +261,49 @@ static struct clk *rockchip_clk_register_frac_branch(const char *name,
 	return clk;
 }
 
-static DEFINE_SPINLOCK(clk_lock);
-static struct clk **clk_table;
-static void __iomem *reg_base;
-static struct clk_onecell_data clk_data;
 static struct device_node *cru_node;
 static struct regmap *grf;
 
-void __init rockchip_clk_init(struct device_node *np, void __iomem *base,
-			      unsigned long nr_clks)
+struct rockchip_clk_provider *__init rockchip_clk_init(
+			void __iomem *base, unsigned long nr_clks)
 {
-	reg_base = base;
-	cru_node = np;
-	grf = ERR_PTR(-EPROBE_DEFER);
+	struct rockchip_clk_provider *ctx;
+	struct clk **clk_table;
+	int i;
+
+	ctx = kzalloc(sizeof(struct rockchip_clk_provider), GFP_KERNEL);
+	if (!ctx)
+		panic("could not allocate clock provider context.\n");
 
 	clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL);
 	if (!clk_table)
-		pr_err("%s: could not allocate clock lookup table\n", __func__);
+		panic("could not allocate clock lookup table\n");
+
+	for (i = 0; i < nr_clks; ++i)
+		clk_table[i] = ERR_PTR(-ENOENT);
+
+	ctx->reg_base = base;
+	ctx->clk_data.clks = clk_table;
+	ctx->clk_data.clk_num = nr_clks;
+	spin_lock_init(&ctx->lock);
+
+	return ctx;
+}
 
-	clk_data.clks = clk_table;
-	clk_data.clk_num = nr_clks;
-	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+void __init rockchip_clk_common_cru_init(struct device_node *np)
+{
+	cru_node = np;
+	grf = ERR_PTR(-EPROBE_DEFER);
+}
+
+void __init rockchip_clk_of_add_provider(struct device_node *np,
+				struct rockchip_clk_provider *ctx)
+{
+	if (np) {
+		if (of_clk_add_provider(np, of_clk_src_onecell_get,
+					&ctx->clk_data))
+			panic("could not register clk provider\n");
+	}
 }
 
 struct regmap *rockchip_clk_get_grf(void)
@@ -290,13 +313,15 @@ struct regmap *rockchip_clk_get_grf(void)
 	return grf;
 }
 
-void rockchip_clk_add_lookup(struct clk *clk, unsigned int id)
+void rockchip_clk_add_lookup(struct rockchip_clk_provider *ctx,
+			     struct clk *clk, unsigned int id)
 {
-	if (clk_table && id)
-		clk_table[id] = clk;
+	if (ctx->clk_data.clks && id)
+		ctx->clk_data.clks[id] = clk;
 }
 
-void __init rockchip_clk_register_plls(struct rockchip_pll_clock *list,
+void __init rockchip_clk_register_plls(struct rockchip_clk_provider *ctx,
+				struct rockchip_pll_clock *list,
 				unsigned int nr_pll, int grf_lock_offset)
 {
 	struct clk *clk;
@@ -305,21 +330,22 @@ void __init rockchip_clk_register_plls(struct rockchip_pll_clock *list,
 	for (idx = 0; idx < nr_pll; idx++, list++) {
 		clk = rockchip_clk_register_pll(list->type, list->name,
 				list->parent_names, list->num_parents,
-				reg_base, list->con_offset, grf_lock_offset,
+				ctx->reg_base, list->con_offset, grf_lock_offset,
 				list->lock_shift, list->mode_offset,
 				list->mode_shift, list->rate_table,
-				list->pll_flags, &clk_lock);
+				list->pll_flags, &ctx->lock);
 		if (IS_ERR(clk)) {
 			pr_err("%s: failed to register clock %s\n", __func__,
 				list->name);
 			continue;
 		}
 
-		rockchip_clk_add_lookup(clk, list->id);
+		rockchip_clk_add_lookup(ctx, clk, list->id);
 	}
 }
 
 void __init rockchip_clk_register_branches(
+				      struct rockchip_clk_provider *ctx,
 				      struct rockchip_clk_branch *list,
 				      unsigned int nr_clk)
 {
@@ -335,56 +361,56 @@ void __init rockchip_clk_register_branches(
 		case branch_mux:
 			clk = clk_register_mux(NULL, list->name,
 				list->parent_names, list->num_parents,
-				flags, reg_base + list->muxdiv_offset,
+				flags, ctx->reg_base + list->muxdiv_offset,
 				list->mux_shift, list->mux_width,
-				list->mux_flags, &clk_lock);
+				list->mux_flags, &ctx->lock);
 			break;
 		case branch_divider:
 			if (list->div_table)
 				clk = clk_register_divider_table(NULL,
 					list->name, list->parent_names[0],
-					flags, reg_base + list->muxdiv_offset,
+					flags, ctx->reg_base + list->muxdiv_offset,
 					list->div_shift, list->div_width,
 					list->div_flags, list->div_table,
-					&clk_lock);
+					&ctx->lock);
 			else
 				clk = clk_register_divider(NULL, list->name,
 					list->parent_names[0], flags,
-					reg_base + list->muxdiv_offset,
+					ctx->reg_base + list->muxdiv_offset,
 					list->div_shift, list->div_width,
-					list->div_flags, &clk_lock);
+					list->div_flags, &ctx->lock);
 			break;
 		case branch_fraction_divider:
-			clk = rockchip_clk_register_frac_branch(list->name,
+			clk = rockchip_clk_register_frac_branch(ctx, list->name,
 				list->parent_names, list->num_parents,
-				reg_base, list->muxdiv_offset, list->div_flags,
+				ctx->reg_base, list->muxdiv_offset, list->div_flags,
 				list->gate_offset, list->gate_shift,
 				list->gate_flags, flags, list->child,
-				&clk_lock);
+				&ctx->lock);
 			break;
 		case branch_gate:
 			flags |= CLK_SET_RATE_PARENT;
 
 			clk = clk_register_gate(NULL, list->name,
 				list->parent_names[0], flags,
-				reg_base + list->gate_offset,
-				list->gate_shift, list->gate_flags, &clk_lock);
+				ctx->reg_base + list->gate_offset,
+				list->gate_shift, list->gate_flags, &ctx->lock);
 			break;
 		case branch_composite:
 			clk = rockchip_clk_register_branch(list->name,
 				list->parent_names, list->num_parents,
-				reg_base, list->muxdiv_offset, list->mux_shift,
+				ctx->reg_base, list->muxdiv_offset, list->mux_shift,
 				list->mux_width, list->mux_flags,
 				list->div_shift, list->div_width,
 				list->div_flags, list->div_table,
 				list->gate_offset, list->gate_shift,
-				list->gate_flags, flags, &clk_lock);
+				list->gate_flags, flags, &ctx->lock);
 			break;
 		case branch_mmc:
 			clk = rockchip_clk_register_mmc(
 				list->name,
 				list->parent_names, list->num_parents,
-				reg_base + list->muxdiv_offset,
+				ctx->reg_base + list->muxdiv_offset,
 				list->div_shift
 			);
 			break;
@@ -392,8 +418,8 @@ void __init rockchip_clk_register_branches(
 			clk = rockchip_clk_register_inverter(
 				list->name, list->parent_names,
 				list->num_parents,
-				reg_base + list->muxdiv_offset,
-				list->div_shift, list->div_flags, &clk_lock);
+				ctx->reg_base + list->muxdiv_offset,
+				list->div_shift, list->div_flags, &ctx->lock);
 			break;
 		}
 
@@ -410,11 +436,12 @@ void __init rockchip_clk_register_branches(
 			continue;
 		}
 
-		rockchip_clk_add_lookup(clk, list->id);
+		rockchip_clk_add_lookup(ctx, clk, list->id);
 	}
 }
 
-void __init rockchip_clk_register_armclk(unsigned int lookup_id,
+void __init rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx,
+			unsigned int lookup_id,
 			const char *name, const char *const *parent_names,
 			u8 num_parents,
 			const struct rockchip_cpuclk_reg_data *reg_data,
@@ -424,15 +451,15 @@ void __init rockchip_clk_register_armclk(unsigned int lookup_id,
 	struct clk *clk;
 
 	clk = rockchip_clk_register_cpuclk(name, parent_names, num_parents,
-					   reg_data, rates, nrates, reg_base,
-					   &clk_lock);
+					   reg_data, rates, nrates, ctx->reg_base,
+					   &ctx->lock);
 	if (IS_ERR(clk)) {
 		pr_err("%s: failed to register clock %s: %ld\n",
 		       __func__, name, PTR_ERR(clk));
 		return;
 	}
 
-	rockchip_clk_add_lookup(clk, lookup_id);
+	rockchip_clk_add_lookup(ctx, clk, lookup_id);
 }
 
 void __init rockchip_clk_protect_critical(const char *const clocks[],
@@ -449,6 +476,7 @@ void __init rockchip_clk_protect_critical(const char *const clocks[],
 	}
 }
 
+static void __iomem *rst_base;
 static unsigned int reg_restart;
 static void (*cb_restart)(void);
 static int rockchip_restart_notify(struct notifier_block *this,
@@ -457,7 +485,7 @@ static int rockchip_restart_notify(struct notifier_block *this,
 	if (cb_restart)
 		cb_restart();
 
-	writel(0xfdb9, reg_base + reg_restart);
+	writel(0xfdb9, rst_base + reg_restart);
 	return NOTIFY_DONE;
 }
 
@@ -466,10 +494,12 @@ static struct notifier_block rockchip_restart_handler = {
 	.priority = 128,
 };
 
-void __init rockchip_register_restart_notifier(unsigned int reg, void (*cb)(void))
+void __init rockchip_register_restart_notifier(struct rockchip_clk_provider *ctx,
+					       unsigned int reg, void (*cb)(void))
 {
 	int ret;
 
+	rst_base = ctx->reg_base;
 	reg_restart = reg;
 	cb_restart = cb;
 	ret = register_restart_handler(&rockchip_restart_handler);
diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
index ff8bd23..f6552b6 100644
--- a/drivers/clk/rockchip/clk.h
+++ b/drivers/clk/rockchip/clk.h
@@ -27,6 +27,7 @@
 #define CLK_ROCKCHIP_CLK_H
 
 #include <linux/io.h>
+#include <linux/clk-provider.h>
 
 struct clk;
 
@@ -127,6 +128,18 @@ enum rockchip_pll_type {
 	.nb = _nb,						\
 }
 
+/**
+ * struct rockchip_clk_provider: information about clock provider
+ * @reg_base: virtual address for the register base.
+ * @clk_data: holds clock related data like clk* and number of clocks.
+ * @lock: maintains exclusion between callbacks for a given clock-provider.
+ */
+struct rockchip_clk_provider {
+	void __iomem *reg_base;
+	struct clk_onecell_data clk_data;
+	spinlock_t lock;
+};
+
 struct rockchip_pll_rate_table {
 	unsigned long rate;
 	unsigned int nr;
@@ -508,21 +521,29 @@ struct rockchip_clk_branch {
 		.div_flags	= if,				\
 	}
 
-void rockchip_clk_init(struct device_node *np, void __iomem *base,
-		       unsigned long nr_clks);
+struct rockchip_clk_provider *rockchip_clk_init(
+			void __iomem *base, unsigned long nr_clks);
+void rockchip_clk_common_cru_init(struct device_node *np);
+void rockchip_clk_of_add_provider(struct device_node *np,
+				struct rockchip_clk_provider *ctx);
 struct regmap *rockchip_clk_get_grf(void);
-void rockchip_clk_add_lookup(struct clk *clk, unsigned int id);
-void rockchip_clk_register_branches(struct rockchip_clk_branch *clk_list,
+void rockchip_clk_add_lookup(struct rockchip_clk_provider *ctx,
+			     struct clk *clk, unsigned int id);
+void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
+				    struct rockchip_clk_branch *list,
 				    unsigned int nr_clk);
-void rockchip_clk_register_plls(struct rockchip_pll_clock *pll_list,
+void rockchip_clk_register_plls(struct rockchip_clk_provider *ctx,
+				struct rockchip_pll_clock *pll_list,
 				unsigned int nr_pll, int grf_lock_offset);
-void rockchip_clk_register_armclk(unsigned int lookup_id, const char *name,
+void rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx,
+			unsigned int lookup_id, const char *name,
 			const char *const *parent_names, u8 num_parents,
 			const struct rockchip_cpuclk_reg_data *reg_data,
 			const struct rockchip_cpuclk_rate_table *rates,
 			int nrates);
 void rockchip_clk_protect_critical(const char *const clocks[], int nclocks);
-void rockchip_register_restart_notifier(unsigned int reg, void (*cb)(void));
+void rockchip_register_restart_notifier(struct rockchip_clk_provider *ctx,
+					unsigned int reg, void (*cb)(void));
 
 #define ROCKCHIP_SOFTRST_HIWORD_MASK	BIT(0)
 
-- 
1.7.9.5




From dan.carpenter at oracle.com  Wed Jan 20 01:53:52 2016
From: dan.carpenter at oracle.com (Dan Carpenter)
Date: Wed, 20 Jan 2016 12:53:52 +0300
Subject: [patch] drm: rockchip: signedness bug in dw_mipi_dsi_get_lane_bps()
Message-ID: <20160120095352.GA19898@mwanda>

"bpp" needs to be signed for the error handling to work.

Fixes: 84e05408fcfe ('drm: rockchip: Support Synopsys DW MIPI DSI')
Signed-off-by: Dan Carpenter <dan.carpenter at oracle.com>

diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
index 7bfe243..c35e5af 100644
--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
@@ -461,7 +461,8 @@ static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi)
 
 static int dw_mipi_dsi_get_lane_bps(struct dw_mipi_dsi *dsi)
 {
-	unsigned int bpp, i, pre;
+	int bpp;
+	unsigned int i, pre;
 	unsigned long mpclk, pllref, tmp;
 	unsigned int m = 1, n = 1, target_mbps = 1000;
 	unsigned int max_mbps = dptdin_map[ARRAY_SIZE(dptdin_map) - 1].max_mbps;


From heiko at sntech.de  Wed Jan 20 06:42:54 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Wed, 20 Jan 2016 15:42:54 +0100
Subject: [PATCH] clk-divider: make sure read-only dividers do not write to
 their register
Message-ID: <8710207.gKyLbM2bKY@phil>

Commit e6d5e7d90be9 ("clk-divider: Fix READ_ONLY when divider > 1") removed
the special ops struct for read-only clocks and instead opted to handle
them regularly.

On the rk3368 this results in breakage as aclkm now gets set a value.
While it is the same divider, the A53 core still doesn't like it.
The reason being that "ACLKENMasserts one clock cycle before the rising
edge of ACLKM" and the clock should only be touched when STANDBYWFIL2
is asserted.

So make sure, read-only clocks don't touch the clock-register at all
even if only writing the same value.

Fixes: e6d5e7d90be9 ("clk-divider: Fix READ_ONLY when divider > 1")
Reported-by: Zhang Qing <zhangqing at rock-chips.com>
Signed-off-by: Heiko Stuebner <heiko at sntech.de>
---
 drivers/clk/clk-divider.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index ded3ff4..6dfe261 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -391,6 +391,10 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
 	unsigned long flags = 0;
 	u32 val;
 
+	/* don't do writes in read-only case */
+	if (divider->flags & CLK_DIVIDER_READ_ONLY)
+		return 0;
+
 	value = divider_get_val(rate, parent_rate, divider->table,
 				divider->width, divider->flags);
 
-- 
2.6.4




From james.hogan at imgtec.com  Wed Jan 20 06:57:31 2016
From: james.hogan at imgtec.com (James Hogan)
Date: Wed, 20 Jan 2016 14:57:31 +0000
Subject: [PATCH] clk-divider: make sure read-only dividers do not write
 to their register
In-Reply-To: <8710207.gKyLbM2bKY@phil>
References: <8710207.gKyLbM2bKY@phil>
Message-ID: <20160120145731.GA6864@jhogan-linux.le.imgtec.org>

On Wed, Jan 20, 2016 at 03:42:54PM +0100, Heiko Stuebner wrote:
> Commit e6d5e7d90be9 ("clk-divider: Fix READ_ONLY when divider > 1") removed
> the special ops struct for read-only clocks and instead opted to handle
> them regularly.
> 
> On the rk3368 this results in breakage as aclkm now gets set a value.
> While it is the same divider, the A53 core still doesn't like it.
> The reason being that "ACLKENMasserts one clock cycle before the rising
> edge of ACLKM" and the clock should only be touched when STANDBYWFIL2
> is asserted.
> 
> So make sure, read-only clocks don't touch the clock-register at all
> even if only writing the same value.
> 
> Fixes: e6d5e7d90be9 ("clk-divider: Fix READ_ONLY when divider > 1")
> Reported-by: Zhang Qing <zhangqing at rock-chips.com>
> Signed-off-by: Heiko Stuebner <heiko at sntech.de>

It wouldn't hurt to clarify in the comment that this isn't just an
optimisation, and that even writing the same value to the hardware may
not be safe.

In any case
Reviewed-by: James Hogan <james.hogan at imgtec.com>

Cheers
James

> ---
>  drivers/clk/clk-divider.c | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
> index ded3ff4..6dfe261 100644
> --- a/drivers/clk/clk-divider.c
> +++ b/drivers/clk/clk-divider.c
> @@ -391,6 +391,10 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
>  	unsigned long flags = 0;
>  	u32 val;
>  
> +	/* don't do writes in read-only case */
> +	if (divider->flags & CLK_DIVIDER_READ_ONLY)
> +		return 0;
> +
>  	value = divider_get_val(rate, parent_rate, divider->table,
>  				divider->width, divider->flags);
>  
> -- 
> 2.6.4
> 
> 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160120/b6fd5394/attachment.sig>

From afaerber at suse.de  Wed Jan 20 08:38:33 2016
From: afaerber at suse.de (=?UTF-8?q?Andreas=20F=C3=A4rber?=)
Date: Wed, 20 Jan 2016 17:38:33 +0100
Subject: [PATCH] ARM64: dts: rockchip: Add GeekBox config
Message-ID: <1453307913-8907-1-git-send-email-afaerber@suse.de>

The GeekBox contains an MXM3 module with a Rockchip RK3368 SoC.
Some connectors are available directly on the module.

This adds initial support, namely serial, PMIC and USB.

Signed-off-by: Andreas F?rber <afaerber at suse.de>
---
 Hi Heiko,
 
 Here's an initial shot at a .dts for GeekBox.
 GMAC was not yet working for me.
 
 Regards,
 Andreas

 arch/arm64/boot/dts/rockchip/Makefile            |   1 +
 arch/arm64/boot/dts/rockchip/rk3368-geekbox.dts  |  48 +++++
 arch/arm64/boot/dts/rockchip/rk3368-geekbox.dtsi | 242 +++++++++++++++++++++++
 3 files changed, 291 insertions(+)
 create mode 100644 arch/arm64/boot/dts/rockchip/rk3368-geekbox.dts
 create mode 100644 arch/arm64/boot/dts/rockchip/rk3368-geekbox.dtsi

diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile
index e3f0b5f..df37865 100644
--- a/arch/arm64/boot/dts/rockchip/Makefile
+++ b/arch/arm64/boot/dts/rockchip/Makefile
@@ -1,4 +1,5 @@
 dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-evb-act8846.dtb
+dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-geekbox.dtb
 dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-r88.dtb
 
 always		:= $(dtb-y)
diff --git a/arch/arm64/boot/dts/rockchip/rk3368-geekbox.dts b/arch/arm64/boot/dts/rockchip/rk3368-geekbox.dts
new file mode 100644
index 0000000..eb24ecd
--- /dev/null
+++ b/arch/arm64/boot/dts/rockchip/rk3368-geekbox.dts
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2016 Andreas F?rber
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of the
+ *     License, or (at your option) any later version.
+ *
+ *     This file is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     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.
+ */
+
+/dts-v1/;
+#include "rk3368-geekbox.dtsi"
+
+/ {
+	model = "GeekBox";
+};
diff --git a/arch/arm64/boot/dts/rockchip/rk3368-geekbox.dtsi b/arch/arm64/boot/dts/rockchip/rk3368-geekbox.dtsi
new file mode 100644
index 0000000..f083cdb
--- /dev/null
+++ b/arch/arm64/boot/dts/rockchip/rk3368-geekbox.dtsi
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2016 Andreas F?rber
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of the
+ *     License, or (at your option) any later version.
+ *
+ *     This file is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     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.
+ */
+
+#include "rk3368.dtsi"
+
+/ {
+	compatible = "geekbuying,geekbox", "rockchip,rk3368";
+
+	chosen {
+		stdout-path = "serial2:115200n8";
+	};
+
+	memory {
+		device_type = "memory";
+		reg = <0x0 0x0 0x0 0x80000000>;
+	};
+
+	leds: gpio-leds {
+		compatible = "gpio-leds";
+
+		blue {
+			gpios = <&gpio2 2 GPIO_ACTIVE_HIGH>;
+			label = "geekbox:blue:led";
+			default-state = "on";
+		};
+
+		red {
+			gpios = <&gpio2 3 GPIO_ACTIVE_HIGH>;
+			label = "geekbox:red:led";
+			default-state = "off";
+		};
+	};
+
+	vcc_sys: vcc-sys-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_sys";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		regulator-always-on;
+		regulator-boot-on;
+	};
+};
+
+&i2c0 {
+	status = "okay";
+
+	rk808: pmic at 1b {
+		compatible = "rockchip,rk808";
+		reg = <0x1b>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pmic_int>, <&pmic_sleep>;
+		interrupt-parent = <&gpio0>;
+		interrupts = <5 IRQ_TYPE_LEVEL_LOW>;
+		rockchip,system-power-controller;
+		vcc1-supply = <&vcc_sys>;
+		vcc2-supply = <&vcc_sys>;
+		vcc3-supply = <&vcc_sys>;
+		vcc4-supply = <&vcc_sys>;
+		vcc6-supply = <&vcc_sys>;
+		vcc7-supply = <&vcc_sys>;
+		vcc8-supply = <&vcc_io>;
+		vcc9-supply = <&vcc_sys>;
+		vcc10-supply = <&vcc_sys>;
+		vcc11-supply = <&vcc_sys>;
+		vcc12-supply = <&vcc_io>;
+		clock-output-names = "xin32k", "rk808-clkout2";
+		#clock-cells = <1>;
+
+		regulators {
+			vdd_cpu: DCDC_REG1 {
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-min-microvolt = <700000>;
+				regulator-max-microvolt = <1500000>;
+				regulator-name = "vdd_cpu";
+			};
+
+			vdd_log: DCDC_REG2 {
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-min-microvolt = <700000>;
+				regulator-max-microvolt = <1500000>;
+				regulator-name = "vdd_log";
+			};
+
+			vcc_ddr: DCDC_REG3 {
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-name = "vcc_ddr";
+			};
+
+			vcc_io: DCDC_REG4 {
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-name = "vcc_io";
+			};
+
+			vcc18_flash: LDO_REG1 {
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-name = "vcc18_flash";
+			};
+
+			vcc33_lcd: LDO_REG2 {
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-name = "vcc33_lcd";
+			};
+
+			vdd_10: LDO_REG3 {
+				regulator-always-on;
+				regulator-min-microvolt = <1000000>;
+				regulator-max-microvolt = <1000000>;
+				regulator-name = "vdd_10";
+			};
+
+			vcca_18: LDO_REG4 {
+				regulator-boot-on;
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-name = "vcca_18";
+			};
+
+			vccio_sd: LDO_REG5 {
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-name = "vccio_sd";
+			};
+
+			vdd10_lcd: LDO_REG6 {
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-min-microvolt = <1000000>;
+				regulator-max-microvolt = <1000000>;
+				regulator-name = "vdd10_lcd";
+			};
+
+			vcc_18: LDO_REG7 {
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-name = "vcc_18";
+			};
+
+			vcc18_lcd: LDO_REG8 {
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-name = "vcc18_lcd";
+			};
+
+			vcc_sd: SWITCH_REG1 {
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-name = "vcc_sd";
+			};
+
+			vcc_lan: SWITCH_REG2 {
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-name = "vcc_lan";
+			};
+		};
+	};
+};
+
+&pinctrl {
+	pmic {
+		pmic_sleep: pmic-sleep {
+			rockchip,pins = <0 0 RK_FUNC_GPIO &pcfg_pull_down>;
+		};
+
+		pmic_int: pmic-int {
+			rockchip,pins = <0 5 RK_FUNC_GPIO &pcfg_pull_up>;
+		};
+	};
+};
+
+&uart2 {
+	status = "okay";
+};
+
+&usb_host0_ehci {
+	status = "okay";
+};
+
+&usb_otg {
+	status = "okay";
+};
+
+&wdt {
+	status = "okay";
+};
-- 
2.6.2



From dianders at chromium.org  Wed Jan 20 08:50:51 2016
From: dianders at chromium.org (Doug Anderson)
Date: Wed, 20 Jan 2016 08:50:51 -0800
Subject: [PATCH 0/5] clk: rockchip: add full support for HDMI clock on
 rk3288
In-Reply-To: <CAAObsKChYN5nmcm5OWnAzNRGDi_QGSoKvN+0FizhjeT-3mmeXQ@mail.gmail.com>
References: <1415087559-19444-1-git-send-email-kever.yang@rock-chips.com>
 <48806799.thBSifdUp8@diego> <54647142.4080800@rock-chips.com>
 <CAD=FV=Vzec3Qm-kc6zZ4VxSREoZbzVkWbzp-noHYdcEVrLYYSw@mail.gmail.com>
 <CAAObsKChYN5nmcm5OWnAzNRGDi_QGSoKvN+0FizhjeT-3mmeXQ@mail.gmail.com>
Message-ID: <CAD=FV=Wgu=xQ9PcVqfzcedXVU3Rh-HKbBVxHuqgV9OZJEYhYvg@mail.gmail.com>

Tomeu,

On Tue, Jan 19, 2016 at 4:02 AM, Tomeu Vizoso <tomeu at tomeuvizoso.net> wrote:
> On 13 November 2014 at 23:59, Doug Anderson <dianders at chromium.org> wrote:
>> Hi,
>>
>> On Thu, Nov 13, 2014 at 12:52 AM, Kever Yang <kever.yang at rock-chips.com> wrote:
>>> Hi Heiko,
>>>
>>> On 11/07/2014 05:06 AM, Heiko St?bner wrote:
>>>>
>>>> Hi Kever,
>>>>
>>>> Am Dienstag, 4. November 2014, 15:52:34 schrieb Kever Yang:
>>>>>
>>>>> we are going to make a clock usage solution for rk3288:
>>>>> 1. CPLL and GPLL always not change after assign init;
>>>>> 2. NPLL default as 500MHz, may used for most scene;
>>>>> 3. NPLL may be changed by VOP(HDMI) clock for some special
>>>>>     frequency requirement.
>>>>>
>>>>>      I test it with rk3288 evb on top of Heiko's clk-for-next
>>>>
>>>> In general I'm not really sure if allowing one component to arbitarily
>>>> change
>>>> a shared clock wouldn't result in trouble.
>>>>
>>>> At the moment only dclk_vop0 is included in your series, while the hdmi
>>>> controller can connect to both vop0 and vop1.
>>>> And as Doug mentioned the gpu also has the npll as one possible source.
>>>
>>> I think the problem GPU HANGs with 480MHz clock from usbphy has
>>> been fixed with my patch to gerrit:
>>> https://chromium-review.googlesource.com/#/c/229554/
>>>>
>>>>
>>>> Looking through the clock-tree there are a lot more components possibly
>>>> using
>>>> (or wanting to use) the npll: of course the VOPs, the edp, hdmi, isp,
>>>> hevc,
>>>> gpu, tsp uart0 and gmac. So I'm slightly uncomfortable with somehow
>>>> reserving
>>>> the npll for VOP0 alone.
>>>
>>> It's true that I customized the usage of npll for VOP0 when we need some
>>> very special frequency, but it doesn't means other modules can't use the
>>> npll, it will always decided by clock core for module clocks that which
>>> parent
>>> is the best.
>>
>> We will just need to be very careful.  As I've mentioned in the past
>> we'll need to think about what happens to other clocks that happen to
>> be parented by NPLL whenever we change it.
>>
>> So if we do this:
>>
>> 1. NPLL happens to be 500MHz.
>> 2. We set GPU to be 500MHz and it picks NPLL.
>> 3. We change NPLL to a different speed (like 600MHz).
>>
>> ...I believe in this scenario the GPU would start running at 600MHz
>> immediately.  We'd need to add special code to watch out for this and
>> pick an alternate clock for the GPU (like the USB 480) before the NPLL
>> change.  If NPLL later changes back to 500MHz and the GPU still wanted
>> 500MHz, we'd have to decide what to do.
>>
>> The summary is: it's pretty complicated
>
> Hello,
>
> I have heard that this is still an issue in RK3288 support in mainline
> so have given a look and wonder if you have considered having the
> consumers of a shared clock (which could be a child clock) use
> PRE_RATE_CHANGE notifications to adapt to changes in a parent's rate,
> or to abort them if that's not possible.
>
> Alternatively, a consumer can set min and max constraints if it
> doesn't want for others to mess with its provider, and let the other
> consumer deal with failed rate changes (probably by choosing another
> provider).
>
> Have I missed any problems with that?

I don't think it's really that simple, but I certainly could be wrong.

Summary of the system:

* There are two VOPs (video processors) on rk3288: the big one and the
little one.

* The big VOP can handle higher resolutions than the little one but
uses more power.

* Both VOPs two can be sourced from CPLL, GPLL, or NPLL.

* Both VOPs can be setup to process video for either HDMI, eDP, LVDS, ...

* NPLL is the "new" PLL and is kinda like an extra PLL that's not used
for any of the core peripherals.  It's intended to be one that's
changed dynamically based on system needs.

* GPLL appears intended (in current designs) to run at 594 MHz on
rk3288 using a special "low jitter" setting.  This is intended to be
the source clock for HDMI when running the very common 148.5 and 74.25
rates.  It also it intended to be the source clock for various other
peripherals like i2c, eMMC, SD, etc.  This also runs the GPU when the
GPU is running at ~600MHz or ~300MHz.

* CPLL appears intended (in current designs) to run at 400MHz.  There
are some peripherals that run off this plus the GPU at 400MHz, 200MHz,
or 100MHz.


Let's imagine that we're booting up on a laptop where the builtin
panel is hooked up via eDP.  Let's say that the panel really wants
76.42 MHz but that will work just fine with 74.25 MHz too.  If this is
a system that has no other graphics needs, we'd want eDP to take NPLL
and grab 76.42 MHz.  Make the best rate, right?

...but what about if it's a system with an HDMI port?  The eDP panel
is built in and we know it works with 74.25 MHz, but the user could
plug into all sorts of HDMI devices and they may require some very
special PLL rates to get there.  So if we happen to take NPLL for eDP
we'll need to know to switch away from it once something is plugged
into HDMI.  We don't want to block the HDMI request since we know we
can run eDP at 74.25 off GPLL.  Presumably we could see the HDMI
request and then try to remux the eDP on the fly.  ...but what decides
the HDMI is more important to eDP?  You might want different rules if
the eDP port is somehow exposed and the user might plug different
panels into it...


-Doug


From robh at kernel.org  Wed Jan 20 10:28:40 2016
From: robh at kernel.org (Rob Herring)
Date: Wed, 20 Jan 2016 12:28:40 -0600
Subject: [PATCH v2 1/4] dt-bindings: power: reset: add document for
 reboot-mode driver
In-Reply-To: <1452598189-8272-1-git-send-email-andy.yan@rock-chips.com>
References: <1452598029-8222-1-git-send-email-andy.yan@rock-chips.com>
 <1452598189-8272-1-git-send-email-andy.yan@rock-chips.com>
Message-ID: <20160120182840.GA15741@rob-hp-laptop>

On Tue, Jan 12, 2016 at 07:29:49PM +0800, Andy Yan wrote:
> add device tree binding document for reboot-mode driver
> 
> Signed-off-by: Andy Yan <andy.yan at rock-chips.com>
> 
> ---
> 
> Changes in v2: None
> Changes in v1: None
> 
>  .../bindings/power/reset/reboot-mode.txt           | 41 +++++++++++++++++
>  .../bindings/power/reset/syscon-reboot-mode.txt    | 52 ++++++++++++++++++++++
>  2 files changed, 93 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/power/reset/reboot-mode.txt
>  create mode 100644 Documentation/devicetree/bindings/power/reset/syscon-reboot-mode.txt
> 
> diff --git a/Documentation/devicetree/bindings/power/reset/reboot-mode.txt b/Documentation/devicetree/bindings/power/reset/reboot-mode.txt
> new file mode 100644
> index 0000000..81d9f66
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/power/reset/reboot-mode.txt
> @@ -0,0 +1,41 @@
> +Generic reboot mode core map driver
> +
> +This driver get reboot mode arguments and call the write
> +interface to stores the magic value in special register
> +or ram . Then the bootloader can read it and take different
> +action according to the argument stored.
> +
> +Required properties:
> +- compatible: only support "syscon-reboot-mode" now.
> +
> +Each mode is represented as a sub-node of reboot_mode:
> +
> +Subnode required properties:
> +- linux,mode: reboot mode command,such as "loader", "recovery", "fastboot".
> +- loader,magic: magic number for the mode, this is vendor specific.
> +
> +Example:
> +	reboot_mode {

reboot-mode instead please.

> +		compatible = "syscon-reboot-mode";
> +		offset = <0x40>;

This doc by itself is a little confusing. For example, is a child of the 
syscon node? I would remove offset (and perhaps compatible) from this 
example.

> +
> +		loader {
> +			linux,mode = "loader";
> +			loader,magic = <BOOT_LOADER>;
> +		};

Sorry, my previous suggestion was not clear. I'm suggesting get rid of 
the subnodes and just do properties like this:

loader = <BOOT_LOADER>;
maskrom = <BOOT_MASKROM>;

That's the same amount of information unless node names and linux,mode 
values are going to diverge. Do they need to? I can't see a reason.


We need to be clear what loader means. More specifically, it is boot 
into bootloader shell. 

> +
> +		maskrom {

In theory, the bootrom could have multiple modes. This typically means a 
USB download mode. So perhaps a more precise name would be 
"rom-download". 

In chips I'm familiar with the bootrom mode is selected via a different 
mechanism than the secondary bootloader modes, but I suppose the same 
mechanism could be used.

> +			linux,mode = "maskrom";
> +			loader,magic = <BOOT_MASKROM>;
> +		};
> +
> +		recovery {
> +			linux,mode = "recovery";
> +			loader,magic = <BOOT_RECOVERY>;
> +		};
> +
> +		fastboot {
> +			linux,mode = "fastboot";
> +			loader,magic = <BOOT_FASTBOOT>;
> +		};


From john.stultz at linaro.org  Wed Jan 20 10:47:34 2016
From: john.stultz at linaro.org (John Stultz)
Date: Wed, 20 Jan 2016 10:47:34 -0800
Subject: [PATCH v2 1/4] dt-bindings: power: reset: add document for
 reboot-mode driver
In-Reply-To: <20160120182840.GA15741@rob-hp-laptop>
References: <1452598029-8222-1-git-send-email-andy.yan@rock-chips.com>
 <1452598189-8272-1-git-send-email-andy.yan@rock-chips.com>
 <20160120182840.GA15741@rob-hp-laptop>
Message-ID: <CALAqxLVvJqL60nKfrDiZ+p-1ujxuG72iUN3DCvEE3WJ9bpnGLA@mail.gmail.com>

On Wed, Jan 20, 2016 at 10:28 AM, Rob Herring <robh at kernel.org> wrote:
> On Tue, Jan 12, 2016 at 07:29:49PM +0800, Andy Yan wrote:
>> add device tree binding document for reboot-mode driver
>>
>> Signed-off-by: Andy Yan <andy.yan at rock-chips.com>
>>
>> ---
>>
>> Changes in v2: None
>> Changes in v1: None
>>
>>  .../bindings/power/reset/reboot-mode.txt           | 41 +++++++++++++++++
>>  .../bindings/power/reset/syscon-reboot-mode.txt    | 52 ++++++++++++++++++++++
>>  2 files changed, 93 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/power/reset/reboot-mode.txt
>>  create mode 100644 Documentation/devicetree/bindings/power/reset/syscon-reboot-mode.txt
>>
>> diff --git a/Documentation/devicetree/bindings/power/reset/reboot-mode.txt b/Documentation/devicetree/bindings/power/reset/reboot-mode.txt
>> new file mode 100644
>> index 0000000..81d9f66
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/power/reset/reboot-mode.txt
>> @@ -0,0 +1,41 @@
>> +Generic reboot mode core map driver
>> +
>> +This driver get reboot mode arguments and call the write
>> +interface to stores the magic value in special register
>> +or ram . Then the bootloader can read it and take different
>> +action according to the argument stored.
>> +
>> +Required properties:
>> +- compatible: only support "syscon-reboot-mode" now.
>> +
>> +Each mode is represented as a sub-node of reboot_mode:
>> +
>> +Subnode required properties:
>> +- linux,mode: reboot mode command,such as "loader", "recovery", "fastboot".
>> +- loader,magic: magic number for the mode, this is vendor specific.
>> +
>> +Example:
>> +     reboot_mode {
>
> reboot-mode instead please.
>
>> +             compatible = "syscon-reboot-mode";
>> +             offset = <0x40>;
>
> This doc by itself is a little confusing. For example, is a child of the
> syscon node? I would remove offset (and perhaps compatible) from this
> example.
>
>> +
>> +             loader {
>> +                     linux,mode = "loader";
>> +                     loader,magic = <BOOT_LOADER>;
>> +             };
>
> Sorry, my previous suggestion was not clear. I'm suggesting get rid of
> the subnodes and just do properties like this:
>
> loader = <BOOT_LOADER>;
> maskrom = <BOOT_MASKROM>;
>
> That's the same amount of information unless node names and linux,mode
> values are going to diverge. Do they need to? I can't see a reason.


It seems like devices already have a number of various different
vendor specific commands.  So this sort of flexibility helps us adapt
the driver to different hardware/system environments (which may use
different conventions then what Android commonly uses).

Unless I'm misunderstanding you and you're instead suggesting we can
dynamically parse the command mode from the node name?

That said, I agree we should try to push vendors to using the same
conventions when they are providing the same functionality. But I'm
not sure we should enforce that by making vendors introduce a new dt
binding for every new reboot command they want to implement.

thanks
-john


From heiko at sntech.de  Wed Jan 20 11:51:06 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Wed, 20 Jan 2016 20:51:06 +0100
Subject: [PATCH v2] clk-divider: make sure read-only dividers do not write to
 their register
In-Reply-To: <8710207.gKyLbM2bKY@phil>
References: <8710207.gKyLbM2bKY@phil>
Message-ID: <3578615.iBrKplh45Q@phil>

Commit e6d5e7d90be9 ("clk-divider: Fix READ_ONLY when divider > 1") removed
the special ops struct for read-only clocks and instead opted to handle
them regularly.

On the rk3368 this results in breakage as aclkm now gets set a value.
While it is the same divider value, the A53 core still doesn't like it,
which can result in the cpu ending up in a hang.
The reason being that "ACLKENMasserts one clock cycle before the rising
edge of ACLKM" and the clock should only be touched when STANDBYWFIL2
is asserted.

So make sure read-only clocks don't touch the clock-register at all
even if only writing the same value, as even writing the same value
may not be safe in all cases.

Fixes: e6d5e7d90be9 ("clk-divider: Fix READ_ONLY when divider > 1")
Reported-by: Zhang Qing <zhangqing at rock-chips.com>
Signed-off-by: Heiko Stuebner <heiko at sntech.de>
Reviewed-by: James Hogan <james.hogan at imgtec.com>
---
changes in v2:
- emphasize that even setting the same divider may be unsafe

 drivers/clk/clk-divider.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index ded3ff4..6dfe261 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -391,6 +391,10 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
 	unsigned long flags = 0;
 	u32 val;
 
+	/* don't do writes in read-only case, as it may be unsafe */
+	if (divider->flags & CLK_DIVIDER_READ_ONLY)
+		return 0;
+
 	value = divider_get_val(rate, parent_rate, divider->table,
 				divider->width, divider->flags);
 
-- 
2.6.4




From robh at kernel.org  Wed Jan 20 11:53:44 2016
From: robh at kernel.org (Rob Herring)
Date: Wed, 20 Jan 2016 13:53:44 -0600
Subject: [PATCH v2 1/4] dt-bindings: power: reset: add document for
 reboot-mode driver
In-Reply-To: <CALAqxLVvJqL60nKfrDiZ+p-1ujxuG72iUN3DCvEE3WJ9bpnGLA@mail.gmail.com>
References: <1452598029-8222-1-git-send-email-andy.yan@rock-chips.com>
 <1452598189-8272-1-git-send-email-andy.yan@rock-chips.com>
 <20160120182840.GA15741@rob-hp-laptop>
 <CALAqxLVvJqL60nKfrDiZ+p-1ujxuG72iUN3DCvEE3WJ9bpnGLA@mail.gmail.com>
Message-ID: <CAL_Jsq+suCyqSi+cJiVLuA0aZKSciAaeUKW9A5Y_X_3wyW60Ug@mail.gmail.com>

On Wed, Jan 20, 2016 at 12:47 PM, John Stultz <john.stultz at linaro.org> wrote:
> On Wed, Jan 20, 2016 at 10:28 AM, Rob Herring <robh at kernel.org> wrote:
>> On Tue, Jan 12, 2016 at 07:29:49PM +0800, Andy Yan wrote:
>>> add device tree binding document for reboot-mode driver
>>>
>>> Signed-off-by: Andy Yan <andy.yan at rock-chips.com>
>>>
>>> ---
>>>
>>> Changes in v2: None
>>> Changes in v1: None
>>>
>>>  .../bindings/power/reset/reboot-mode.txt           | 41 +++++++++++++++++
>>>  .../bindings/power/reset/syscon-reboot-mode.txt    | 52 ++++++++++++++++++++++
>>>  2 files changed, 93 insertions(+)
>>>  create mode 100644 Documentation/devicetree/bindings/power/reset/reboot-mode.txt
>>>  create mode 100644 Documentation/devicetree/bindings/power/reset/syscon-reboot-mode.txt
>>>
>>> diff --git a/Documentation/devicetree/bindings/power/reset/reboot-mode.txt b/Documentation/devicetree/bindings/power/reset/reboot-mode.txt
>>> new file mode 100644
>>> index 0000000..81d9f66
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/power/reset/reboot-mode.txt
>>> @@ -0,0 +1,41 @@
>>> +Generic reboot mode core map driver
>>> +
>>> +This driver get reboot mode arguments and call the write
>>> +interface to stores the magic value in special register
>>> +or ram . Then the bootloader can read it and take different
>>> +action according to the argument stored.
>>> +
>>> +Required properties:
>>> +- compatible: only support "syscon-reboot-mode" now.
>>> +
>>> +Each mode is represented as a sub-node of reboot_mode:
>>> +
>>> +Subnode required properties:
>>> +- linux,mode: reboot mode command,such as "loader", "recovery", "fastboot".
>>> +- loader,magic: magic number for the mode, this is vendor specific.
>>> +
>>> +Example:
>>> +     reboot_mode {
>>
>> reboot-mode instead please.
>>
>>> +             compatible = "syscon-reboot-mode";
>>> +             offset = <0x40>;
>>
>> This doc by itself is a little confusing. For example, is a child of the
>> syscon node? I would remove offset (and perhaps compatible) from this
>> example.
>>
>>> +
>>> +             loader {
>>> +                     linux,mode = "loader";
>>> +                     loader,magic = <BOOT_LOADER>;
>>> +             };
>>
>> Sorry, my previous suggestion was not clear. I'm suggesting get rid of
>> the subnodes and just do properties like this:
>>
>> loader = <BOOT_LOADER>;
>> maskrom = <BOOT_MASKROM>;
>>
>> That's the same amount of information unless node names and linux,mode
>> values are going to diverge. Do they need to? I can't see a reason.
>
>
> It seems like devices already have a number of various different
> vendor specific commands.  So this sort of flexibility helps us adapt
> the driver to different hardware/system environments (which may use
> different conventions then what Android commonly uses).
>
> Unless I'm misunderstanding you and you're instead suggesting we can
> dynamically parse the command mode from the node name?

Yes, except it would be the property name. The driver could either
iterate over the properties of the reboot-mode node or lookup the
property when it has the reboot reason string. We generally like to
parse everything up front at probe, but the latter would be somewhat
easier to implement. Perhaps we would want to prefix nodes with
"mode-" or something.

Vendors can certainly still add their own (e.g.
vendor,halt-and-catch-fire = <0xdead>).

The question here really is will we ever need additional data for each
mode (in the DT) besides "magic". We can do some amount of extending
the property like allowing more that single 32-bit value. We can't mix
strings and integers though. If we don't need that, then lets go with
the more concise approach. This isn't really an evolving area.
Selecting boot mode/device has existed for a long time on PCs (it is
fixed list in the IPMI spec for example). So I can't imagine that we'd
have to extend this, but I'd like to hear thoughts on it. You had
mentioned the Galaxy Nexus having a value and a string, but I'd find
it surprising if the bootloader actually uses the string.

> That said, I agree we should try to push vendors to using the same
> conventions when they are providing the same functionality. But I'm
> not sure we should enforce that by making vendors introduce a new dt
> binding for every new reboot command they want to implement.

Either way they need to document it. It's either a new linux,mode
value or a new property name. Vendors are free to add whatever they
want, but for upstream it has to go thru review.

Rob


From john.stultz at linaro.org  Wed Jan 20 12:25:44 2016
From: john.stultz at linaro.org (John Stultz)
Date: Wed, 20 Jan 2016 12:25:44 -0800
Subject: [PATCH v2 1/4] dt-bindings: power: reset: add document for
 reboot-mode driver
In-Reply-To: <CAL_Jsq+suCyqSi+cJiVLuA0aZKSciAaeUKW9A5Y_X_3wyW60Ug@mail.gmail.com>
References: <1452598029-8222-1-git-send-email-andy.yan@rock-chips.com>
 <1452598189-8272-1-git-send-email-andy.yan@rock-chips.com>
 <20160120182840.GA15741@rob-hp-laptop>
 <CALAqxLVvJqL60nKfrDiZ+p-1ujxuG72iUN3DCvEE3WJ9bpnGLA@mail.gmail.com>
 <CAL_Jsq+suCyqSi+cJiVLuA0aZKSciAaeUKW9A5Y_X_3wyW60Ug@mail.gmail.com>
Message-ID: <CALAqxLVY7mg2arS7u63fRow8Ln2u7hbrfFf1h2UcoojtwJMRyw@mail.gmail.com>

On Wed, Jan 20, 2016 at 11:53 AM, Rob Herring <robh at kernel.org> wrote:
> On Wed, Jan 20, 2016 at 12:47 PM, John Stultz <john.stultz at linaro.org> wrote:
> The question here really is will we ever need additional data for each
> mode (in the DT) besides "magic". We can do some amount of extending
> the property like allowing more that single 32-bit value. We can't mix
> strings and integers though. If we don't need that, then lets go with
> the more concise approach. This isn't really an evolving area.
> Selecting boot mode/device has existed for a long time on PCs (it is
> fixed list in the IPMI spec for example). So I can't imagine that we'd
> have to extend this, but I'd like to hear thoughts on it. You had
> mentioned the Galaxy Nexus having a value and a string, but I'd find
> it surprising if the bootloader actually uses the string.

So I've been told the galaxy nexus and possibly other older TI
hardware used only a string.

And the HTC implementations I've seen have code for both a string and
command to be placed, but usually don't actually copy anything to the
string address (which suggest its not used by the bootloader, as you
note).

Either way, we'd have to extend or implement a new driver, but that's
a bridge I think we can cross later when we see such hardware support
upstream (rather then try to create a super driver that handles
everything a creative firmware developer could come up with).

thanks
-john


From heiko at sntech.de  Wed Jan 20 13:49:19 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Wed, 20 Jan 2016 22:49:19 +0100
Subject: [PATCH 3/4] clk: rockchip: rk3368: fix parents of video
 encoder/decoder
In-Reply-To: <1453326560-20475-1-git-send-email-heiko@sntech.de>
References: <1453326560-20475-1-git-send-email-heiko@sntech.de>
Message-ID: <1453326560-20475-3-git-send-email-heiko@sntech.de>

The vdpu and vepu clocks can also be parented to the npll and current
parent list also is wrong as it would use the npll as "usbphy" source,
so adapt the parent to the correct one.

Fixes: 3536c97a52db ("clk: rockchip: add rk3368 clock controller")
Signed-off-by: Heiko Stuebner <heiko at sntech.de>
---
 drivers/clk/rockchip/clk-rk3368.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/rockchip/clk-rk3368.c b/drivers/clk/rockchip/clk-rk3368.c
index 3c9733e..6037beb 100644
--- a/drivers/clk/rockchip/clk-rk3368.c
+++ b/drivers/clk/rockchip/clk-rk3368.c
@@ -384,10 +384,10 @@ static struct rockchip_clk_branch rk3368_clk_branches[] __initdata = {
 	 * Clock-Architecture Diagram 3
 	 */
 
-	COMPOSITE(0, "aclk_vepu", mux_pll_src_cpll_gpll_usb_p, 0,
+	COMPOSITE(0, "aclk_vepu", mux_pll_src_cpll_gpll_npll_usb_p, 0,
 			RK3368_CLKSEL_CON(15), 6, 2, MFLAGS, 0, 5, DFLAGS,
 			RK3368_CLKGATE_CON(4), 6, GFLAGS),
-	COMPOSITE(0, "aclk_vdpu", mux_pll_src_cpll_gpll_usb_p, 0,
+	COMPOSITE(0, "aclk_vdpu", mux_pll_src_cpll_gpll_npll_usb_p, 0,
 			RK3368_CLKSEL_CON(15), 14, 2, MFLAGS, 8, 5, DFLAGS,
 			RK3368_CLKGATE_CON(4), 7, GFLAGS),
 
-- 
2.6.4



From heiko at sntech.de  Wed Jan 20 13:49:20 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Wed, 20 Jan 2016 22:49:20 +0100
Subject: [PATCH 4/4] clk: rockchip: rk3368: fix hdmi_cec gate-register
In-Reply-To: <1453326560-20475-1-git-send-email-heiko@sntech.de>
References: <1453326560-20475-1-git-send-email-heiko@sntech.de>
Message-ID: <1453326560-20475-4-git-send-email-heiko@sntech.de>

Fix a typo making the sclk_hdmi_cec access a wrong register to handle
its gate.

Fixes: 3536c97a52db ("clk: rockchip: add rk3368 clock controller")
Signed-off-by: Heiko Stuebner <heiko at sntech.de>
---
 drivers/clk/rockchip/clk-rk3368.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/clk/rockchip/clk-rk3368.c b/drivers/clk/rockchip/clk-rk3368.c
index 6037beb..57acb62 100644
--- a/drivers/clk/rockchip/clk-rk3368.c
+++ b/drivers/clk/rockchip/clk-rk3368.c
@@ -442,7 +442,7 @@ static struct rockchip_clk_branch rk3368_clk_branches[] __initdata = {
 	GATE(SCLK_HDMI_HDCP, "sclk_hdmi_hdcp", "xin24m", 0,
 			RK3368_CLKGATE_CON(4), 13, GFLAGS),
 	GATE(SCLK_HDMI_CEC, "sclk_hdmi_cec", "xin32k", 0,
-			RK3368_CLKGATE_CON(5), 12, GFLAGS),
+			RK3368_CLKGATE_CON(4), 12, GFLAGS),
 
 	COMPOSITE_NODIV(0, "vip_src", mux_pll_src_cpll_gpll_p, 0,
 			RK3368_CLKSEL_CON(21), 15, 1, MFLAGS,
-- 
2.6.4



From heiko at sntech.de  Wed Jan 20 13:49:17 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Wed, 20 Jan 2016 22:49:17 +0100
Subject: [PATCH 1/4] clk: rockchip: fix cpuclk mux bit of big cpu-cluster
Message-ID: <1453326560-20475-1-git-send-email-heiko@sntech.de>

Both clusters have their mux bit in bit 7 of their respective register.
For whatever reason the big cluster currently lists bit 15 which is
definitly wrong.

Fixes: 3536c97a52db ("clk: rockchip: add rk3368 clock controller")
Reported-by: Zhang Qing <zhangqing at rock-chips.com>
Signed-off-by: Heiko Stuebner <heiko at sntech.de>
---
I plan to include them into my clk-fixes branch, so posted for reference
and possible objections ;-)

 drivers/clk/rockchip/clk-rk3368.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/clk/rockchip/clk-rk3368.c b/drivers/clk/rockchip/clk-rk3368.c
index 21f3ea9..f6667b8 100644
--- a/drivers/clk/rockchip/clk-rk3368.c
+++ b/drivers/clk/rockchip/clk-rk3368.c
@@ -165,7 +165,7 @@ static const struct rockchip_cpuclk_reg_data rk3368_cpuclkb_data = {
 	.core_reg = RK3368_CLKSEL_CON(0),
 	.div_core_shift = 0,
 	.div_core_mask = 0x1f,
-	.mux_core_shift = 15,
+	.mux_core_shift = 7,
 };
 
 static const struct rockchip_cpuclk_reg_data rk3368_cpuclkl_data = {
-- 
2.6.4



From heiko at sntech.de  Wed Jan 20 13:49:18 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Wed, 20 Jan 2016 22:49:18 +0100
Subject: [PATCH 2/4] clk: rockchip: fix rk3368 cpuclk core dividers
In-Reply-To: <1453326560-20475-1-git-send-email-heiko@sntech.de>
References: <1453326560-20475-1-git-send-email-heiko@sntech.de>
Message-ID: <1453326560-20475-2-git-send-email-heiko@sntech.de>

Similar to commit 9880d4277f6a ("clk: rockchip: fix rk3288 cpuclk core
dividers") it seems the cpuclk dividers are one to high on the rk3368
as well.

And again similar to the previous fix, we opt to make the divider list
contain the values to be written to use the same paradigm for them on all
supported socs.

Fixes: 3536c97a52db ("clk: rockchip: add rk3368 clock controller")
Reported-by: Zhang Qing <zhangqing at rock-chips.com>
Signed-off-by: Heiko Stuebner <heiko at sntech.de>
---
 drivers/clk/rockchip/clk-rk3368.c | 40 +++++++++++++++++++--------------------
 1 file changed, 20 insertions(+), 20 deletions(-)

diff --git a/drivers/clk/rockchip/clk-rk3368.c b/drivers/clk/rockchip/clk-rk3368.c
index f6667b8..3c9733e 100644
--- a/drivers/clk/rockchip/clk-rk3368.c
+++ b/drivers/clk/rockchip/clk-rk3368.c
@@ -218,29 +218,29 @@ static const struct rockchip_cpuclk_reg_data rk3368_cpuclkl_data = {
 	}
 
 static struct rockchip_cpuclk_rate_table rk3368_cpuclkb_rates[] __initdata = {
-	RK3368_CPUCLKB_RATE(1512000000, 2, 6, 6),
-	RK3368_CPUCLKB_RATE(1488000000, 2, 5, 5),
-	RK3368_CPUCLKB_RATE(1416000000, 2, 5, 5),
-	RK3368_CPUCLKB_RATE(1200000000, 2, 4, 4),
-	RK3368_CPUCLKB_RATE(1008000000, 2, 4, 4),
-	RK3368_CPUCLKB_RATE( 816000000, 2, 3, 3),
-	RK3368_CPUCLKB_RATE( 696000000, 2, 3, 3),
-	RK3368_CPUCLKB_RATE( 600000000, 2, 2, 2),
-	RK3368_CPUCLKB_RATE( 408000000, 2, 2, 2),
-	RK3368_CPUCLKB_RATE( 312000000, 2, 2, 2),
+	RK3368_CPUCLKB_RATE(1512000000, 1, 5, 5),
+	RK3368_CPUCLKB_RATE(1488000000, 1, 4, 4),
+	RK3368_CPUCLKB_RATE(1416000000, 1, 4, 4),
+	RK3368_CPUCLKB_RATE(1200000000, 1, 3, 3),
+	RK3368_CPUCLKB_RATE(1008000000, 1, 3, 3),
+	RK3368_CPUCLKB_RATE( 816000000, 1, 2, 2),
+	RK3368_CPUCLKB_RATE( 696000000, 1, 2, 2),
+	RK3368_CPUCLKB_RATE( 600000000, 1, 1, 1),
+	RK3368_CPUCLKB_RATE( 408000000, 1, 1, 1),
+	RK3368_CPUCLKB_RATE( 312000000, 1, 1, 1),
 };
 
 static struct rockchip_cpuclk_rate_table rk3368_cpuclkl_rates[] __initdata = {
-	RK3368_CPUCLKL_RATE(1512000000, 2, 7, 7),
-	RK3368_CPUCLKL_RATE(1488000000, 2, 6, 6),
-	RK3368_CPUCLKL_RATE(1416000000, 2, 6, 6),
-	RK3368_CPUCLKL_RATE(1200000000, 2, 5, 5),
-	RK3368_CPUCLKL_RATE(1008000000, 2, 5, 5),
-	RK3368_CPUCLKL_RATE( 816000000, 2, 4, 4),
-	RK3368_CPUCLKL_RATE( 696000000, 2, 3, 3),
-	RK3368_CPUCLKL_RATE( 600000000, 2, 3, 3),
-	RK3368_CPUCLKL_RATE( 408000000, 2, 2, 2),
-	RK3368_CPUCLKL_RATE( 312000000, 2, 2, 2),
+	RK3368_CPUCLKL_RATE(1512000000, 1, 6, 6),
+	RK3368_CPUCLKL_RATE(1488000000, 1, 5, 5),
+	RK3368_CPUCLKL_RATE(1416000000, 1, 5, 5),
+	RK3368_CPUCLKL_RATE(1200000000, 1, 4, 4),
+	RK3368_CPUCLKL_RATE(1008000000, 1, 4, 4),
+	RK3368_CPUCLKL_RATE( 816000000, 1, 3, 3),
+	RK3368_CPUCLKL_RATE( 696000000, 1, 2, 2),
+	RK3368_CPUCLKL_RATE( 600000000, 1, 2, 2),
+	RK3368_CPUCLKL_RATE( 408000000, 1, 1, 1),
+	RK3368_CPUCLKL_RATE( 312000000, 1, 1, 1),
 };
 
 static struct rockchip_clk_branch rk3368_clk_branches[] __initdata = {
-- 
2.6.4



From sboyd at codeaurora.org  Wed Jan 20 14:06:27 2016
From: sboyd at codeaurora.org (Stephen Boyd)
Date: Wed, 20 Jan 2016 14:06:27 -0800
Subject: [PATCH 1/4] clk: rockchip: fix cpuclk mux bit of big cpu-cluster
In-Reply-To: <1453326560-20475-1-git-send-email-heiko@sntech.de>
References: <1453326560-20475-1-git-send-email-heiko@sntech.de>
Message-ID: <56A004E3.5000309@codeaurora.org>

On 01/20/2016 01:49 PM, Heiko Stuebner wrote:
> Both clusters have their mux bit in bit 7 of their respective register.
> For whatever reason the big cluster currently lists bit 15 which is
> definitly wrong.
>
> Fixes: 3536c97a52db ("clk: rockchip: add rk3368 clock controller")
> Reported-by: Zhang Qing <zhangqing at rock-chips.com>
> Signed-off-by: Heiko Stuebner <heiko at sntech.de>
> ---
> I plan to include them into my clk-fixes branch, so posted for reference
> and possible objections ;-)

None of these patches are fixes to regressions introduced in the merge
window for v4.5, so we wouldn't be considering them for clk-fixes. We
can certainly queue them up in clk-next for v4.6 and let stable process
funnel them to the right stable trees though.

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



From sboyd at codeaurora.org  Wed Jan 20 14:16:56 2016
From: sboyd at codeaurora.org (Stephen Boyd)
Date: Wed, 20 Jan 2016 14:16:56 -0800
Subject: [PATCH v2] clk-divider: make sure read-only dividers do not
 write to their register
In-Reply-To: <3578615.iBrKplh45Q@phil>
References: <8710207.gKyLbM2bKY@phil>
 <3578615.iBrKplh45Q@phil>
Message-ID: <20160120221604.GC12841@codeaurora.org>

On 01/20, Heiko Stuebner wrote:
> Commit e6d5e7d90be9 ("clk-divider: Fix READ_ONLY when divider > 1") removed
> the special ops struct for read-only clocks and instead opted to handle
> them regularly.
> 
> On the rk3368 this results in breakage as aclkm now gets set a value.
> While it is the same divider value, the A53 core still doesn't like it,
> which can result in the cpu ending up in a hang.
> The reason being that "ACLKENMasserts one clock cycle before the rising
> edge of ACLKM" and the clock should only be touched when STANDBYWFIL2
> is asserted.
> 
> So make sure read-only clocks don't touch the clock-register at all
> even if only writing the same value, as even writing the same value
> may not be safe in all cases.
> 
> Fixes: e6d5e7d90be9 ("clk-divider: Fix READ_ONLY when divider > 1")
> Reported-by: Zhang Qing <zhangqing at rock-chips.com>
> Signed-off-by: Heiko Stuebner <heiko at sntech.de>
> Reviewed-by: James Hogan <james.hogan at imgtec.com>
> ---

Hmph, the same patch[1] has been sitting in my inbox for months
now but I never got a response to my last question and then
forgot about it. I'll repeat the question here and hopefully we
can finish the discussion.

Maybe it would make more sense to have different ops for read
only dividers? The clk_set_rate op would be empty, while we would
have proper recalc_rate and round_rate ops. We can't just flat
out revert commit e6d5e7d90be9 (clk-divider: Fix READ_ONLY when
divider > 1, 2014-11-14) because that will introduce the problem
that it was fixing, but as long as we implement round_rate in
addition to recalc_rate it should work.

[1] http://lkml.kernel.org/r/1428392806-14538-1-git-send-email-jy0922.shim at samsung.com

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


From heiko at sntech.de  Wed Jan 20 14:25:03 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Wed, 20 Jan 2016 23:25:03 +0100
Subject: [PATCH v7 5/5] ARM: dts: rockchip: add support mipi panel
 tv080wum-nl0
In-Reply-To: <569EDE2F.6060400@rock-chips.com>
References: <1452053038-32098-1-git-send-email-zyw@rock-chips.com>
 <1649978.zzK0Pex4sR@phil> <569EDE2F.6060400@rock-chips.com>
Message-ID: <4804270.fNNME6HN13@phil>

Am Mittwoch, 20. Januar 2016, 09:09:03 schrieb Chris Zhong:
> Hi Heiko
> 
> On 01/20/2016 03:26 AM, Heiko Stuebner wrote:
> > Hi Chris,
> > 
> > Am Mittwoch, 6. Januar 2016, 12:03:57 schrieb Chris Zhong:
> >> This tv080wum-nl0 panel is a mipi panel, it can use in MIPI_TX socket
> >> of rk3288 evb board.
> >> 
> >> Signed-off-by: Chris Zhong <zyw at rock-chips.com>
> > 
> > [...]
> > 
> >> diff --git a/arch/arm/boot/dts/rk3288-evb-act8846.dts
> >> b/arch/arm/boot/dts/rk3288-evb-act8846.dts index e662147..7d5475b
> >> 100644
> >> --- a/arch/arm/boot/dts/rk3288-evb-act8846.dts
> >> +++ b/arch/arm/boot/dts/rk3288-evb-act8846.dts
> >> @@ -50,6 +50,8 @@
> >> 
> >>   		gpio = <&gpio7 3 GPIO_ACTIVE_HIGH>;
> >>   		pinctrl-names = "default";
> >>   		pinctrl-0 = <&lcd_en>;
> >> 
> >> +		regulator-always-on;
> >> +		regulator-boot-on;
> > 
> > Is this only needed for the panel itself and can you try to get the
> > panel
> > supplies set up correctly instead in that case please?
> 
> It is not only for panel, LCDC_VDD need this supply too.
> And rk3288-evb-rk808.dts has set this regulator to always on, so I do
> the same thing in here.

ok, so we keep the regulator-always-on for now, but could probably still add 
the supply to the panel as well? That way once vcc_lcd handling is complete 
in the future, we don't get surprises :-)

I guess I can add that power-supply property myself when applying.


Heiko

> 
> >>   		regulator-name = "vcc_lcd";
> >>   		vin-supply = <&vcc_io>;
> >>   	
> >>   	};
> >> 
> >> diff --git a/arch/arm/boot/dts/rk3288-evb.dtsi
> >> b/arch/arm/boot/dts/rk3288-evb.dtsi index 4faabdb..0c91858 100644
> >> --- a/arch/arm/boot/dts/rk3288-evb.dtsi
> >> +++ b/arch/arm/boot/dts/rk3288-evb.dtsi
> >> @@ -47,7 +47,7 @@
> >> 
> >>   		reg = <0x0 0x80000000>;
> >>   	
> >>   	};
> >> 
> >> -	backlight {
> >> +	backlight: backlight {
> >> 
> >>   		compatible = "pwm-backlight";
> >>   		brightness-levels = <
> >>   		
> >>   			  0   1   2   3   4   5   6   7
> >> 
> >> @@ -183,6 +183,16 @@
> >> 
> >>   	status = "okay";
> >>   
> >>   };
> >> 
> >> +&mipi_dsi {
> >> +	status = "okay";
> >> +	panel {
> >> +		compatible ="boe,tv080wum-nl0";
> >> +		reg = <0>;
> >> +		backlight = <&backlight>;
> >> +		status = "okay";
> > 
> > in the evb-schematics I see, vcc_lcd as supply for the panel.
> > 
> > So I'd think the panel node here should have a
> > 
> > 	power-supply = <&vcc_lcd>;
> > 
> > instead of having the always-on above?
> > 
> > 
> > Thanks
> > Heiko
> > 
> >> +	};
> >> +};
> >> +
> >> 
> >>   &sdmmc {
> >>   
> >>   	bus-width = <4>;
> >>   	cap-mmc-highspeed;


From heiko at sntech.de  Wed Jan 20 14:25:57 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Wed, 20 Jan 2016 23:25:57 +0100
Subject: [PATCH 1/4] clk: rockchip: fix cpuclk mux bit of big cpu-cluster
In-Reply-To: <56A004E3.5000309@codeaurora.org>
References: <1453326560-20475-1-git-send-email-heiko@sntech.de>
 <56A004E3.5000309@codeaurora.org>
Message-ID: <2048998.GeQnePRN87@phil>

Am Mittwoch, 20. Januar 2016, 14:06:27 schrieb Stephen Boyd:
> On 01/20/2016 01:49 PM, Heiko Stuebner wrote:
> > Both clusters have their mux bit in bit 7 of their respective register.
> > For whatever reason the big cluster currently lists bit 15 which is
> > definitly wrong.
> > 
> > Fixes: 3536c97a52db ("clk: rockchip: add rk3368 clock controller")
> > Reported-by: Zhang Qing <zhangqing at rock-chips.com>
> > Signed-off-by: Heiko Stuebner <heiko at sntech.de>
> > ---
> > I plan to include them into my clk-fixes branch, so posted for reference
> > and possible objections ;-)
> 
> None of these patches are fixes to regressions introduced in the merge
> window for v4.5, so we wouldn't be considering them for clk-fixes. We
> can certainly queue them up in clk-next for v4.6 and let stable process
> funnel them to the right stable trees though.

ok, I'll move them over to that part then :-)


From heiko at sntech.de  Wed Jan 20 14:29:56 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Wed, 20 Jan 2016 23:29:56 +0100
Subject: [PATCH v2] clk-divider: make sure read-only dividers do not write
 to their register
In-Reply-To: <20160120221604.GC12841@codeaurora.org>
References: <8710207.gKyLbM2bKY@phil> <3578615.iBrKplh45Q@phil>
 <20160120221604.GC12841@codeaurora.org>
Message-ID: <2694181.qDMgtzpC9k@phil>

Am Mittwoch, 20. Januar 2016, 14:16:56 schrieb Stephen Boyd:
> On 01/20, Heiko Stuebner wrote:
> > Commit e6d5e7d90be9 ("clk-divider: Fix READ_ONLY when divider > 1")
> > removed the special ops struct for read-only clocks and instead opted
> > to handle them regularly.
> > 
> > On the rk3368 this results in breakage as aclkm now gets set a value.
> > While it is the same divider value, the A53 core still doesn't like it,
> > which can result in the cpu ending up in a hang.
> > The reason being that "ACLKENMasserts one clock cycle before the rising
> > edge of ACLKM" and the clock should only be touched when STANDBYWFIL2
> > is asserted.
> > 
> > So make sure read-only clocks don't touch the clock-register at all
> > even if only writing the same value, as even writing the same value
> > may not be safe in all cases.
> > 
> > Fixes: e6d5e7d90be9 ("clk-divider: Fix READ_ONLY when divider > 1")
> > Reported-by: Zhang Qing <zhangqing at rock-chips.com>
> > Signed-off-by: Heiko Stuebner <heiko at sntech.de>
> > Reviewed-by: James Hogan <james.hogan at imgtec.com>
> > ---
> 
> Hmph, the same patch[1] has been sitting in my inbox for months
> now but I never got a response to my last question and then
> forgot about it. I'll repeat the question here and hopefully we
> can finish the discussion.
> 
> Maybe it would make more sense to have different ops for read
> only dividers? The clk_set_rate op would be empty, while we would
> have proper recalc_rate and round_rate ops. We can't just flat
> out revert commit e6d5e7d90be9 (clk-divider: Fix READ_ONLY when
> divider > 1, 2014-11-14) because that will introduce the problem
> that it was fixing, but as long as we implement round_rate in
> addition to recalc_rate it should work.
> 
> [1]
> http://lkml.kernel.org/r/1428392806-14538-1-git-send-email-jy0922.shim at sa
> msung.com

Sorry for the second submission then. That really is from quite a while ago. 
Reintroducing a secondary ops-struct with a round-rate sounds fine for my 
side - I'll give that a shot tomorrow.


Heiko


From heiko at sntech.de  Wed Jan 20 15:08:16 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Thu, 21 Jan 2016 00:08:16 +0100
Subject: [PATCH] ARM64: dts: rockchip: Add GeekBox config
In-Reply-To: <1453307913-8907-1-git-send-email-afaerber@suse.de>
References: <1453307913-8907-1-git-send-email-afaerber@suse.de>
Message-ID: <6705185.M2rneQv8FF@phil>

Am Mittwoch, 20. Januar 2016, 17:38:33 schrieb Andreas F?rber:
> The GeekBox contains an MXM3 module with a Rockchip RK3368 SoC.
> Some connectors are available directly on the module.
> 
> This adds initial support, namely serial, PMIC and USB.
> 
> Signed-off-by: Andreas F?rber <afaerber at suse.de>
> ---
>  Hi Heiko,
> 
>  Here's an initial shot at a .dts for GeekBox.
>  GMAC was not yet working for me.
> 
>  Regards,
>  Andreas
> 
>  arch/arm64/boot/dts/rockchip/Makefile            |   1 +
>  arch/arm64/boot/dts/rockchip/rk3368-geekbox.dts  |  48 +++++
>  arch/arm64/boot/dts/rockchip/rk3368-geekbox.dtsi | 242
> +++++++++++++++++++++++ 3 files changed, 291 insertions(+)
>  create mode 100644 arch/arm64/boot/dts/rockchip/rk3368-geekbox.dts
>  create mode 100644 arch/arm64/boot/dts/rockchip/rk3368-geekbox.dtsi

You might want to explain your reasoning for having a dtsi and an empty 
standalone dts. I guess you want to add a second one for module+baseboard?

The interesting artifact here is, that the baseboard can actually be used 
standalone and the baseboard just extends it where in contrast a rk3288 
rock2 for example isn't actually usable without a baseboard.

So I'm wondering if this really should be encoded in the dts or should uses 
something like dts overlays instead to enable one geekbox module to be used 
alone and in the "landingship".


> 
> diff --git a/arch/arm64/boot/dts/rockchip/Makefile
> b/arch/arm64/boot/dts/rockchip/Makefile index e3f0b5f..df37865 100644
> --- a/arch/arm64/boot/dts/rockchip/Makefile
> +++ b/arch/arm64/boot/dts/rockchip/Makefile
> @@ -1,4 +1,5 @@
>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-evb-act8846.dtb
> +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-geekbox.dtb
>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-r88.dtb
> 
>  always		:= $(dtb-y)
> diff --git a/arch/arm64/boot/dts/rockchip/rk3368-geekbox.dts
> b/arch/arm64/boot/dts/rockchip/rk3368-geekbox.dts new file mode 100644
> index 0000000..eb24ecd
> --- /dev/null
> +++ b/arch/arm64/boot/dts/rockchip/rk3368-geekbox.dts
> @@ -0,0 +1,48 @@
> +/*
> + * Copyright (c) 2016 Andreas F?rber
> + *
> + * This file is dual-licensed: you can use it either under the terms
> + * of the GPL or the X11 license, at your option. Note that this dual
> + * licensing only applies to this file, and not this project as a
> + * whole.
> + *
> + *  a) This file is free software; you can redistribute it and/or
> + *     modify it under the terms of the GNU General Public License as
> + *     published by the Free Software Foundation; either version 2 of the
> + *     License, or (at your option) any later version.
> + *
> + *     This file is distributed in the hope that it will be useful,
> + *     but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *     GNU General Public License for more details.
> + *
> + * Or, alternatively,
> + *
> + *  b) Permission is hereby granted, free of charge, to any person
> + *     obtaining a copy of this software and associated documentation
> + *     files (the "Software"), to deal in the Software without
> + *     restriction, including without limitation the rights to use,
> + *     copy, modify, merge, publish, distribute, sublicense, and/or
> + *     sell copies of the Software, and to permit persons to whom the
> + *     Software is furnished to do so, subject to the following
> + *     conditions:
> + *
> + *     The above copyright notice and this permission notice shall be
> + *     included in all copies or substantial portions of the Software.
> + *
> + *     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.
> + */
> +
> +/dts-v1/;
> +#include "rk3368-geekbox.dtsi"
> +
> +/ {
> +	model = "GeekBox";
> +};
> diff --git a/arch/arm64/boot/dts/rockchip/rk3368-geekbox.dtsi
> b/arch/arm64/boot/dts/rockchip/rk3368-geekbox.dtsi new file mode 100644
> index 0000000..f083cdb
> --- /dev/null
> +++ b/arch/arm64/boot/dts/rockchip/rk3368-geekbox.dtsi
> @@ -0,0 +1,242 @@
> +/*
> + * Copyright (c) 2016 Andreas F?rber
> + *
> + * This file is dual-licensed: you can use it either under the terms
> + * of the GPL or the X11 license, at your option. Note that this dual
> + * licensing only applies to this file, and not this project as a
> + * whole.
> + *
> + *  a) This file is free software; you can redistribute it and/or
> + *     modify it under the terms of the GNU General Public License as
> + *     published by the Free Software Foundation; either version 2 of the
> + *     License, or (at your option) any later version.
> + *
> + *     This file is distributed in the hope that it will be useful,
> + *     but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *     GNU General Public License for more details.
> + *
> + * Or, alternatively,
> + *
> + *  b) Permission is hereby granted, free of charge, to any person
> + *     obtaining a copy of this software and associated documentation
> + *     files (the "Software"), to deal in the Software without
> + *     restriction, including without limitation the rights to use,
> + *     copy, modify, merge, publish, distribute, sublicense, and/or
> + *     sell copies of the Software, and to permit persons to whom the
> + *     Software is furnished to do so, subject to the following
> + *     conditions:
> + *
> + *     The above copyright notice and this permission notice shall be
> + *     included in all copies or substantial portions of the Software.
> + *
> + *     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.
> + */
> +
> +#include "rk3368.dtsi"
> +
> +/ {
> +	compatible = "geekbuying,geekbox", "rockchip,rk3368";

you're introducing
- a new vendor name (geekbuying), so should update 
Documentation/devicetree/bindings/vendor-prefixes.txt in a separate patch
- a new board, so should update 
Documentation/devicetree/bindings/arm/rockchip.txt


> +
> +	chosen {
> +		stdout-path = "serial2:115200n8";
> +	};
> +
> +	memory {
> +		device_type = "memory";
> +		reg = <0x0 0x0 0x0 0x80000000>;
> +	};
> +
> +	leds: gpio-leds {
> +		compatible = "gpio-leds";
> +
> +		blue {
> +			gpios = <&gpio2 2 GPIO_ACTIVE_HIGH>;
> +			label = "geekbox:blue:led";
> +			default-state = "on";
> +		};
> +
> +		red {
> +			gpios = <&gpio2 3 GPIO_ACTIVE_HIGH>;
> +			label = "geekbox:red:led";
> +			default-state = "off";
> +		};
> +	};
> +
> +	vcc_sys: vcc-sys-regulator {
> +		compatible = "regulator-fixed";
> +		regulator-name = "vcc_sys";
> +		regulator-min-microvolt = <5000000>;
> +		regulator-max-microvolt = <5000000>;
> +		regulator-always-on;
> +		regulator-boot-on;
> +	};
> +};
> +
> +&i2c0 {
> +	status = "okay";
> +
> +	rk808: pmic at 1b {
> +		compatible = "rockchip,rk808";
> +		reg = <0x1b>;
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&pmic_int>, <&pmic_sleep>;
> +		interrupt-parent = <&gpio0>;
> +		interrupts = <5 IRQ_TYPE_LEVEL_LOW>;
> +		rockchip,system-power-controller;
> +		vcc1-supply = <&vcc_sys>;
> +		vcc2-supply = <&vcc_sys>;
> +		vcc3-supply = <&vcc_sys>;
> +		vcc4-supply = <&vcc_sys>;
> +		vcc6-supply = <&vcc_sys>;
> +		vcc7-supply = <&vcc_sys>;
> +		vcc8-supply = <&vcc_io>;
> +		vcc9-supply = <&vcc_sys>;
> +		vcc10-supply = <&vcc_sys>;
> +		vcc11-supply = <&vcc_sys>;
> +		vcc12-supply = <&vcc_io>;
> +		clock-output-names = "xin32k", "rk808-clkout2";
> +		#clock-cells = <1>;
> +
> +		regulators {
> +			vdd_cpu: DCDC_REG1 {
> +				regulator-always-on;
> +				regulator-boot-on;
> +				regulator-min-microvolt = <700000>;
> +				regulator-max-microvolt = <1500000>;
> +				regulator-name = "vdd_cpu";
> +			};
> +
> +			vdd_log: DCDC_REG2 {
> +				regulator-always-on;
> +				regulator-boot-on;
> +				regulator-min-microvolt = <700000>;
> +				regulator-max-microvolt = <1500000>;
> +				regulator-name = "vdd_log";
> +			};
> +
> +			vcc_ddr: DCDC_REG3 {
> +				regulator-always-on;
> +				regulator-boot-on;
> +				regulator-name = "vcc_ddr";
> +			};
> +
> +			vcc_io: DCDC_REG4 {
> +				regulator-always-on;
> +				regulator-boot-on;
> +				regulator-min-microvolt = <3300000>;
> +				regulator-max-microvolt = <3300000>;
> +				regulator-name = "vcc_io";
> +			};
> +
> +			vcc18_flash: LDO_REG1 {
> +				regulator-always-on;
> +				regulator-boot-on;
> +				regulator-min-microvolt = <1800000>;
> +				regulator-max-microvolt = <1800000>;
> +				regulator-name = "vcc18_flash";
> +			};
> +
> +			vcc33_lcd: LDO_REG2 {
> +				regulator-always-on;
> +				regulator-boot-on;
> +				regulator-min-microvolt = <3300000>;
> +				regulator-max-microvolt = <3300000>;
> +				regulator-name = "vcc33_lcd";
> +			};
> +
> +			vdd_10: LDO_REG3 {
> +				regulator-always-on;

why all others also "boot-on" and this one not?


Otherwise that looks fine

Heiko


From heiko at sntech.de  Wed Jan 20 15:14:25 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Thu, 21 Jan 2016 00:14:25 +0100
Subject: [patch] drm: rockchip: signedness bug in
 dw_mipi_dsi_get_lane_bps()
In-Reply-To: <20160120095352.GA19898@mwanda>
References: <20160120095352.GA19898@mwanda>
Message-ID: <181674264.ynnWZU1Yz8@phil>

Hi Dan,

Am Mittwoch, 20. Januar 2016, 12:53:52 schrieb Dan Carpenter:
> "bpp" needs to be signed for the error handling to work.
> 
> Fixes: 84e05408fcfe ('drm: rockchip: Support Synopsys DW MIPI DSI')
> Signed-off-by: Dan Carpenter <dan.carpenter at oracle.com>

There's also "drm/rockchip/dsi: fix handling mipi_dsi_pixel_format_to_bpp 
result" from Andrzej Hajda from january the 14th doing the same change.


Heiko

> 
> diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
> b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c index 7bfe243..c35e5af 100644
> --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
> +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
> @@ -461,7 +461,8 @@ static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi
> *dsi)
> 
>  static int dw_mipi_dsi_get_lane_bps(struct dw_mipi_dsi *dsi)
>  {
> -	unsigned int bpp, i, pre;
> +	int bpp;
> +	unsigned int i, pre;
>  	unsigned long mpclk, pllref, tmp;
>  	unsigned int m = 1, n = 1, target_mbps = 1000;
>  	unsigned int max_mbps = dptdin_map[ARRAY_SIZE(dptdin_map) - 
1].max_mbps;



From heiko at sntech.de  Wed Jan 20 15:38:30 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Thu, 21 Jan 2016 00:38:30 +0100
Subject: [PATCH v2] clk: rockchip: Add support for multiple clock providers
In-Reply-To: <1453280809-3120-1-git-send-email-zhengxing@rock-chips.com>
References: <1453280809-3120-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <3395528.oy9qVuyESf@phil>

Hi,

Am Mittwoch, 20. Januar 2016, 17:06:49 schrieb Xing Zheng:
> There are need to support Multi-CRUs probability in future, but
> it is not supported on the current Rockchip Clock Framework.
> 
> Therefore, this patch add support a provider as the parameter
> handler when we call the clock register functions for per CRU.
> 
> Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>

overall this looks really nice. Thanks for following up on our talk so 
quickly :-) .


> ---
> 
> Changes in v2:
> - Fix missed to call rockchip_clk_common_cru_init when SoCs clock init.
> 
>  drivers/clk/rockchip/clk-rk3036.c |   15 +++--
>  drivers/clk/rockchip/clk-rk3188.c |   40 ++++++++-----
>  drivers/clk/rockchip/clk-rk3228.c |   15 +++--
>  drivers/clk/rockchip/clk-rk3288.c |   17 ++++--
>  drivers/clk/rockchip/clk-rk3368.c |   19 +++---
>  drivers/clk/rockchip/clk.c        |  120
> +++++++++++++++++++++++-------------- drivers/clk/rockchip/clk.h        |
>   35 ++++++++---
>  7 files changed, 170 insertions(+), 91 deletions(-)
> 
> diff --git a/drivers/clk/rockchip/clk-rk3036.c
> b/drivers/clk/rockchip/clk-rk3036.c index 483913b..050ad13 100644
> --- a/drivers/clk/rockchip/clk-rk3036.c
> +++ b/drivers/clk/rockchip/clk-rk3036.c
> @@ -434,6 +434,7 @@ static const char *const rk3036_critical_clocks[]
> __initconst = {
> 
>  static void __init rk3036_clk_init(struct device_node *np)
>  {
> +	struct rockchip_clk_provider *ctx;
>  	void __iomem *reg_base;
>  	struct clk *clk;
> 
> @@ -443,7 +444,9 @@ static void __init rk3036_clk_init(struct device_node
> *np) return;
>  	}
> 
> -	rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
> +	ctx = rockchip_clk_init(reg_base, CLK_NR_CLKS);
> +
> +	rockchip_clk_common_cru_init(np);
> 
>  	/* xin12m is created by an cru-internal divider */
>  	clk = clk_register_fixed_factor(NULL, "xin12m", "xin24m", 0, 1, 2);
> @@ -473,15 +476,15 @@ static void __init rk3036_clk_init(struct
> device_node *np) pr_warn("%s: could not register clock sclk_macref_out:
> %ld\n", __func__, PTR_ERR(clk));
> 
> -	rockchip_clk_register_plls(rk3036_pll_clks,
> +	rockchip_clk_register_plls(ctx, rk3036_pll_clks,
>  				   ARRAY_SIZE(rk3036_pll_clks),
>  				   RK3036_GRF_SOC_STATUS0);
> -	rockchip_clk_register_branches(rk3036_clk_branches,
> +	rockchip_clk_register_branches(ctx, rk3036_clk_branches,
>  				  ARRAY_SIZE(rk3036_clk_branches));
>  	rockchip_clk_protect_critical(rk3036_critical_clocks,
>  				      ARRAY_SIZE(rk3036_critical_clocks));
> 
> -	rockchip_clk_register_armclk(ARMCLK, "armclk",
> +	rockchip_clk_register_armclk(ctx, ARMCLK, "armclk",
>  			mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
>  			&rk3036_cpuclk_data, rk3036_cpuclk_rates,
>  			ARRAY_SIZE(rk3036_cpuclk_rates));
> @@ -489,6 +492,8 @@ static void __init rk3036_clk_init(struct device_node
> *np) rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0),
> ROCKCHIP_SOFTRST_HIWORD_MASK);
> 
> -	rockchip_register_restart_notifier(RK2928_GLB_SRST_FST, NULL);
> +	rockchip_register_restart_notifier(ctx, RK2928_GLB_SRST_FST, NULL);
> +
> +	rockchip_clk_of_add_provider(np, ctx);
>  }
>  CLK_OF_DECLARE(rk3036_cru, "rockchip,rk3036-cru", rk3036_clk_init);
> diff --git a/drivers/clk/rockchip/clk-rk3188.c
> b/drivers/clk/rockchip/clk-rk3188.c index 7f7444c..338a22e 100644
> --- a/drivers/clk/rockchip/clk-rk3188.c
> +++ b/drivers/clk/rockchip/clk-rk3188.c
> @@ -750,18 +750,19 @@ static const char *const rk3188_critical_clocks[]
> __initconst = { "pclk_peri",
>  };
> 
> -static void __init rk3188_common_clk_init(struct device_node *np)
> +static struct rockchip_clk_provider *__init rk3188_common_clk_init(struct
> device_node *np) {
> +	struct rockchip_clk_provider *ctx;
>  	void __iomem *reg_base;
>  	struct clk *clk;
> 
>  	reg_base = of_iomap(np, 0);
> -	if (!reg_base) {
> -		pr_err("%s: could not map cru region\n", __func__);
> -		return;
> -	}
> +	if (!reg_base)
> +		panic("%s: could not map cru region\n", __func__);

I don't believe in doing panics everywhere :-), please do
	return ERR_PTR(-ENOMEM);
here and in similar locations and let the top-most caller handle errors.



> @@ -260,27 +261,49 @@ static struct clk
> *rockchip_clk_register_frac_branch(const char *name, return clk;
>  }
> 
> -static DEFINE_SPINLOCK(clk_lock);
> -static struct clk **clk_table;
> -static void __iomem *reg_base;
> -static struct clk_onecell_data clk_data;
>  static struct device_node *cru_node;

please also include the devicetree node into the ctx struct. That way we can 
keep track of all of them (when there are multiple).


>  static struct regmap *grf;
> 
> -void __init rockchip_clk_init(struct device_node *np, void __iomem *base,
> -			      unsigned long nr_clks)
> +struct rockchip_clk_provider *__init rockchip_clk_init(
> +			void __iomem *base, unsigned long nr_clks)
>  {
> -	reg_base = base;
> -	cru_node = np;
> -	grf = ERR_PTR(-EPROBE_DEFER);
> +	struct rockchip_clk_provider *ctx;
> +	struct clk **clk_table;
> +	int i;
> +
> +	ctx = kzalloc(sizeof(struct rockchip_clk_provider), GFP_KERNEL);
> +	if (!ctx)
> +		panic("could not allocate clock provider context.\n");

as I wrote above, please return a real error here. Especially when there are 
multiple clock controllers, it might actually get a bit farther in cases 
when only one of them fails, which might help in debugging underlying issues 
in such cases.


> 
>  	clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL);
>  	if (!clk_table)
> -		pr_err("%s: could not allocate clock lookup table\n", __func__);
> +		panic("could not allocate clock lookup table\n");

same here (and kfree the ctx from above before returning)


> +
> +	for (i = 0; i < nr_clks; ++i)
> +		clk_table[i] = ERR_PTR(-ENOENT);
> +
> +	ctx->reg_base = base;
> +	ctx->clk_data.clks = clk_table;
> +	ctx->clk_data.clk_num = nr_clks;
> +	spin_lock_init(&ctx->lock);
> +
> +	return ctx;
> +}
> 
> -	clk_data.clks = clk_table;
> -	clk_data.clk_num = nr_clks;
> -	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
> +void __init rockchip_clk_common_cru_init(struct device_node *np)
> +{
> +	cru_node = np;
> +	grf = ERR_PTR(-EPROBE_DEFER);

I'm not sure why this is a separate function. Setting the node pointer (into 
the new struct as written above) can still happen in rockchip_clk_init, and 
initializing the global grf pointer as well.


Heiko


From mark.yao at rock-chips.com  Wed Jan 20 16:51:58 2016
From: mark.yao at rock-chips.com (Mark yao)
Date: Thu, 21 Jan 2016 08:51:58 +0800
Subject: [PATCH v2 0/3] drm/rockchip: fix cursor performance with atomic
In-Reply-To: <cover.1453200263.git.john@metanate.com>
References: <cover.1452782114.git.john@metanate.com>
 <cover.1453200263.git.john@metanate.com>
Message-ID: <56A02BAE.1050009@rock-chips.com>

On 2016?01?19? 18:46, John Keeping wrote:
> The first two patches are unchanged since v1 but the comment in the
> third has been expanded following Thierry's comments.
>
> John Keeping (3):
>    drm/atomic-helper: Export framebuffer_changed()
>    drm/rockchip: don't wait for vblank if fb hasn't changed
>    drm/rockchip: explain why we can't wait_for_vblanks
>
>   drivers/gpu/drm/drm_atomic_helper.c        | 24 ++++++++++++++++++++----
>   drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 23 +++++++++++++++++++++--
>   include/drm/drm_atomic_helper.h            |  4 ++++
>   3 files changed, 45 insertions(+), 6 deletions(-)
>

Hi John

Thanks for your fix, applied these three patches into my drm-next, :-)

-- 
?ark Yao




From mark.yao at rock-chips.com  Wed Jan 20 17:17:16 2016
From: mark.yao at rock-chips.com (Mark Yao)
Date: Thu, 21 Jan 2016 09:17:16 +0800
Subject: [PATCH] drm/rockchip: fix wrong pitch/size using on gem
Message-ID: <1453339036-22864-1-git-send-email-mark.yao@rock-chips.com>

args->pitch and args->size may not be set by userspace, sometimes
userspace only malloc args and not memset args to zero, then
args->pitch and args->size is random, it is very danger to use
pitch/size on gem.

pitch's type is u32, and min_pitch's type is int, example,
pitch is 0xffffffff, then pitch < min_pitch return true, then gem will
alloc very very big bufffer, it would eat all the memory and cause kernel
crash.

Stop using pitch/size from args, calc them from other args.

Signed-off-by: Mark Yao <mark.yao at rock-chips.com>
---
 drivers/gpu/drm/rockchip/rockchip_drm_gem.c |    9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
index d908321..18e0733 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
@@ -234,13 +234,8 @@ int rockchip_gem_dumb_create(struct drm_file *file_priv,
 	/*
 	 * align to 64 bytes since Mali requires it.
 	 */
-	min_pitch = ALIGN(min_pitch, 64);
-
-	if (args->pitch < min_pitch)
-		args->pitch = min_pitch;
-
-	if (args->size < args->pitch * args->height)
-		args->size = args->pitch * args->height;
+	args->pitch = ALIGN(min_pitch, 64);
+	args->size = args->pitch * args->height;
 
 	rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size,
 						 &args->handle);
-- 
1.7.9.5




From zhangqing at rock-chips.com  Thu Jan 21 01:42:42 2016
From: zhangqing at rock-chips.com (zhangqing)
Date: Thu, 21 Jan 2016 01:42:42 -0800
Subject: [PATCH 2/4] clk: rockchip: fix rk3368 cpuclk core dividers
In-Reply-To: <1453326560-20475-2-git-send-email-heiko@sntech.de>
References: <1453326560-20475-1-git-send-email-heiko@sntech.de>
 <1453326560-20475-2-git-send-email-heiko@sntech.de>
Message-ID: <56A0A812.5090803@rock-chips.com>

hi:

On 01/20/2016 01:49 PM, Heiko Stuebner wrote:
> Similar to commit 9880d4277f6a ("clk: rockchip: fix rk3288 cpuclk core
> dividers") it seems the cpuclk dividers are one to high on the rk3368
> as well.
>
> And again similar to the previous fix, we opt to make the divider list
> contain the values to be written to use the same paradigm for them on all
> supported socs.
>
> Fixes: 3536c97a52db ("clk: rockchip: add rk3368 clock controller")
> Reported-by: Zhang Qing <zhangqing at rock-chips.com>
> Signed-off-by: Heiko Stuebner <heiko at sntech.de>

Reviewed-by: zhangqing <zhangqing at rock-chips.com>

> ---
>   drivers/clk/rockchip/clk-rk3368.c | 40 +++++++++++++++++++--------------------
>   1 file changed, 20 insertions(+), 20 deletions(-)
>
> diff --git a/drivers/clk/rockchip/clk-rk3368.c b/drivers/clk/rockchip/clk-rk3368.c
> index f6667b8..3c9733e 100644
> --- a/drivers/clk/rockchip/clk-rk3368.c
> +++ b/drivers/clk/rockchip/clk-rk3368.c
> @@ -218,29 +218,29 @@ static const struct rockchip_cpuclk_reg_data rk3368_cpuclkl_data = {
>   	}
>
>   static struct rockchip_cpuclk_rate_table rk3368_cpuclkb_rates[] __initdata = {
> -	RK3368_CPUCLKB_RATE(1512000000, 2, 6, 6),
> -	RK3368_CPUCLKB_RATE(1488000000, 2, 5, 5),
> -	RK3368_CPUCLKB_RATE(1416000000, 2, 5, 5),
> -	RK3368_CPUCLKB_RATE(1200000000, 2, 4, 4),
> -	RK3368_CPUCLKB_RATE(1008000000, 2, 4, 4),
> -	RK3368_CPUCLKB_RATE( 816000000, 2, 3, 3),
> -	RK3368_CPUCLKB_RATE( 696000000, 2, 3, 3),
> -	RK3368_CPUCLKB_RATE( 600000000, 2, 2, 2),
> -	RK3368_CPUCLKB_RATE( 408000000, 2, 2, 2),
> -	RK3368_CPUCLKB_RATE( 312000000, 2, 2, 2),
> +	RK3368_CPUCLKB_RATE(1512000000, 1, 5, 5),
> +	RK3368_CPUCLKB_RATE(1488000000, 1, 4, 4),
> +	RK3368_CPUCLKB_RATE(1416000000, 1, 4, 4),
> +	RK3368_CPUCLKB_RATE(1200000000, 1, 3, 3),
> +	RK3368_CPUCLKB_RATE(1008000000, 1, 3, 3),
> +	RK3368_CPUCLKB_RATE( 816000000, 1, 2, 2),
> +	RK3368_CPUCLKB_RATE( 696000000, 1, 2, 2),
> +	RK3368_CPUCLKB_RATE( 600000000, 1, 1, 1),
> +	RK3368_CPUCLKB_RATE( 408000000, 1, 1, 1),
> +	RK3368_CPUCLKB_RATE( 312000000, 1, 1, 1),
>   };
>
>   static struct rockchip_cpuclk_rate_table rk3368_cpuclkl_rates[] __initdata = {
> -	RK3368_CPUCLKL_RATE(1512000000, 2, 7, 7),
> -	RK3368_CPUCLKL_RATE(1488000000, 2, 6, 6),
> -	RK3368_CPUCLKL_RATE(1416000000, 2, 6, 6),
> -	RK3368_CPUCLKL_RATE(1200000000, 2, 5, 5),
> -	RK3368_CPUCLKL_RATE(1008000000, 2, 5, 5),
> -	RK3368_CPUCLKL_RATE( 816000000, 2, 4, 4),
> -	RK3368_CPUCLKL_RATE( 696000000, 2, 3, 3),
> -	RK3368_CPUCLKL_RATE( 600000000, 2, 3, 3),
> -	RK3368_CPUCLKL_RATE( 408000000, 2, 2, 2),
> -	RK3368_CPUCLKL_RATE( 312000000, 2, 2, 2),
> +	RK3368_CPUCLKL_RATE(1512000000, 1, 6, 6),
> +	RK3368_CPUCLKL_RATE(1488000000, 1, 5, 5),
> +	RK3368_CPUCLKL_RATE(1416000000, 1, 5, 5),
> +	RK3368_CPUCLKL_RATE(1200000000, 1, 4, 4),
> +	RK3368_CPUCLKL_RATE(1008000000, 1, 4, 4),
> +	RK3368_CPUCLKL_RATE( 816000000, 1, 3, 3),
> +	RK3368_CPUCLKL_RATE( 696000000, 1, 2, 2),
> +	RK3368_CPUCLKL_RATE( 600000000, 1, 2, 2),
> +	RK3368_CPUCLKL_RATE( 408000000, 1, 1, 1),
> +	RK3368_CPUCLKL_RATE( 312000000, 1, 1, 1),
>   };
>
>   static struct rockchip_clk_branch rk3368_clk_branches[] __initdata = {
>



From zhangqing at rock-chips.com  Thu Jan 21 01:43:37 2016
From: zhangqing at rock-chips.com (zhangqing)
Date: Thu, 21 Jan 2016 01:43:37 -0800
Subject: [PATCH 3/4] clk: rockchip: rk3368: fix parents of video
 encoder/decoder
In-Reply-To: <1453326560-20475-3-git-send-email-heiko@sntech.de>
References: <1453326560-20475-1-git-send-email-heiko@sntech.de>
 <1453326560-20475-3-git-send-email-heiko@sntech.de>
Message-ID: <56A0A849.2040505@rock-chips.com>

hi:

On 01/20/2016 01:49 PM, Heiko Stuebner wrote:
> The vdpu and vepu clocks can also be parented to the npll and current
> parent list also is wrong as it would use the npll as "usbphy" source,
> so adapt the parent to the correct one.
>
> Fixes: 3536c97a52db ("clk: rockchip: add rk3368 clock controller")
> Signed-off-by: Heiko Stuebner <heiko at sntech.de>

Reviewed-by: zhangqing <zhangqing at rock-chips.com>

> ---
>   drivers/clk/rockchip/clk-rk3368.c | 4 ++--
>   1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/clk/rockchip/clk-rk3368.c b/drivers/clk/rockchip/clk-rk3368.c
> index 3c9733e..6037beb 100644
> --- a/drivers/clk/rockchip/clk-rk3368.c
> +++ b/drivers/clk/rockchip/clk-rk3368.c
> @@ -384,10 +384,10 @@ static struct rockchip_clk_branch rk3368_clk_branches[] __initdata = {
>   	 * Clock-Architecture Diagram 3
>   	 */
>
> -	COMPOSITE(0, "aclk_vepu", mux_pll_src_cpll_gpll_usb_p, 0,
> +	COMPOSITE(0, "aclk_vepu", mux_pll_src_cpll_gpll_npll_usb_p, 0,
>   			RK3368_CLKSEL_CON(15), 6, 2, MFLAGS, 0, 5, DFLAGS,
>   			RK3368_CLKGATE_CON(4), 6, GFLAGS),
> -	COMPOSITE(0, "aclk_vdpu", mux_pll_src_cpll_gpll_usb_p, 0,
> +	COMPOSITE(0, "aclk_vdpu", mux_pll_src_cpll_gpll_npll_usb_p, 0,
>   			RK3368_CLKSEL_CON(15), 14, 2, MFLAGS, 8, 5, DFLAGS,
>   			RK3368_CLKGATE_CON(4), 7, GFLAGS),
>
>



From zhangqing at rock-chips.com  Thu Jan 21 01:44:26 2016
From: zhangqing at rock-chips.com (zhangqing)
Date: Thu, 21 Jan 2016 01:44:26 -0800
Subject: [PATCH 4/4] clk: rockchip: rk3368: fix hdmi_cec gate-register
In-Reply-To: <1453326560-20475-4-git-send-email-heiko@sntech.de>
References: <1453326560-20475-1-git-send-email-heiko@sntech.de>
 <1453326560-20475-4-git-send-email-heiko@sntech.de>
Message-ID: <56A0A87A.10409@rock-chips.com>

hi:

On 01/20/2016 01:49 PM, Heiko Stuebner wrote:
> Fix a typo making the sclk_hdmi_cec access a wrong register to handle
> its gate.
>
> Fixes: 3536c97a52db ("clk: rockchip: add rk3368 clock controller")
> Signed-off-by: Heiko Stuebner <heiko at sntech.de>

Reviewed-by: zhangqing <zhangqing at rock-chips.com>

> ---
>   drivers/clk/rockchip/clk-rk3368.c | 2 +-
>   1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/clk/rockchip/clk-rk3368.c b/drivers/clk/rockchip/clk-rk3368.c
> index 6037beb..57acb62 100644
> --- a/drivers/clk/rockchip/clk-rk3368.c
> +++ b/drivers/clk/rockchip/clk-rk3368.c
> @@ -442,7 +442,7 @@ static struct rockchip_clk_branch rk3368_clk_branches[] __initdata = {
>   	GATE(SCLK_HDMI_HDCP, "sclk_hdmi_hdcp", "xin24m", 0,
>   			RK3368_CLKGATE_CON(4), 13, GFLAGS),
>   	GATE(SCLK_HDMI_CEC, "sclk_hdmi_cec", "xin32k", 0,
> -			RK3368_CLKGATE_CON(5), 12, GFLAGS),
> +			RK3368_CLKGATE_CON(4), 12, GFLAGS),
>
>   	COMPOSITE_NODIV(0, "vip_src", mux_pll_src_cpll_gpll_p, 0,
>   			RK3368_CLKSEL_CON(21), 15, 1, MFLAGS,
>



From zhangqing at rock-chips.com  Thu Jan 21 01:44:57 2016
From: zhangqing at rock-chips.com (zhangqing)
Date: Thu, 21 Jan 2016 01:44:57 -0800
Subject: [PATCH 1/4] clk: rockchip: fix cpuclk mux bit of big cpu-cluster
In-Reply-To: <1453326560-20475-1-git-send-email-heiko@sntech.de>
References: <1453326560-20475-1-git-send-email-heiko@sntech.de>
Message-ID: <56A0A899.9080908@rock-chips.com>

hi:

On 01/20/2016 01:49 PM, Heiko Stuebner wrote:
> Both clusters have their mux bit in bit 7 of their respective register.
> For whatever reason the big cluster currently lists bit 15 which is
> definitly wrong.
>
> Fixes: 3536c97a52db ("clk: rockchip: add rk3368 clock controller")
> Reported-by: Zhang Qing <zhangqing at rock-chips.com>
> Signed-off-by: Heiko Stuebner <heiko at sntech.de>

Reviewed-by: zhangqing <zhangqing at rock-chips.com>

> ---
> I plan to include them into my clk-fixes branch, so posted for reference
> and possible objections ;-)
>
>   drivers/clk/rockchip/clk-rk3368.c | 2 +-
>   1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/clk/rockchip/clk-rk3368.c b/drivers/clk/rockchip/clk-rk3368.c
> index 21f3ea9..f6667b8 100644
> --- a/drivers/clk/rockchip/clk-rk3368.c
> +++ b/drivers/clk/rockchip/clk-rk3368.c
> @@ -165,7 +165,7 @@ static const struct rockchip_cpuclk_reg_data rk3368_cpuclkb_data = {
>   	.core_reg = RK3368_CLKSEL_CON(0),
>   	.div_core_shift = 0,
>   	.div_core_mask = 0x1f,
> -	.mux_core_shift = 15,
> +	.mux_core_shift = 7,
>   };
>
>   static const struct rockchip_cpuclk_reg_data rk3368_cpuclkl_data = {
>



From afaerber at suse.de  Wed Jan 20 18:27:10 2016
From: afaerber at suse.de (=?UTF-8?Q?Andreas_F=c3=a4rber?=)
Date: Thu, 21 Jan 2016 03:27:10 +0100
Subject: [PATCH] ARM64: dts: rockchip: Add GeekBox config
In-Reply-To: <6705185.M2rneQv8FF@phil>
References: <1453307913-8907-1-git-send-email-afaerber@suse.de>
 <6705185.M2rneQv8FF@phil>
Message-ID: <56A041FE.7000104@suse.de>

Am 21.01.2016 um 00:08 schrieb Heiko Stuebner:
> Am Mittwoch, 20. Januar 2016, 17:38:33 schrieb Andreas F?rber:
>> The GeekBox contains an MXM3 module with a Rockchip RK3368 SoC.
>> Some connectors are available directly on the module.
>>
>> This adds initial support, namely serial, PMIC and USB.
>>
>> Signed-off-by: Andreas F?rber <afaerber at suse.de>
>> ---
>>  Hi Heiko,
>>
>>  Here's an initial shot at a .dts for GeekBox.
>>  GMAC was not yet working for me.
>>
>>  Regards,
>>  Andreas
>>
>>  arch/arm64/boot/dts/rockchip/Makefile            |   1 +
>>  arch/arm64/boot/dts/rockchip/rk3368-geekbox.dts  |  48 +++++
>>  arch/arm64/boot/dts/rockchip/rk3368-geekbox.dtsi | 242
>> +++++++++++++++++++++++ 3 files changed, 291 insertions(+)
>>  create mode 100644 arch/arm64/boot/dts/rockchip/rk3368-geekbox.dts
>>  create mode 100644 arch/arm64/boot/dts/rockchip/rk3368-geekbox.dtsi
> 
> You might want to explain your reasoning for having a dtsi and an empty 
> standalone dts. I guess you want to add a second one for module+baseboard?

More generally, as mentioned in the commit message, it's a module, so
automatically I did a .dtsi to allow for easy reuse.

At LinuxCon JP I had discussed with Grant Likely about whether it's okay
to #include a .dts rather than .dtsi and don't remember getting a clear
answer as we ended up drawing a line between hardware and FPGA.

And yes, I may later add a .dts for the Landingship - it would mainly
enable more nodes, I envision. But I assume that we're not bound to just
the Landingship with such a standard connector.

For now I'm more interested in incrementally adding further nodes to the
module's own file.

> The interesting artifact here is, that the baseboard can actually be used 
> standalone and the baseboard just extends it where in contrast a rk3288 
> rock2 for example isn't actually usable without a baseboard.
> 
> So I'm wondering if this really should be encoded in the dts or should uses 
> something like dts overlays instead to enable one geekbox module to be used 
> alone and in the "landingship".

Well, with the Parallella I made the experience that I used a single
.dts initially, and it turned out to be a bad choice when we tried
adding stuff to the DT while new hardware models surfaced - we still
haven't managed to split it up and it's stuck in limbo without USB.
I'd really like to avoid that going forward, so I'm trying to look
forward here in keeping our options open with a clean design.

How would your overlay get loaded? Wouldn't that need some special
kernel driver like for the BeagleBone? Not so attractive for a single
optional baseboard I care about.

>> diff --git a/arch/arm64/boot/dts/rockchip/Makefile
>> b/arch/arm64/boot/dts/rockchip/Makefile index e3f0b5f..df37865 100644
>> --- a/arch/arm64/boot/dts/rockchip/Makefile
>> +++ b/arch/arm64/boot/dts/rockchip/Makefile
>> @@ -1,4 +1,5 @@
>>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-evb-act8846.dtb
>> +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-geekbox.dtb
>>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-r88.dtb
>>
>>  always		:= $(dtb-y)
>> diff --git a/arch/arm64/boot/dts/rockchip/rk3368-geekbox.dts
>> b/arch/arm64/boot/dts/rockchip/rk3368-geekbox.dts new file mode 100644
>> index 0000000..eb24ecd
>> --- /dev/null
>> +++ b/arch/arm64/boot/dts/rockchip/rk3368-geekbox.dts
>> @@ -0,0 +1,48 @@
>> +/*
>> + * Copyright (c) 2016 Andreas F?rber
>> + *
>> + * This file is dual-licensed: you can use it either under the terms
>> + * of the GPL or the X11 license, at your option. Note that this dual
>> + * licensing only applies to this file, and not this project as a
>> + * whole.
>> + *
>> + *  a) This file is free software; you can redistribute it and/or
>> + *     modify it under the terms of the GNU General Public License as
>> + *     published by the Free Software Foundation; either version 2 of the
>> + *     License, or (at your option) any later version.
>> + *
>> + *     This file is distributed in the hope that it will be useful,
>> + *     but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + *     GNU General Public License for more details.
>> + *
>> + * Or, alternatively,
>> + *
>> + *  b) Permission is hereby granted, free of charge, to any person
>> + *     obtaining a copy of this software and associated documentation
>> + *     files (the "Software"), to deal in the Software without
>> + *     restriction, including without limitation the rights to use,
>> + *     copy, modify, merge, publish, distribute, sublicense, and/or
>> + *     sell copies of the Software, and to permit persons to whom the
>> + *     Software is furnished to do so, subject to the following
>> + *     conditions:
>> + *
>> + *     The above copyright notice and this permission notice shall be
>> + *     included in all copies or substantial portions of the Software.
>> + *
>> + *     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.
>> + */
>> +
>> +/dts-v1/;
>> +#include "rk3368-geekbox.dtsi"
>> +
>> +/ {
>> +	model = "GeekBox";
>> +};
>> diff --git a/arch/arm64/boot/dts/rockchip/rk3368-geekbox.dtsi
>> b/arch/arm64/boot/dts/rockchip/rk3368-geekbox.dtsi new file mode 100644
>> index 0000000..f083cdb
>> --- /dev/null
>> +++ b/arch/arm64/boot/dts/rockchip/rk3368-geekbox.dtsi
>> @@ -0,0 +1,242 @@
>> +/*
>> + * Copyright (c) 2016 Andreas F?rber
>> + *
>> + * This file is dual-licensed: you can use it either under the terms
>> + * of the GPL or the X11 license, at your option. Note that this dual
>> + * licensing only applies to this file, and not this project as a
>> + * whole.
>> + *
>> + *  a) This file is free software; you can redistribute it and/or
>> + *     modify it under the terms of the GNU General Public License as
>> + *     published by the Free Software Foundation; either version 2 of the
>> + *     License, or (at your option) any later version.
>> + *
>> + *     This file is distributed in the hope that it will be useful,
>> + *     but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + *     GNU General Public License for more details.
>> + *
>> + * Or, alternatively,
>> + *
>> + *  b) Permission is hereby granted, free of charge, to any person
>> + *     obtaining a copy of this software and associated documentation
>> + *     files (the "Software"), to deal in the Software without
>> + *     restriction, including without limitation the rights to use,
>> + *     copy, modify, merge, publish, distribute, sublicense, and/or
>> + *     sell copies of the Software, and to permit persons to whom the
>> + *     Software is furnished to do so, subject to the following
>> + *     conditions:
>> + *
>> + *     The above copyright notice and this permission notice shall be
>> + *     included in all copies or substantial portions of the Software.
>> + *
>> + *     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.
>> + */
>> +
>> +#include "rk3368.dtsi"
>> +
>> +/ {
>> +	compatible = "geekbuying,geekbox", "rockchip,rk3368";
> 
> you're introducing
> - a new vendor name (geekbuying), so should update 
> Documentation/devicetree/bindings/vendor-prefixes.txt in a separate patch

True. I did not check whether they're NASDAQ-listed either, but I assume
no; haven't compared the downstream DT for what they might've been using
themselves.

> - a new board, so should update 
> Documentation/devicetree/bindings/arm/rockchip.txt

Will do.

>> +
>> +	chosen {
>> +		stdout-path = "serial2:115200n8";
>> +	};
>> +
>> +	memory {
>> +		device_type = "memory";
>> +		reg = <0x0 0x0 0x0 0x80000000>;
>> +	};
>> +
>> +	leds: gpio-leds {
>> +		compatible = "gpio-leds";
>> +
>> +		blue {
>> +			gpios = <&gpio2 2 GPIO_ACTIVE_HIGH>;
>> +			label = "geekbox:blue:led";
>> +			default-state = "on";
>> +		};
>> +
>> +		red {
>> +			gpios = <&gpio2 3 GPIO_ACTIVE_HIGH>;
>> +			label = "geekbox:red:led";
>> +			default-state = "off";
>> +		};
>> +	};
>> +
>> +	vcc_sys: vcc-sys-regulator {
>> +		compatible = "regulator-fixed";
>> +		regulator-name = "vcc_sys";
>> +		regulator-min-microvolt = <5000000>;
>> +		regulator-max-microvolt = <5000000>;
>> +		regulator-always-on;
>> +		regulator-boot-on;
>> +	};
>> +};
>> +
>> +&i2c0 {
>> +	status = "okay";
>> +
>> +	rk808: pmic at 1b {
>> +		compatible = "rockchip,rk808";
>> +		reg = <0x1b>;
>> +		pinctrl-names = "default";
>> +		pinctrl-0 = <&pmic_int>, <&pmic_sleep>;
>> +		interrupt-parent = <&gpio0>;
>> +		interrupts = <5 IRQ_TYPE_LEVEL_LOW>;
>> +		rockchip,system-power-controller;
>> +		vcc1-supply = <&vcc_sys>;
>> +		vcc2-supply = <&vcc_sys>;
>> +		vcc3-supply = <&vcc_sys>;
>> +		vcc4-supply = <&vcc_sys>;
>> +		vcc6-supply = <&vcc_sys>;
>> +		vcc7-supply = <&vcc_sys>;
>> +		vcc8-supply = <&vcc_io>;
>> +		vcc9-supply = <&vcc_sys>;
>> +		vcc10-supply = <&vcc_sys>;
>> +		vcc11-supply = <&vcc_sys>;
>> +		vcc12-supply = <&vcc_io>;
>> +		clock-output-names = "xin32k", "rk808-clkout2";
>> +		#clock-cells = <1>;
>> +
>> +		regulators {
>> +			vdd_cpu: DCDC_REG1 {
>> +				regulator-always-on;
>> +				regulator-boot-on;
>> +				regulator-min-microvolt = <700000>;
>> +				regulator-max-microvolt = <1500000>;
>> +				regulator-name = "vdd_cpu";
>> +			};
>> +
>> +			vdd_log: DCDC_REG2 {
>> +				regulator-always-on;
>> +				regulator-boot-on;
>> +				regulator-min-microvolt = <700000>;
>> +				regulator-max-microvolt = <1500000>;
>> +				regulator-name = "vdd_log";
>> +			};
>> +
>> +			vcc_ddr: DCDC_REG3 {
>> +				regulator-always-on;
>> +				regulator-boot-on;
>> +				regulator-name = "vcc_ddr";
>> +			};
>> +
>> +			vcc_io: DCDC_REG4 {
>> +				regulator-always-on;
>> +				regulator-boot-on;
>> +				regulator-min-microvolt = <3300000>;
>> +				regulator-max-microvolt = <3300000>;
>> +				regulator-name = "vcc_io";
>> +			};
>> +
>> +			vcc18_flash: LDO_REG1 {
>> +				regulator-always-on;
>> +				regulator-boot-on;
>> +				regulator-min-microvolt = <1800000>;
>> +				regulator-max-microvolt = <1800000>;
>> +				regulator-name = "vcc18_flash";
>> +			};
>> +
>> +			vcc33_lcd: LDO_REG2 {
>> +				regulator-always-on;
>> +				regulator-boot-on;
>> +				regulator-min-microvolt = <3300000>;
>> +				regulator-max-microvolt = <3300000>;
>> +				regulator-name = "vcc33_lcd";
>> +			};
>> +
>> +			vdd_10: LDO_REG3 {
>> +				regulator-always-on;
> 
> why all others also "boot-on" and this one not?

Good question. I had to fiddle with voltages and always-on / boot-on to
get things to work so far, it kept probing with -EINVAL earlier.

According to the schematics, the PLL is behind vdd_10, so it probably
should be boot-on, too. I'm now less certain whether some of the others
should be boot-on though, in particular in light of RK3368 mainline
U-Boot not existing yet and choices possibly being different there.

Counter-question: Why does the -r88.dts have them all as fixed
regulators? I simply assumed here that the RK808-B is sufficiently
compatible with rockchip,rk808?

Regards,
Andreas

-- 
SUSE Linux GmbH, Maxfeldstr. 5, 90409 N?rnberg, Germany
GF: Felix Imend?rffer, Jane Smithard, Graham Norton; HRB 21284 (AG N?rnberg)


From dianders at chromium.org  Wed Jan 20 21:04:12 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Wed, 20 Jan 2016 21:04:12 -0800
Subject: [PATCH v4 01/21] usb: dwc2: rockchip: Make the max_transfer_size
 automatic
In-Reply-To: <1453352672-27890-1-git-send-email-dianders@chromium.org>
References: <1453352672-27890-1-git-send-email-dianders@chromium.org>
Message-ID: <1453352672-27890-2-git-send-email-dianders@chromium.org>

Previously we needed to set the max_transfer_size to explicitly be 65535
because the old driver would detect that our hardware could support much
bigger transfers and then would try to do them.  This wouldn't work
since the DMA alignment code couldn't support it.

Later in commit e8f8c14d9da7 ("usb: dwc2: clip max_transfer_size to
65535") upstream added support for clipping this automatically.  Since
that commit it has been OK to just use "-1" (default), but nobody
bothered to change it.

Let's change it to default now for two reasons:
- It's nice to use autodetected params.
- If we can remove the 65535 limit, we can transfer more!

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Acked-by: John Youn <johnyoun at synopsys.com>
Tested-by: Heiko Stuebner <heiko at sntech.de>
---
Changes in v4:
- Add John's Acks from <https://patchwork.kernel.org/patch/7631551>

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/platform.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
index 510f787434b3..5008a467ce06 100644
--- a/drivers/usb/dwc2/platform.c
+++ b/drivers/usb/dwc2/platform.c
@@ -129,7 +129,7 @@ static const struct dwc2_core_params params_rk3066 = {
 	.host_rx_fifo_size		= 520,	/* 520 DWORDs */
 	.host_nperio_tx_fifo_size	= 128,	/* 128 DWORDs */
 	.host_perio_tx_fifo_size	= 256,	/* 256 DWORDs */
-	.max_transfer_size		= 65535,
+	.max_transfer_size		= -1,
 	.max_packet_count		= -1,
 	.host_channels			= -1,
 	.phy_type			= -1,
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Wed Jan 20 21:04:15 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Wed, 20 Jan 2016 21:04:15 -0800
Subject: [PATCH v4 04/21] usb: dwc2: host: Set host_perio_tx_fifo_size to 304
 for rk3066
In-Reply-To: <1453352672-27890-1-git-send-email-dianders@chromium.org>
References: <1453352672-27890-1-git-send-email-dianders@chromium.org>
Message-ID: <1453352672-27890-5-git-send-email-dianders@chromium.org>

Looking at rk3288, there are two dwc2 ports.  One has 960
total_fifo_size and the other 972.

We're currently assigning 528 + 128 + 256 = 912.  That means we're
wasting 48 words on one port and 60 words on the other.  Since we have
one settings block for both ports, let's just eat the 48 words that we
know we have and assign them to what's probably the most
latency-senstive part of the system: periodic transfers.

Presumably other rk3288 parts using the rk3066 settings also have these
extra 48 words as well.

Note: ideally we'd adjust dwc2_calculate_dynamic_fifo() to handle this
more dynamically, but until that's done hardcoding things here seems OK.

NOTE: no known use cases are improved by this patch, but in my stressful
USB tests I certainly see my periodic FIFO filling up both before and
after this change.  Presumably there are some USB configurations where
the periodic FIFO would have been filled before where it no longer is.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v4:
- Set host_perio_tx_fifo_size to 304 for rk3066 new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/platform.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
index b6d7666e715c..afdcdeee266d 100644
--- a/drivers/usb/dwc2/platform.c
+++ b/drivers/usb/dwc2/platform.c
@@ -128,7 +128,7 @@ static const struct dwc2_core_params params_rk3066 = {
 	.en_multiple_tx_fifo		= -1,
 	.host_rx_fifo_size		= 528,	/* 528 DWORDs */
 	.host_nperio_tx_fifo_size	= 128,	/* 128 DWORDs */
-	.host_perio_tx_fifo_size	= 256,	/* 256 DWORDs */
+	.host_perio_tx_fifo_size	= 304,	/* 304 DWORDs */
 	.max_transfer_size		= -1,
 	.max_packet_count		= -1,
 	.host_channels			= -1,
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Wed Jan 20 21:04:11 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Wed, 20 Jan 2016 21:04:11 -0800
Subject: [PATCH v4 0/21] usb: dwc2: host: Fix and speed up all the stuff,
 especially with splits
Message-ID: <1453352672-27890-1-git-send-email-dianders@chromium.org>

This is a bit of catchall series for all the bug fix and performance
patches I've been working on over the last few months.  Note that for
dwc2 we need to do LOTS in software and need super low interrupt
latency, so most performance improvements actually fix real bugs.

Patches are structured to start with no-brainer stuff that could be
applied ASAP, especially things I've already gotten Acks for.  Things
get slightly more RFC / RFT like as we get farther down the series.
Anything that can be landed sooner rather than later (especially those
Acked long ago) would help in re-posts (I'm not biased, of course).

It's been a few months since my last post of this series.  In the
meantime I've added a bunch of small bugfixes to the start of it and
also TOTALLY REWROTE the microframe scheduler.  I'll say up front: I
know nothing about USB.  I haven't read the whole spec.  I'm not
terribly familiar with the OHCI, EHCI, and XHCI drivers in the kernel.
...and I'm pretty clueless overall.  Nevertheless, I've attempted to
write up a fancy scheduler based on the portion of the spec talking
about microframe scheduling requirements.  This rewritten scheduler does
seem to help when I start jamming lots of USB things into a hub, so
presumably the code is a reasonably starting point.  Given my current
understanding of USB the old code was fairly insane, so presumably even
if my new patch isn't perfect it's better than what we had.

Anyway, on to the patches:

1. usb: dwc2: rockchip: Make the max_transfer_size automatic

   No brainer.  Can land any time.

2. usb: dwc2: host: Get aligned DMA in a more supported way

   Although this touches a lot of code, it's mostly just deleting
   stuff.  The way this is working is nearly the same as tegra.  Biggest
   objection I expect is that it has too much duplication with tegra and
   musb.  I'd personally prefer to land it now and remove duplication
   later, but up to others.  Speeding up interrupt handler helps with
   SOF scheduling, so this is not just a dumb optimization.

3. usb: dwc2: host: Set host_rx_fifo_size to 528 for rk3066
4. usb: dwc2: host: Set host_perio_tx_fifo_size to 304 for rk3066

   Seems like a good idea and small impact, but if someone hates it or
   it breaks on some Rockchip SoC, just drop it.  I've only tested on
   rk3288 so it would be nice if someone with access to more Rockchip
   SoCs can give a tested by.

5. usb: dwc2: host: Avoid use of chan->qh after qh freed

   Simple bugfix.  Unrelated to the series but thrown in here.

6. usb: dwc2: host: Always add to the tail of queues

   Big functionality improvement.  Small patch.  Suggest applying ASAP.

7. usb: dwc2: hcd: fix split transfer schedule sequence

   Unless I'm misunderstanding, this should be a no-brainer to fix.
   Could be some bikeshedding on how to fix this.  Let me know if/how
   you want me to spin.  Otherwise I'd say land it and it will fix a
   bunch of stuff.

8. usb: dwc2: host: Add scheduler tracing

   Shouldn't hurt anything.  If you have bikesheds, let me know.

9.  usb: dwc2: host: Add a delay before releasing periodic bandwidth
10. usb: dwc2: host: Giveback URB in tasklet context

   I think we should take these.  They improve things a bunch and I have
   found no regressions due to them.  Additional testing appreciated, of
   course.

11. usb: dwc2: host: Use periodic interrupt even with DMA

   Just came up with this one recently so it's had slightly less
   testing.  ...but it certainly fixed a bunch of stuff.  Could probably
   be moved around in the series to be pretty much anywhere.  I don't
   think this has a huge impact until we fix the scheduler (below) but
   at the same time I'm pretty sure it's something that's been wrong for
   a long time.

12. usb: dwc2: host: Rename some fields in struct dwc2_qh
13. usb: dwc2: host: Reorder things in hcd_queue.c
14. usb: dwc2: host: Split code out to make dwc2_do_reserve()

   Cleanups to make future patches easier to understand.  Bikeshed away.
   All no-op changes.

15. usb: dwc2: host: Add scheduler logging for missed SOFs

   I found this to be quite helpful.  If you hate it, drop it from the
   series.

16. usb: dwc2: host: Manage frame nums better in scheduler

   Doesn't totally make sense on its own, but a good halfway point to
   the microframe scheduler.  ...and shouldn't regress anything.  Allows
   us to do the "Properly set even/odd frame" patch below which
   definitely improves things.

17. usb: dwc2: host: Schedule periodic right away if it's time

   Yet another small change to make scheduling tighter.

18. usb: dwc2: host: Add dwc2_hcd_get_future_frame_number() call

   Prep for ("usb: dwc2: host: Properly set even/odd frame")

19. usb: dwc2: host: Properly set even/odd frame

   Helps quite a bit.  Helps even more after the redone microframe
   scheduler.  Feel free to tidy up if you see easy ways to do this.
   Maybe someone has a better way to estimate time on the wire?

20. usb: dwc2: host: Totally redo the microframe scheduler

   Eyeballs please!  I think I've stared at this too much and now my
   eyes are glazing over.  This definitely helps but also probably needs
   a few more spins?  Of course, if nobody wants to review it, IMHO
   checking it in as-is is WAAAAY better than what we had before.

21. usb: dwc2: host: If using uframe scheduler, end splits better

   Low confidence in this one.  Worry that it will end something too
   soon, but haven't seen it yet.

===

Below is discussion of some of the speedup stuff (mostly relevant to the
first few patches).

===

The dwc2 interrupt handler is quite slow.  On rk3288 with a few things
plugged into the ports and with cpufreq locked at 696MHz (to simulate
real world idle system), I can easily observe dwc2_handle_hcd_intr()
taking > 120 us, sometimes > 150 us.  Note that SOF interrupts come
every 125 us with high speed USB, so taking > 120 us in the interrupt
handler is a big deal.

The patches here will speed up the interrupt controller significantly.
After this series, I have a hard time seeing the interrupt controller
taking > 20 us and I don't ever see it taking > 30 us ever in my tests
unless I bring the cpufreq back down.  With the cpufreq at 126 MHz I can
still see the interrupt handler take > 50 us, so I'm sure we could
improve this further.  ...but hey, it's a start.

This series also shows big speed improvements when testing with a USB
Gigabit Ethernet adapter.  Previously the tested adapter would top out
at about 15MB/s.  After these changes it gets about 23MB/s.

In addition to the speedup, this series also has the advantage of
simplifying dwc2 and making it more like everyone else (introducing the
possibility of future simplifications).  Picking this series up will
help your diffstat and likely win you friends.  ;)

===

Steps for gathering data with ftrace (for some reason I have to run
twice):

cd /sys/devices/system/cpu/cpu0/cpufreq/
echo userspace > scaling_governor
echo 696000 > scaling_setspeed

cd /sys/kernel/debug/tracing
echo 0 > tracing_on
echo "" > trace
echo nop > current_tracer
echo function_graph > current_tracer
echo dwc2_handle_hcd_intr > set_graph_function
echo dwc2_handle_common_intr >> set_graph_function
echo dwc2_handle_hcd_intr > set_ftrace_filter
echo dwc2_handle_common_intr >> set_ftrace_filter
echo funcgraph-abstime > trace_options
echo 70 > tracing_thresh
echo 1 > /sys/kernel/debug/tracing/tracing_on

sleep 2
cat trace

Changes in v4:
- Add John's Acks from <https://patchwork.kernel.org/patch/7631551>
- Set host_rx_fifo_size to 528 for rk3066 new for v4.
- Set host_perio_tx_fifo_size to 304 for rk3066 new for v4.
- Avoid use of chan->qh after qh freed new for v4.
- Always add to the tail of queues new for v4.
- fix split transfer schedule sequence new for v4.
- Retooled scheduler tracing a bit, so left off John's Ack from v3.
- Moved periodic bandwidth release delay patch earlier again.
- A bit earlier in the list of patches than in v3.
- Use periodic interrupt even with DMA new for v4.
- Rename some fields in struct dwc2_qh new for v4.
- Reorder things in hcd_queue.c new for v4.
- Split code out to make dwc2_do_reserve() new for v4.
- Add scheduler logging for missed SOFs new for v4.
- Manage frame nums better in scheduler new for v4.
- Schedule periodic right away if it's time new for v4.
- Add dwc2_hcd_get_future_frame_number() call new for v4.
- Properly set even/odd frame new for v4.
- Figured out what the microframe scheduler was supposed to do.
- Microframe rewrite is totally different from v3, hopefully more right.
- Microframe rewrite is later in the series now.
- If using uframe scheduler, end splits better new for v4.

Changes in v3:
- Moved periodic bandwidth release delay patch later in the series.
- The uframe scheduler patch is folded into optimization series.
- Optimize uframe scheduler "single uframe" case a little.
- uframe scheduler now atop logging patches.
- uframe scheduler now before delayed bandwidth release patches.
- Add defines like EARLY_FRAME_USEC
- Reorder dwc2_deschedule_periodic() in prep for future patches.
- uframe scheduler now shows real usefulness w/ future patches!
- Assuming single_tt is new for v3; not terribly well tested (yet).
- Keep track and use our uframe new for v3.

Changes in v2:
- Add a warn if setup_dma is not aligned (Julius Werner).
- Periodic bandwidth release delay new for V2
- Commit message now says that URB giveback change needs delay change.
- Totally rewrote uframe scheduler again after writing test code.
- uframe scheduler atop delayed bandwidth release patches.

Douglas Anderson (21):
  usb: dwc2: rockchip: Make the max_transfer_size automatic
  usb: dwc2: host: Get aligned DMA in a more supported way
  usb: dwc2: host: Set host_rx_fifo_size to 528 for rk3066
  usb: dwc2: host: Set host_perio_tx_fifo_size to 304 for rk3066
  usb: dwc2: host: Avoid use of chan->qh after qh freed
  usb: dwc2: host: Always add to the tail of queues
  usb: dwc2: hcd: fix split transfer schedule sequence
  usb: dwc2: host: Add scheduler tracing
  usb: dwc2: host: Add a delay before releasing periodic bandwidth
  usb: dwc2: host: Giveback URB in tasklet context
  usb: dwc2: host: Use periodic interrupt even with DMA
  usb: dwc2: host: Rename some fields in struct dwc2_qh
  usb: dwc2: host: Reorder things in hcd_queue.c
  usb: dwc2: host: Split code out to make dwc2_do_reserve()
  usb: dwc2: host: Add scheduler logging for missed SOFs
  usb: dwc2: host: Manage frame nums better in scheduler
  usb: dwc2: host: Schedule periodic right away if it's time
  usb: dwc2: host: Add dwc2_hcd_get_future_frame_number() call
  usb: dwc2: host: Properly set even/odd frame
  usb: dwc2: host: Totally redo the microframe scheduler
  usb: dwc2: host: If using uframe scheduler, end splits better

 drivers/usb/dwc2/core.c      |  119 ++-
 drivers/usb/dwc2/core.h      |  115 ++-
 drivers/usb/dwc2/hcd.c       |  385 +++++---
 drivers/usb/dwc2/hcd.h       |  126 ++-
 drivers/usb/dwc2/hcd_ddma.c  |   41 +-
 drivers/usb/dwc2/hcd_intr.c  |  164 ++--
 drivers/usb/dwc2/hcd_queue.c | 1995 ++++++++++++++++++++++++++++++++++--------
 drivers/usb/dwc2/platform.c  |    6 +-
 8 files changed, 2279 insertions(+), 672 deletions(-)

-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Wed Jan 20 21:04:14 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Wed, 20 Jan 2016 21:04:14 -0800
Subject: [PATCH v4 03/21] usb: dwc2: host: Set host_rx_fifo_size to 528 for
 rk3066
In-Reply-To: <1453352672-27890-1-git-send-email-dianders@chromium.org>
References: <1453352672-27890-1-git-send-email-dianders@chromium.org>
Message-ID: <1453352672-27890-4-git-send-email-dianders@chromium.org>

As documented in dwc2_calculate_dynamic_fifo(), host_rx_fifo_size should
really be:
 2 * ((Largest Packet size / 4) + 1 + 1) + n
 with n = number of host channel.

We have 9 host channels, so
 2 * ((1024/4) + 2) + 9 = 516 + 9 = 525

We've got 960 / 972 total_fifo_size on rk3288 (and presumably on
rk3066) and 525 + 128 + 256 = 909 so we're still under on both ports
even when we increment by 5.

Since we have space, Kever Yang suggests bumping by 8.  He says this
will meet INCR16 access and next fifo type can start with a aligned
address.

...so let's bump up by 8.  In the future, it would be nice if
dwc2_calculate_dynamic_fifo() could handle the "too small" FIFO case and
come up with something more dynamically.  When we do that we can figure
out how to allocate the extra 48 / 60 bytes of FIFO that we're currently
wasting.

NOTE: no known bugs are fixed by this patch, but it seems like a simple
fix and ought to fix someone.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v4:
- Set host_rx_fifo_size to 528 for rk3066 new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/platform.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
index 5008a467ce06..b6d7666e715c 100644
--- a/drivers/usb/dwc2/platform.c
+++ b/drivers/usb/dwc2/platform.c
@@ -126,7 +126,7 @@ static const struct dwc2_core_params params_rk3066 = {
 	.speed				= -1,
 	.enable_dynamic_fifo		= 1,
 	.en_multiple_tx_fifo		= -1,
-	.host_rx_fifo_size		= 520,	/* 520 DWORDs */
+	.host_rx_fifo_size		= 528,	/* 528 DWORDs */
 	.host_nperio_tx_fifo_size	= 128,	/* 128 DWORDs */
 	.host_perio_tx_fifo_size	= 256,	/* 256 DWORDs */
 	.max_transfer_size		= -1,
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Wed Jan 20 21:04:16 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Wed, 20 Jan 2016 21:04:16 -0800
Subject: [PATCH v4 05/21] usb: dwc2: host: Avoid use of chan->qh after qh freed
In-Reply-To: <1453352672-27890-1-git-send-email-dianders@chromium.org>
References: <1453352672-27890-1-git-send-email-dianders@chromium.org>
Message-ID: <1453352672-27890-6-git-send-email-dianders@chromium.org>

When poking around with USB devices with slub_debug enabled, I found
another obvious use after free.  Turns out that in dwc2_hc_n_intr() I
was in a state when the contents of chan->qh was filled with 0x6b,
indicating that chan->qh was freed but chan still had a reference to
it.

Let's make sure that whenever we free qh we also make sure we remove a
reference from its channel.

The bug fixed here doesn't appear to be new--I believe I just got lucky
and happened to see it while stress testing.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v4:
- Avoid use of chan->qh after qh freed new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/hcd.c      |  8 ++++++++
 drivers/usb/dwc2/hcd_intr.c | 10 ++++++++++
 2 files changed, 18 insertions(+)

diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index bc4bdbc1534e..7783c8ba0173 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -164,6 +164,9 @@ static void dwc2_qh_list_free(struct dwc2_hsotg *hsotg,
 					 qtd_list_entry)
 			dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh);
 
+		if (qh->channel && qh->channel->qh == qh)
+			qh->channel->qh = NULL;
+
 		spin_unlock_irqrestore(&hsotg->lock, flags);
 		dwc2_hcd_qh_free(hsotg, qh);
 		spin_lock_irqsave(&hsotg->lock, flags);
@@ -554,7 +557,12 @@ static int dwc2_hcd_endpoint_disable(struct dwc2_hsotg *hsotg,
 		dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh);
 
 	ep->hcpriv = NULL;
+
+	if (qh->channel && qh->channel->qh == qh)
+		qh->channel->qh = NULL;
+
 	spin_unlock_irqrestore(&hsotg->lock, flags);
+
 	dwc2_hcd_qh_free(hsotg, qh);
 
 	return 0;
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index 352c98364317..99efc2bd1617 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -1935,6 +1935,16 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum)
 	}
 
 	dwc2_writel(hcint, hsotg->regs + HCINT(chnum));
+
+	/*
+	 * If we got an interrupt after someone called
+	 * dwc2_hcd_endpoint_disable() we don't want to crash below
+	 */
+	if (!chan->qh) {
+		dev_warn(hsotg->dev, "Interrupt on disabled channel\n");
+		return;
+	}
+
 	chan->hcint = hcint;
 	hcint &= hcintmsk;
 
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Wed Jan 20 21:04:13 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Wed, 20 Jan 2016 21:04:13 -0800
Subject: [PATCH v4 02/21] usb: dwc2: host: Get aligned DMA in a more supported
 way
In-Reply-To: <1453352672-27890-1-git-send-email-dianders@chromium.org>
References: <1453352672-27890-1-git-send-email-dianders@chromium.org>
Message-ID: <1453352672-27890-3-git-send-email-dianders@chromium.org>

All other host controllers who want aligned buffers for DMA do it a
certain way.  Let's do that too instead of working behind the USB core's
back.  This makes our interrupt handler not take forever and also rips
out a lot of code, simplifying things a bunch.

This also has the side effect of removing the 65535 max transfer size
limit.

NOTE: The actual code to allocate the aligned buffers is ripped almost
completely from the tegra EHCI driver.  At some point in the future we
may want to add this functionality to the USB core to share more code
everywhere.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Acked-by: John Youn <johnyoun at synopsys.com>
Tested-by: Heiko Stuebner <heiko at sntech.de>
Tested-by: John Youn <johnyoun at synopsys.com>
---
Changes in v4:
- Add John's Acks from <https://patchwork.kernel.org/patch/7631551>

Changes in v3: None
Changes in v2:
- Add a warn if setup_dma is not aligned (Julius Werner).

 drivers/usb/dwc2/core.c      |  21 +-----
 drivers/usb/dwc2/hcd.c       | 170 +++++++++++++++++++++----------------------
 drivers/usb/dwc2/hcd.h       |  10 ---
 drivers/usb/dwc2/hcd_intr.c  |  65 -----------------
 drivers/usb/dwc2/hcd_queue.c |   7 +-
 5 files changed, 87 insertions(+), 186 deletions(-)

diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index 39a0fa8a4c0a..73f2771b7740 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -1958,19 +1958,11 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
 	}
 
 	if (hsotg->core_params->dma_enable > 0) {
-		dma_addr_t dma_addr;
-
-		if (chan->align_buf) {
-			if (dbg_hc(chan))
-				dev_vdbg(hsotg->dev, "align_buf\n");
-			dma_addr = chan->align_buf;
-		} else {
-			dma_addr = chan->xfer_dma;
-		}
-		dwc2_writel((u32)dma_addr, hsotg->regs + HCDMA(chan->hc_num));
+		dwc2_writel((u32)chan->xfer_dma,
+			    hsotg->regs + HCDMA(chan->hc_num));
 		if (dbg_hc(chan))
 			dev_vdbg(hsotg->dev, "Wrote %08lx to HCDMA(%d)\n",
-				 (unsigned long)dma_addr, chan->hc_num);
+				 (unsigned long)chan->xfer_dma, chan->hc_num);
 	}
 
 	/* Start the split */
@@ -3363,13 +3355,6 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
 	width = (hwcfg3 & GHWCFG3_XFER_SIZE_CNTR_WIDTH_MASK) >>
 		GHWCFG3_XFER_SIZE_CNTR_WIDTH_SHIFT;
 	hw->max_transfer_size = (1 << (width + 11)) - 1;
-	/*
-	 * Clip max_transfer_size to 65535. dwc2_hc_setup_align_buf() allocates
-	 * coherent buffers with this size, and if it's too large we can
-	 * exhaust the coherent DMA pool.
-	 */
-	if (hw->max_transfer_size > 65535)
-		hw->max_transfer_size = 65535;
 	width = (hwcfg3 & GHWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK) >>
 		GHWCFG3_PACKET_SIZE_CNTR_WIDTH_SHIFT;
 	hw->max_packet_count = (1 << (width + 4)) - 1;
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 8847c72e55f6..bc4bdbc1534e 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -635,9 +635,9 @@ static void dwc2_hc_init_split(struct dwc2_hsotg *hsotg,
 	chan->hub_port = (u8)hub_port;
 }
 
-static void *dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
-			       struct dwc2_host_chan *chan,
-			       struct dwc2_qtd *qtd, void *bufptr)
+static void dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
+			      struct dwc2_host_chan *chan,
+			      struct dwc2_qtd *qtd)
 {
 	struct dwc2_hcd_urb *urb = qtd->urb;
 	struct dwc2_hcd_iso_packet_desc *frame_desc;
@@ -657,7 +657,6 @@ static void *dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
 			else
 				chan->xfer_buf = urb->setup_packet;
 			chan->xfer_len = 8;
-			bufptr = NULL;
 			break;
 
 		case DWC2_CONTROL_DATA:
@@ -684,7 +683,6 @@ static void *dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
 				chan->xfer_dma = hsotg->status_buf_dma;
 			else
 				chan->xfer_buf = hsotg->status_buf;
-			bufptr = NULL;
 			break;
 		}
 		break;
@@ -717,14 +715,6 @@ static void *dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
 
 		chan->xfer_len = frame_desc->length - qtd->isoc_split_offset;
 
-		/* For non-dword aligned buffers */
-		if (hsotg->core_params->dma_enable > 0 &&
-		    (chan->xfer_dma & 0x3))
-			bufptr = (u8 *)urb->buf + frame_desc->offset +
-					qtd->isoc_split_offset;
-		else
-			bufptr = NULL;
-
 		if (chan->xact_pos == DWC2_HCSPLT_XACTPOS_ALL) {
 			if (chan->xfer_len <= 188)
 				chan->xact_pos = DWC2_HCSPLT_XACTPOS_ALL;
@@ -733,63 +723,93 @@ static void *dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
 		}
 		break;
 	}
+}
+
+#define DWC2_USB_DMA_ALIGN 4
+
+struct dma_aligned_buffer {
+	void *kmalloc_ptr;
+	void *old_xfer_buffer;
+	u8 data[0];
+};
+
+static void dwc2_free_dma_aligned_buffer(struct urb *urb)
+{
+	struct dma_aligned_buffer *temp;
+
+	if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER))
+		return;
+
+	temp = container_of(urb->transfer_buffer,
+		struct dma_aligned_buffer, data);
 
-	return bufptr;
+	if (usb_urb_dir_in(urb))
+		memcpy(temp->old_xfer_buffer, temp->data,
+		       urb->transfer_buffer_length);
+	urb->transfer_buffer = temp->old_xfer_buffer;
+	kfree(temp->kmalloc_ptr);
+
+	urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER;
 }
 
-static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
-				   struct dwc2_host_chan *chan,
-				   struct dwc2_hcd_urb *urb, void *bufptr)
+static int dwc2_alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags)
 {
-	u32 buf_size;
-	struct urb *usb_urb;
-	struct usb_hcd *hcd;
+	struct dma_aligned_buffer *temp, *kmalloc_ptr;
+	size_t kmalloc_size;
 
-	if (!qh->dw_align_buf) {
-		if (chan->ep_type != USB_ENDPOINT_XFER_ISOC)
-			buf_size = hsotg->core_params->max_transfer_size;
-		else
-			/* 3072 = 3 max-size Isoc packets */
-			buf_size = 3072;
+	if (urb->num_sgs || urb->sg ||
+	    urb->transfer_buffer_length == 0 ||
+	    !((uintptr_t)urb->transfer_buffer & (DWC2_USB_DMA_ALIGN - 1)))
+		return 0;
 
-		qh->dw_align_buf = kmalloc(buf_size, GFP_ATOMIC | GFP_DMA);
-		if (!qh->dw_align_buf)
-			return -ENOMEM;
-		qh->dw_align_buf_size = buf_size;
-	}
+	/* Allocate a buffer with enough padding for alignment */
+	kmalloc_size = urb->transfer_buffer_length +
+		sizeof(struct dma_aligned_buffer) + DWC2_USB_DMA_ALIGN - 1;
 
-	if (chan->xfer_len) {
-		dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
-		usb_urb = urb->priv;
+	kmalloc_ptr = kmalloc(kmalloc_size, mem_flags);
+	if (!kmalloc_ptr)
+		return -ENOMEM;
 
-		if (usb_urb) {
-			if (usb_urb->transfer_flags &
-			    (URB_SETUP_MAP_SINGLE | URB_DMA_MAP_SG |
-			     URB_DMA_MAP_PAGE | URB_DMA_MAP_SINGLE)) {
-				hcd = dwc2_hsotg_to_hcd(hsotg);
-				usb_hcd_unmap_urb_for_dma(hcd, usb_urb);
-			}
-			if (!chan->ep_is_in)
-				memcpy(qh->dw_align_buf, bufptr,
-				       chan->xfer_len);
-		} else {
-			dev_warn(hsotg->dev, "no URB in dwc2_urb\n");
-		}
-	}
+	/* Position our struct dma_aligned_buffer such that data is aligned */
+	temp = PTR_ALIGN(kmalloc_ptr + 1, DWC2_USB_DMA_ALIGN) - 1;
+	temp->kmalloc_ptr = kmalloc_ptr;
+	temp->old_xfer_buffer = urb->transfer_buffer;
+	if (usb_urb_dir_out(urb))
+		memcpy(temp->data, urb->transfer_buffer,
+		       urb->transfer_buffer_length);
+	urb->transfer_buffer = temp->data;
 
-	qh->dw_align_buf_dma = dma_map_single(hsotg->dev,
-			qh->dw_align_buf, qh->dw_align_buf_size,
-			chan->ep_is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
-	if (dma_mapping_error(hsotg->dev, qh->dw_align_buf_dma)) {
-		dev_err(hsotg->dev, "can't map align_buf\n");
-		chan->align_buf = 0;
-		return -EINVAL;
-	}
+	urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER;
 
-	chan->align_buf = qh->dw_align_buf_dma;
 	return 0;
 }
 
+static int dwc2_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
+				      gfp_t mem_flags)
+{
+	int ret;
+
+	/* We assume setup_dma is always aligned; warn if not */
+	WARN_ON_ONCE(urb->setup_dma &&
+		     (urb->setup_dma & (DWC2_USB_DMA_ALIGN - 1)));
+
+	ret = dwc2_alloc_dma_aligned_buffer(urb, mem_flags);
+	if (ret)
+		return ret;
+
+	ret = usb_hcd_map_urb_for_dma(hcd, urb, mem_flags);
+	if (ret)
+		dwc2_free_dma_aligned_buffer(urb);
+
+	return ret;
+}
+
+static void dwc2_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
+{
+	usb_hcd_unmap_urb_for_dma(hcd, urb);
+	dwc2_free_dma_aligned_buffer(urb);
+}
+
 /**
  * dwc2_assign_and_init_hc() - Assigns transactions from a QTD to a free host
  * channel and initializes the host channel to perform the transactions. The
@@ -804,7 +824,6 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 	struct dwc2_host_chan *chan;
 	struct dwc2_hcd_urb *urb;
 	struct dwc2_qtd *qtd;
-	void *bufptr = NULL;
 
 	if (dbg_qh(qh))
 		dev_vdbg(hsotg->dev, "%s(%p,%p)\n", __func__, hsotg, qh);
@@ -866,16 +885,10 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		!dwc2_hcd_is_pipe_in(&urb->pipe_info))
 		urb->actual_length = urb->length;
 
-	if (hsotg->core_params->dma_enable > 0) {
+	if (hsotg->core_params->dma_enable > 0)
 		chan->xfer_dma = urb->dma + urb->actual_length;
-
-		/* For non-dword aligned case */
-		if (hsotg->core_params->dma_desc_enable <= 0 &&
-		    (chan->xfer_dma & 0x3))
-			bufptr = (u8 *)urb->buf + urb->actual_length;
-	} else {
+	else
 		chan->xfer_buf = (u8 *)urb->buf + urb->actual_length;
-	}
 
 	chan->xfer_len = urb->length - urb->actual_length;
 	chan->xfer_count = 0;
@@ -887,27 +900,7 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		chan->do_split = 0;
 
 	/* Set the transfer attributes */
-	bufptr = dwc2_hc_init_xfer(hsotg, chan, qtd, bufptr);
-
-	/* Non DWORD-aligned buffer case */
-	if (bufptr) {
-		dev_vdbg(hsotg->dev, "Non-aligned buffer\n");
-		if (dwc2_hc_setup_align_buf(hsotg, qh, chan, urb, bufptr)) {
-			dev_err(hsotg->dev,
-				"%s: Failed to allocate memory to handle non-dword aligned buffer\n",
-				__func__);
-			/* Add channel back to free list */
-			chan->align_buf = 0;
-			chan->multi_count = 0;
-			list_add_tail(&chan->hc_list_entry,
-				      &hsotg->free_hc_list);
-			qtd->in_process = 0;
-			qh->channel = NULL;
-			return -ENOMEM;
-		}
-	} else {
-		chan->align_buf = 0;
-	}
+	dwc2_hc_init_xfer(hsotg, chan, qtd);
 
 	if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
 	    chan->ep_type == USB_ENDPOINT_XFER_ISOC)
@@ -2971,6 +2964,9 @@ static struct hc_driver dwc2_hc_driver = {
 
 	.bus_suspend = _dwc2_hcd_suspend,
 	.bus_resume = _dwc2_hcd_resume,
+
+	.map_urb_for_dma	= dwc2_map_urb_for_dma,
+	.unmap_urb_for_dma	= dwc2_unmap_urb_for_dma,
 };
 
 /*
diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index 8f0a29cefdf7..42f2e4e233da 100644
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -75,8 +75,6 @@ struct dwc2_qh;
  *                      (micro)frame
  * @xfer_buf:           Pointer to current transfer buffer position
  * @xfer_dma:           DMA address of xfer_buf
- * @align_buf:          In Buffer DMA mode this will be used if xfer_buf is not
- *                      DWORD aligned
  * @xfer_len:           Total number of bytes to transfer
  * @xfer_count:         Number of bytes transferred so far
  * @start_pkt_count:    Packet count at start of transfer
@@ -133,7 +131,6 @@ struct dwc2_host_chan {
 
 	u8 *xfer_buf;
 	dma_addr_t xfer_dma;
-	dma_addr_t align_buf;
 	u32 xfer_len;
 	u32 xfer_count;
 	u16 start_pkt_count;
@@ -243,10 +240,6 @@ enum dwc2_transaction_type {
  * @frame_usecs:        Internal variable used by the microframe scheduler
  * @start_split_frame:  (Micro)frame at which last start split was initialized
  * @ntd:                Actual number of transfer descriptors in a list
- * @dw_align_buf:       Used instead of original buffer if its physical address
- *                      is not dword-aligned
- * @dw_align_buf_size:  Size of dw_align_buf
- * @dw_align_buf_dma:   DMA address for dw_align_buf
  * @qtd_list:           List of QTDs for this QH
  * @channel:            Host channel currently processing transfers for this QH
  * @qh_list_entry:      Entry for QH in either the periodic or non-periodic
@@ -279,9 +272,6 @@ struct dwc2_qh {
 	u16 frame_usecs[8];
 	u16 start_split_frame;
 	u16 ntd;
-	u8 *dw_align_buf;
-	int dw_align_buf_size;
-	dma_addr_t dw_align_buf_dma;
 	struct list_head qtd_list;
 	struct dwc2_host_chan *channel;
 	struct list_head qh_list_entry;
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index f8253803a050..352c98364317 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -472,18 +472,6 @@ static int dwc2_update_urb_state(struct dwc2_hsotg *hsotg,
 		xfer_length = urb->length - urb->actual_length;
 	}
 
-	/* Non DWORD-aligned buffer case handling */
-	if (chan->align_buf && xfer_length) {
-		dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
-		dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
-				chan->qh->dw_align_buf_size,
-				chan->ep_is_in ?
-				DMA_FROM_DEVICE : DMA_TO_DEVICE);
-		if (chan->ep_is_in)
-			memcpy(urb->buf + urb->actual_length,
-					chan->qh->dw_align_buf, xfer_length);
-	}
-
 	dev_vdbg(hsotg->dev, "urb->actual_length=%d xfer_length=%d\n",
 		 urb->actual_length, xfer_length);
 	urb->actual_length += xfer_length;
@@ -565,21 +553,6 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state(
 		frame_desc->status = 0;
 		frame_desc->actual_length = dwc2_get_actual_xfer_length(hsotg,
 					chan, chnum, qtd, halt_status, NULL);
-
-		/* Non DWORD-aligned buffer case handling */
-		if (chan->align_buf && frame_desc->actual_length) {
-			dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n",
-				 __func__);
-			dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
-					chan->qh->dw_align_buf_size,
-					chan->ep_is_in ?
-					DMA_FROM_DEVICE : DMA_TO_DEVICE);
-			if (chan->ep_is_in)
-				memcpy(urb->buf + frame_desc->offset +
-					qtd->isoc_split_offset,
-					chan->qh->dw_align_buf,
-					frame_desc->actual_length);
-		}
 		break;
 	case DWC2_HC_XFER_FRAME_OVERRUN:
 		urb->error_count++;
@@ -600,21 +573,6 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state(
 		frame_desc->actual_length = dwc2_get_actual_xfer_length(hsotg,
 					chan, chnum, qtd, halt_status, NULL);
 
-		/* Non DWORD-aligned buffer case handling */
-		if (chan->align_buf && frame_desc->actual_length) {
-			dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n",
-				 __func__);
-			dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
-					chan->qh->dw_align_buf_size,
-					chan->ep_is_in ?
-					DMA_FROM_DEVICE : DMA_TO_DEVICE);
-			if (chan->ep_is_in)
-				memcpy(urb->buf + frame_desc->offset +
-					qtd->isoc_split_offset,
-					chan->qh->dw_align_buf,
-					frame_desc->actual_length);
-		}
-
 		/* Skip whole frame */
 		if (chan->qh->do_split &&
 		    chan->ep_type == USB_ENDPOINT_XFER_ISOC && chan->ep_is_in &&
@@ -680,8 +638,6 @@ static void dwc2_deactivate_qh(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 	}
 
 no_qtd:
-	if (qh->channel)
-		qh->channel->align_buf = 0;
 	qh->channel = NULL;
 	dwc2_hcd_qh_deactivate(hsotg, qh, continue_split);
 }
@@ -946,14 +902,6 @@ static int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg,
 
 	frame_desc->actual_length += len;
 
-	if (chan->align_buf) {
-		dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
-		dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
-				chan->qh->dw_align_buf_size, DMA_FROM_DEVICE);
-		memcpy(qtd->urb->buf + frame_desc->offset +
-		       qtd->isoc_split_offset, chan->qh->dw_align_buf, len);
-	}
-
 	qtd->isoc_split_offset += len;
 
 	if (frame_desc->actual_length >= frame_desc->length) {
@@ -1176,19 +1124,6 @@ static void dwc2_update_urb_state_abn(struct dwc2_hsotg *hsotg,
 		xfer_length = urb->length - urb->actual_length;
 	}
 
-	/* Non DWORD-aligned buffer case handling */
-	if (chan->align_buf && xfer_length && chan->ep_is_in) {
-		dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
-		dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
-				chan->qh->dw_align_buf_size,
-				chan->ep_is_in ?
-				DMA_FROM_DEVICE : DMA_TO_DEVICE);
-		if (chan->ep_is_in)
-			memcpy(urb->buf + urb->actual_length,
-					chan->qh->dw_align_buf,
-					xfer_length);
-	}
-
 	urb->actual_length += xfer_length;
 
 	hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum));
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 27d402f680a3..e0933a9dfad7 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -232,13 +232,8 @@ struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
  */
 void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
-	if (qh->desc_list) {
+	if (qh->desc_list)
 		dwc2_hcd_qh_free_ddma(hsotg, qh);
-	} else {
-		/* kfree(NULL) is safe */
-		kfree(qh->dw_align_buf);
-		qh->dw_align_buf_dma = (dma_addr_t)0;
-	}
 	kfree(qh);
 }
 
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Wed Jan 20 21:04:18 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Wed, 20 Jan 2016 21:04:18 -0800
Subject: [PATCH v4 07/21] usb: dwc2: hcd: fix split transfer schedule sequence
In-Reply-To: <1453352672-27890-1-git-send-email-dianders@chromium.org>
References: <1453352672-27890-1-git-send-email-dianders@chromium.org>
Message-ID: <1453352672-27890-8-git-send-email-dianders@chromium.org>

We're supposed to keep outstanding splits in order.  Keep track of a
list of the order of splits and process channel interrupts in that
order.

Without this change and the following setup:
* Rockchip rk3288 Chromebook, using port ff540000
  -> Pluggable 7-port Hub with Charging (powered)
     -> Microsoft Wireless Keyboard 2000 in port 1.
     -> Das Keyboard in port 2.

...I find that I get dropped keys on the Microsoft keyboard (I'm sure
there are other combinations that fail, but this documents my test).
Specifically I've been typing "hahahahahahaha" on the keyboard and often
see keys dropped or repeated.

After this change the above setup works properly.  This patch is based
on a previous patch proposed by Yunzhi Li ("usb: dwc2: hcd: fix periodic
transfer schedule sequence")

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Signed-off-by: Yunzhi Li <lyz at rock-chips.com>
---
Changes in v4:
- fix split transfer schedule sequence new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/core.c     |  6 ++++++
 drivers/usb/dwc2/core.h     |  2 ++
 drivers/usb/dwc2/hcd.c      |  3 +++
 drivers/usb/dwc2/hcd.h      |  2 ++
 drivers/usb/dwc2/hcd_intr.c | 17 +++++++++++++++++
 5 files changed, 30 insertions(+)

diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index 73f2771b7740..29f4bfe5970f 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -1676,6 +1676,8 @@ void dwc2_hc_cleanup(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan)
 
 	chan->xfer_started = 0;
 
+	list_del_init(&chan->split_order_list_entry);
+
 	/*
 	 * Clear channel interrupt enables and any unhandled channel interrupt
 	 * conditions
@@ -1868,6 +1870,10 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
 			ec_mc = 3;
 		else
 			ec_mc = 1;
+
+		/* Put ourselves on the list to keep order straight */
+		list_move_tail(&chan->split_order_list_entry,
+			       &hsotg->split_order);
 	} else {
 		if (dbg_hc(chan))
 			dev_vdbg(hsotg->dev, "no split\n");
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 7fb6434f4639..538cf38af0e4 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -657,6 +657,7 @@ struct dwc2_hregs_backup {
  *                      periodic_sched_ready because it must be rescheduled for
  *                      the next frame. Otherwise, the item moves to
  *                      periodic_sched_inactive.
+ * @split_order:        List keeping track of channels doing splits, in order.
  * @periodic_usecs:     Total bandwidth claimed so far for periodic transfers.
  *                      This value is in microseconds per (micro)frame. The
  *                      assumption is that all periodic transfers may occur in
@@ -780,6 +781,7 @@ struct dwc2_hsotg {
 	struct list_head periodic_sched_ready;
 	struct list_head periodic_sched_assigned;
 	struct list_head periodic_sched_queued;
+	struct list_head split_order;
 	u16 periodic_usecs;
 	u16 frame_usecs[8];
 	u16 frame_number;
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index d2daaea88d91..d2356d52ac7b 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -3151,6 +3151,8 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
 	INIT_LIST_HEAD(&hsotg->periodic_sched_assigned);
 	INIT_LIST_HEAD(&hsotg->periodic_sched_queued);
 
+	INIT_LIST_HEAD(&hsotg->split_order);
+
 	/*
 	 * Create a host channel descriptor for each host channel implemented
 	 * in the controller. Initialize the channel descriptor array.
@@ -3164,6 +3166,7 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
 		if (channel == NULL)
 			goto error3;
 		channel->hc_num = i;
+		INIT_LIST_HEAD(&channel->split_order_list_entry);
 		hsotg->hc_ptr_array[i] = channel;
 	}
 
diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index 42f2e4e233da..1b46e2e617cc 100644
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -106,6 +106,7 @@ struct dwc2_qh;
  * @hc_list_entry:      For linking to list of host channels
  * @desc_list_addr:     Current QH's descriptor list DMA address
  * @desc_list_sz:       Current QH's descriptor list size
+ * @split_order_list_entry: List entry for keeping track of the order of splits
  *
  * This structure represents the state of a single host channel when acting in
  * host mode. It contains the data items needed to transfer packets to an
@@ -158,6 +159,7 @@ struct dwc2_host_chan {
 	struct list_head hc_list_entry;
 	dma_addr_t desc_list_addr;
 	u32 desc_list_sz;
+	struct list_head split_order_list_entry;
 };
 
 struct dwc2_hcd_pipe_info {
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index 2c521c00e5e0..577c91096a51 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -2067,6 +2067,7 @@ static void dwc2_hc_intr(struct dwc2_hsotg *hsotg)
 {
 	u32 haint;
 	int i;
+	struct dwc2_host_chan *chan, *chan_tmp;
 
 	haint = dwc2_readl(hsotg->regs + HAINT);
 	if (dbg_perio()) {
@@ -2075,6 +2076,22 @@ static void dwc2_hc_intr(struct dwc2_hsotg *hsotg)
 		dev_vdbg(hsotg->dev, "HAINT=%08x\n", haint);
 	}
 
+	/*
+	 * According to USB 2.0 spec section 11.18.8, a host must
+	 * issue complete-split transactions in a microframe for a
+	 * set of full-/low-speed endpoints in the same relative
+	 * order as the start-splits were issued in a microframe for.
+	 */
+	list_for_each_entry_safe(chan, chan_tmp, &hsotg->split_order,
+				 split_order_list_entry) {
+		int hc_num = chan->hc_num;
+
+		if (haint & (1 << hc_num)) {
+			dwc2_hc_n_intr(hsotg, hc_num);
+			haint &= ~(1 << hc_num);
+		}
+	}
+
 	for (i = 0; i < hsotg->core_params->host_channels; i++) {
 		if (haint & (1 << i))
 			dwc2_hc_n_intr(hsotg, i);
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Wed Jan 20 21:04:17 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Wed, 20 Jan 2016 21:04:17 -0800
Subject: [PATCH v4 06/21] usb: dwc2: host: Always add to the tail of queues
In-Reply-To: <1453352672-27890-1-git-send-email-dianders@chromium.org>
References: <1453352672-27890-1-git-send-email-dianders@chromium.org>
Message-ID: <1453352672-27890-7-git-send-email-dianders@chromium.org>

The queues the the dwc2 host controller used are truly queues.  That
means FIFO or first in first out.

Unfortunately though the code was iterating through these queues
starting from the head, some places in the code was adding things to the
queue by adding at the head instead of the tail.  That means last in
first out.  Doh.

Go through and just always add to the tail.

Doing this makes things much happier when I've got:
* 7-port USB 2.0 Single-TT hub
* - Microsoft 2.4 GHz Transceiver v7.0 dongle
* - Jabra speakerphone playing music

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v4:
- Always add to the tail of queues new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/hcd.c       | 11 ++++++-----
 drivers/usb/dwc2/hcd_ddma.c  |  4 ++--
 drivers/usb/dwc2/hcd_intr.c  |  4 ++--
 drivers/usb/dwc2/hcd_queue.c |  6 ++++--
 4 files changed, 14 insertions(+), 11 deletions(-)

diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 7783c8ba0173..d2daaea88d91 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -969,7 +969,8 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions(
 		 * periodic assigned schedule
 		 */
 		qh_ptr = qh_ptr->next;
-		list_move(&qh->qh_list_entry, &hsotg->periodic_sched_assigned);
+		list_move_tail(&qh->qh_list_entry,
+			       &hsotg->periodic_sched_assigned);
 		ret_val = DWC2_TRANSACTION_PERIODIC;
 	}
 
@@ -1002,8 +1003,8 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions(
 		 * non-periodic active schedule
 		 */
 		qh_ptr = qh_ptr->next;
-		list_move(&qh->qh_list_entry,
-			  &hsotg->non_periodic_sched_active);
+		list_move_tail(&qh->qh_list_entry,
+			       &hsotg->non_periodic_sched_active);
 
 		if (ret_val == DWC2_TRANSACTION_NONE)
 			ret_val = DWC2_TRANSACTION_NON_PERIODIC;
@@ -1176,8 +1177,8 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg)
 			 * Move the QH from the periodic assigned schedule to
 			 * the periodic queued schedule
 			 */
-			list_move(&qh->qh_list_entry,
-				  &hsotg->periodic_sched_queued);
+			list_move_tail(&qh->qh_list_entry,
+				       &hsotg->periodic_sched_queued);
 
 			/* done queuing high bandwidth */
 			hsotg->queuing_high_bandwidth = 0;
diff --git a/drivers/usb/dwc2/hcd_ddma.c b/drivers/usb/dwc2/hcd_ddma.c
index 36606fc33c0d..16b261cfa92d 100644
--- a/drivers/usb/dwc2/hcd_ddma.c
+++ b/drivers/usb/dwc2/hcd_ddma.c
@@ -1327,8 +1327,8 @@ void dwc2_hcd_complete_xfer_ddma(struct dwc2_hsotg *hsotg,
 			dwc2_hcd_qh_unlink(hsotg, qh);
 		} else {
 			/* Keep in assigned schedule to continue transfer */
-			list_move(&qh->qh_list_entry,
-				  &hsotg->periodic_sched_assigned);
+			list_move_tail(&qh->qh_list_entry,
+				       &hsotg->periodic_sched_assigned);
 			/*
 			 * If channel has been halted during giveback of urb
 			 * then prevent any new scheduling.
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index 99efc2bd1617..2c521c00e5e0 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -143,7 +143,7 @@ static void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
 			 * Move QH to the ready list to be executed next
 			 * (micro)frame
 			 */
-			list_move(&qh->qh_list_entry,
+			list_move_tail(&qh->qh_list_entry,
 				  &hsotg->periodic_sched_ready);
 	}
 	tr_type = dwc2_hcd_select_transactions(hsotg);
@@ -794,7 +794,7 @@ static void dwc2_halt_channel(struct dwc2_hsotg *hsotg,
 			 * halt to be queued when the periodic schedule is
 			 * processed.
 			 */
-			list_move(&chan->qh->qh_list_entry,
+			list_move_tail(&chan->qh->qh_list_entry,
 				  &hsotg->periodic_sched_assigned);
 
 			/*
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index e0933a9dfad7..bc632a72f611 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -732,9 +732,11 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 	     dwc2_frame_num_le(qh->sched_frame, frame_number)) ||
 	    (hsotg->core_params->uframe_sched <= 0 &&
 	     qh->sched_frame == frame_number))
-		list_move(&qh->qh_list_entry, &hsotg->periodic_sched_ready);
+		list_move_tail(&qh->qh_list_entry,
+			       &hsotg->periodic_sched_ready);
 	else
-		list_move(&qh->qh_list_entry, &hsotg->periodic_sched_inactive);
+		list_move_tail(&qh->qh_list_entry,
+			       &hsotg->periodic_sched_inactive);
 }
 
 /**
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Wed Jan 20 21:04:21 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Wed, 20 Jan 2016 21:04:21 -0800
Subject: [PATCH v4 10/21] usb: dwc2: host: Giveback URB in tasklet context
In-Reply-To: <1453352672-27890-1-git-send-email-dianders@chromium.org>
References: <1453352672-27890-1-git-send-email-dianders@chromium.org>
Message-ID: <1453352672-27890-11-git-send-email-dianders@chromium.org>

In commit 94dfd7edfd5c ("USB: HCD: support giveback of URB in tasklet
context") support was added to give back the URB in tasklet context.
Let's take advantage of this in dwc2.

This speeds up the dwc2 interrupt handler considerably.

Note that this requires the change ("usb: dwc2: host: Add a delay before
releasing periodic bandwidth") to come first.

Note that, as per Alan Stern in
<https://patchwork.kernel.org/patch/7555771/>, we also need to make sure
that the extra delay before the device drivers submit more data doesn't
break the scheduler.  At the moment the scheduler is pretty broken (see
future patches) so it's hard to be 100% certain, but I have yet to see
any new breakage introduced by this delay.  ...and speeding up interrupt
processing for dwc2 is a huge deal because it means we've got a better
chance of not missing SOF interrupts.  That means we've got an overall
win here.

Note that when playing USB audio and using a USB webcam and having
several USB keyboards plugged in, the crackling on the USB audio device
is noticably reduced with this patch.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Tested-by: Heiko Stuebner <heiko at sntech.de>
---
Changes in v4:
- A bit earlier in the list of patches than in v3.

Changes in v3: None
Changes in v2:
- Commit message now says that URB giveback change needs delay change.

 drivers/usb/dwc2/hcd.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index d2356d52ac7b..fa684c68b8f6 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -2336,9 +2336,7 @@ void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
 	kfree(qtd->urb);
 	qtd->urb = NULL;
 
-	spin_unlock(&hsotg->lock);
 	usb_hcd_giveback_urb(dwc2_hsotg_to_hcd(hsotg), urb, status);
-	spin_lock(&hsotg->lock);
 }
 
 /*
@@ -2957,7 +2955,7 @@ static struct hc_driver dwc2_hc_driver = {
 	.hcd_priv_size = sizeof(struct wrapper_priv_data),
 
 	.irq = _dwc2_hcd_irq,
-	.flags = HCD_MEMORY | HCD_USB2,
+	.flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
 
 	.start = _dwc2_hcd_start,
 	.stop = _dwc2_hcd_stop,
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Wed Jan 20 21:04:19 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Wed, 20 Jan 2016 21:04:19 -0800
Subject: [PATCH v4 08/21] usb: dwc2: host: Add scheduler tracing
In-Reply-To: <1453352672-27890-1-git-send-email-dianders@chromium.org>
References: <1453352672-27890-1-git-send-email-dianders@chromium.org>
Message-ID: <1453352672-27890-9-git-send-email-dianders@chromium.org>

In preparation for future changes to the scheduler let's add some
tracing that makes it easy for us to see what's happening.  By default
this tracing will be off.

By changing "core.h" you can easily trace to ftrace, the console, or
nowhere.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v4:
- Retooled scheduler tracing a bit, so left off John's Ack from v3.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/core.h      | 20 ++++++++++++++++++++
 drivers/usb/dwc2/hcd.h       |  5 +++++
 drivers/usb/dwc2/hcd_intr.c  |  6 +++++-
 drivers/usb/dwc2/hcd_queue.c | 24 +++++++++++++++++++++++-
 4 files changed, 53 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 538cf38af0e4..18f9e4045643 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -44,6 +44,26 @@
 #include <linux/usb/phy.h>
 #include "hw.h"
 
+/*
+ * Suggested defines for tracers:
+ * - no_printk:    Disable tracing
+ * - pr_info:      Print this info to the console
+ * - trace_printk: Print this info to trace buffer (good for verbose logging)
+ */
+
+#define DWC2_TRACE_SCHEDULER		no_printk
+#define DWC2_TRACE_SCHEDULER_VB		no_printk
+
+/* Detailed scheduler tracing, but won't overwhelm console */
+#define dwc2_sch_dbg(hsotg, fmt, ...)					\
+	DWC2_TRACE_SCHEDULER(pr_fmt("%s: SCH: " fmt),			\
+			     dev_name(hsotg->dev), ##__VA_ARGS__)
+
+/* Verbose scheduler tracing */
+#define dwc2_sch_vdbg(hsotg, fmt, ...)					\
+	DWC2_TRACE_SCHEDULER_VB(pr_fmt("%s: SCH: " fmt),		\
+				dev_name(hsotg->dev), ##__VA_ARGS__)
+
 static inline u32 dwc2_readl(const void __iomem *addr)
 {
 	u32 value = __raw_readl(addr);
diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index 1b46e2e617cc..809bc4ff9116 100644
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -563,6 +563,11 @@ static inline u16 dwc2_frame_num_inc(u16 frame, u16 inc)
 	return (frame + inc) & HFNUM_MAX_FRNUM;
 }
 
+static inline u16 dwc2_frame_num_dec(u16 frame, u16 dec)
+{
+	return (frame + HFNUM_MAX_FRNUM + 1 - dec) & HFNUM_MAX_FRNUM;
+}
+
 static inline u16 dwc2_full_frame_num(u16 frame)
 {
 	return (frame & HFNUM_MAX_FRNUM) >> 3;
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index 577c91096a51..5d25a5ec9736 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -138,13 +138,17 @@ static void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
 	while (qh_entry != &hsotg->periodic_sched_inactive) {
 		qh = list_entry(qh_entry, struct dwc2_qh, qh_list_entry);
 		qh_entry = qh_entry->next;
-		if (dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number))
+		if (dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number)) {
+			dwc2_sch_vdbg(hsotg, "QH=%p ready fn=%04x, sch=%04x\n",
+				      qh, hsotg->frame_number, qh->sched_frame);
+
 			/*
 			 * Move QH to the ready list to be executed next
 			 * (micro)frame
 			 */
 			list_move_tail(&qh->qh_list_entry,
 				  &hsotg->periodic_sched_ready);
+		}
 	}
 	tr_type = dwc2_hcd_select_transactions(hsotg);
 	if (tr_type != DWC2_TRANSACTION_NONE)
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index bc632a72f611..0e9faa75593c 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -113,6 +113,9 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 		qh->sched_frame = dwc2_frame_num_inc(hsotg->frame_number,
 						     SCHEDULE_SLOP);
 		qh->interval = urb->interval;
+		dwc2_sch_dbg(hsotg, "QH=%p init sch=%04x, fn=%04x, int=%#x\n",
+			     qh, qh->sched_frame, hsotg->frame_number,
+			     qh->interval);
 #if 0
 		/* Increase interrupt polling rate for debugging */
 		if (qh->ep_type == USB_ENDPOINT_XFER_INT)
@@ -126,6 +129,11 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 			qh->interval *= 8;
 			qh->sched_frame |= 0x7;
 			qh->start_split_frame = qh->sched_frame;
+			dwc2_sch_dbg(hsotg,
+				     "QH=%p init*8 sch=%04x, fn=%04x, int=%#x\n",
+				     qh, qh->sched_frame, hsotg->frame_number,
+				     qh->interval);
+
 		}
 		dev_dbg(hsotg->dev, "interval=%d\n", qh->interval);
 	}
@@ -482,6 +490,8 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		if (frame >= 0) {
 			qh->sched_frame &= ~0x7;
 			qh->sched_frame |= (frame & 7);
+			dwc2_sch_dbg(hsotg, "QH=%p sched_p sch=%04x, uf=%d\n",
+				     qh, qh->sched_frame, frame);
 		}
 
 		if (status > 0)
@@ -583,10 +593,16 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 
 	if (!dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number) &&
 			!hsotg->frame_number) {
+		u16 new_frame;
+
 		dev_dbg(hsotg->dev,
 				"reset frame number counter\n");
-		qh->sched_frame = dwc2_frame_num_inc(hsotg->frame_number,
+		new_frame = dwc2_frame_num_inc(hsotg->frame_number,
 				SCHEDULE_SLOP);
+
+		dwc2_sch_vdbg(hsotg, "QH=%p reset sch=%04x=>%04x\n",
+			      qh, qh->sched_frame, new_frame);
+		qh->sched_frame = new_frame;
 	}
 
 	/* Add the new QH to the appropriate schedule */
@@ -652,6 +668,7 @@ static void dwc2_sched_periodic_split(struct dwc2_hsotg *hsotg,
 				      int sched_next_periodic_split)
 {
 	u16 incr;
+	u16 old_frame = qh->sched_frame;
 
 	if (sched_next_periodic_split) {
 		qh->sched_frame = frame_number;
@@ -677,6 +694,11 @@ static void dwc2_sched_periodic_split(struct dwc2_hsotg *hsotg,
 		qh->sched_frame |= 0x7;
 		qh->start_split_frame = qh->sched_frame;
 	}
+
+	dwc2_sch_vdbg(hsotg, "QH=%p next(%d) fn=%04x, sch=%04x=>%04x (%+d)\n",
+		      qh, sched_next_periodic_split, frame_number, old_frame,
+		      qh->sched_frame,
+		      dwc2_frame_num_dec(qh->sched_frame, old_frame));
 }
 
 /*
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Wed Jan 20 21:04:22 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Wed, 20 Jan 2016 21:04:22 -0800
Subject: [PATCH v4 11/21] usb: dwc2: host: Use periodic interrupt even with DMA
In-Reply-To: <1453352672-27890-1-git-send-email-dianders@chromium.org>
References: <1453352672-27890-1-git-send-email-dianders@chromium.org>
Message-ID: <1453352672-27890-12-git-send-email-dianders@chromium.org>

The old code in dwc2_process_periodic_channels() would only enable the
"periodic empty" interrupt if we weren't using DMA.  That wasn't right
since we can still get into cases where we have small FIFOs even on
systems that have DMA (the rk3288 is a prime example).

Let's always enable/disable the "periodic empty" when appropriate.  As
part of this:

* Always call dwc2_process_periodic_channels() even if there's nothing
  in periodic_sched_assigned (we move the queue empty check so we still
  avoid the extra work).  That will make extra certain that we will
  properly disable the "periodic empty" interrupt even if there's
  nothing queued up.

* Move the enable of "periodic empty" due to non-empty
  periodic_sched_assigned to be for slave mode (non-DMA mode) only.
  Presumably this was the original intention of the check for DMA since
  it seems to match the comments above where in slave mode we leave
  things on the assigned queue.

Note that even before this change slave mode didn't work for me, so I
can't say for sure that my understanding of slave mode is correct.
However, this shouldn't change anything for slave mode so if slave mode
worked for someone in the past it ought to still work.

With this change, I no longer get constant misses reported by my other
debugging code (and with future patches) when I've got:
* Rockchip rk3288 Chromebook, using port ff540000
  -> Pluggable 7-port Hub with Charging (powered)
     -> Microsoft Wireless Keyboard 2000 in port 1.
     -> Das Keyboard in port 2.
     -> Jabra Speaker in port 3
     -> Logitech, Inc. Webcam C600 in port 4
     -> Microsoft Sidewinder X6 Keyboard in port 5

...and I'm playing music on the USB speaker and capturing video from the
webcam.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v4:
- Use periodic interrupt even with DMA new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/hcd.c | 71 +++++++++++++++++++++++---------------------------
 1 file changed, 32 insertions(+), 39 deletions(-)

diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index fa684c68b8f6..350453999bad 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -1104,10 +1104,14 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg)
 	u32 fspcavail;
 	u32 gintmsk;
 	int status;
-	int no_queue_space = 0;
-	int no_fifo_space = 0;
+	bool no_queue_space = false;
+	bool no_fifo_space = false;
 	u32 qspcavail;
 
+	/* If empty list then just adjust interrupt enables */
+	if (list_empty(&hsotg->periodic_sched_assigned))
+		goto exit;
+
 	if (dbg_perio())
 		dev_vdbg(hsotg->dev, "Queue periodic transactions\n");
 
@@ -1185,42 +1189,32 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg)
 		}
 	}
 
-	if (hsotg->core_params->dma_enable <= 0) {
-		tx_status = dwc2_readl(hsotg->regs + HPTXSTS);
-		qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >>
-			    TXSTS_QSPCAVAIL_SHIFT;
-		fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >>
-			    TXSTS_FSPCAVAIL_SHIFT;
-		if (dbg_perio()) {
-			dev_vdbg(hsotg->dev,
-				 "  P Tx Req Queue Space Avail (after queue): %d\n",
-				 qspcavail);
-			dev_vdbg(hsotg->dev,
-				 "  P Tx FIFO Space Avail (after queue): %d\n",
-				 fspcavail);
-		}
-
-		if (!list_empty(&hsotg->periodic_sched_assigned) ||
-		    no_queue_space || no_fifo_space) {
-			/*
-			 * May need to queue more transactions as the request
-			 * queue or Tx FIFO empties. Enable the periodic Tx
-			 * FIFO empty interrupt. (Always use the half-empty
-			 * level to ensure that new requests are loaded as
-			 * soon as possible.)
-			 */
-			gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+exit:
+	if (no_queue_space || no_fifo_space ||
+	    (hsotg->core_params->dma_enable <= 0 &&
+	     !list_empty(&hsotg->periodic_sched_assigned))) {
+		/*
+		 * May need to queue more transactions as the request
+		 * queue or Tx FIFO empties. Enable the periodic Tx
+		 * FIFO empty interrupt. (Always use the half-empty
+		 * level to ensure that new requests are loaded as
+		 * soon as possible.)
+		 */
+		gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+		if (!(gintmsk & GINTSTS_PTXFEMP)) {
 			gintmsk |= GINTSTS_PTXFEMP;
 			dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
-		} else {
-			/*
-			 * Disable the Tx FIFO empty interrupt since there are
-			 * no more transactions that need to be queued right
-			 * now. This function is called from interrupt
-			 * handlers to queue more transactions as transfer
-			 * states change.
-			 */
-			gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+		}
+	} else {
+		/*
+		 * Disable the Tx FIFO empty interrupt since there are
+		 * no more transactions that need to be queued right
+		 * now. This function is called from interrupt
+		 * handlers to queue more transactions as transfer
+		 * states change.
+		*/
+		gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+		if (gintmsk & GINTSTS_PTXFEMP) {
 			gintmsk &= ~GINTSTS_PTXFEMP;
 			dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
 		}
@@ -1367,9 +1361,8 @@ void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg,
 	dev_vdbg(hsotg->dev, "Queue Transactions\n");
 #endif
 	/* Process host channels associated with periodic transfers */
-	if ((tr_type == DWC2_TRANSACTION_PERIODIC ||
-	     tr_type == DWC2_TRANSACTION_ALL) &&
-	    !list_empty(&hsotg->periodic_sched_assigned))
+	if (tr_type == DWC2_TRANSACTION_PERIODIC ||
+	    tr_type == DWC2_TRANSACTION_ALL)
 		dwc2_process_periodic_channels(hsotg);
 
 	/* Process host channels associated with non-periodic transfers */
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Wed Jan 20 21:04:20 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Wed, 20 Jan 2016 21:04:20 -0800
Subject: [PATCH v4 09/21] usb: dwc2: host: Add a delay before releasing
 periodic bandwidth
In-Reply-To: <1453352672-27890-1-git-send-email-dianders@chromium.org>
References: <1453352672-27890-1-git-send-email-dianders@chromium.org>
Message-ID: <1453352672-27890-10-git-send-email-dianders@chromium.org>

We'd like to be able to use HCD_BH in order to speed up the dwc2 host
interrupt handler quite a bit.  However, according to the kernel doc for
usb_submit_urb() (specifically the part about "Reserved Bandwidth
Transfers"), we need to keep a reservation active as long as a device
driver keeps submitting.  That was easy to do when we gave back the URB
in the interrupt context: we just looked at when our queue was empty and
released the reserved bandwidth then.  ...but now we need a little more
complexity.

We'll follow EHCI's lead in commit 9118f9eb4f1e ("USB: EHCI: improve
interrupt qh unlink") and add a 5ms delay.  Since we don't have a whole
timer infrastructure in dwc2, we'll just add a timer per QH.  The
overhead for this is very small.

Note that the dwc2 scheduler is pretty broken (see future patches to fix
it).  This patch attempts to replicate all old behavior and just add the
proper delay.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v4:
- Moved periodic bandwidth release delay patch earlier again.

Changes in v3:
- Moved periodic bandwidth release delay patch later in the series.

Changes in v2:
- Periodic bandwidth release delay new for V2

 drivers/usb/dwc2/hcd.h       |   6 ++
 drivers/usb/dwc2/hcd_queue.c | 237 +++++++++++++++++++++++++++++++++----------
 2 files changed, 187 insertions(+), 56 deletions(-)

diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index 809bc4ff9116..79473ea35bd6 100644
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -215,6 +215,7 @@ enum dwc2_transaction_type {
 /**
  * struct dwc2_qh - Software queue head structure
  *
+ * @hsotg:              The HCD state structure for the DWC OTG controller
  * @ep_type:            Endpoint type. One of the following values:
  *                       - USB_ENDPOINT_XFER_CONTROL
  *                       - USB_ENDPOINT_XFER_BULK
@@ -252,13 +253,16 @@ enum dwc2_transaction_type {
  * @n_bytes:            Xfer Bytes array. Each element corresponds to a transfer
  *                      descriptor and indicates original XferSize value for the
  *                      descriptor
+ * @unreserve_timer:    Timer for releasing periodic reservation.
  * @tt_buffer_dirty     True if clear_tt_buffer_complete is pending
+ * @unreserve_pending:  True if we planned to unreserve but haven't yet.
  *
  * A Queue Head (QH) holds the static characteristics of an endpoint and
  * maintains a list of transfers (QTDs) for that endpoint. A QH structure may
  * be entered in either the non-periodic or periodic schedule.
  */
 struct dwc2_qh {
+	struct dwc2_hsotg *hsotg;
 	u8 ep_type;
 	u8 ep_is_in;
 	u16 maxp;
@@ -281,7 +285,9 @@ struct dwc2_qh {
 	dma_addr_t desc_list_dma;
 	u32 desc_list_sz;
 	u32 *n_bytes;
+	struct timer_list unreserve_timer;
 	unsigned tt_buffer_dirty:1;
+	unsigned unreserve_pending:1;
 };
 
 /**
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 0e9faa75593c..b9e4867e1afd 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -53,6 +53,94 @@
 #include "core.h"
 #include "hcd.h"
 
+/* Wait this long before releasing periodic reservation */
+#define DWC2_UNRESERVE_DELAY (msecs_to_jiffies(5))
+
+/**
+ * dwc2_do_unreserve() - Actually release the periodic reservation
+ *
+ * This function actually releases the periodic bandwidth that was reserved
+ * by the given qh.
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller
+ * @qh:    QH for the periodic transfer.
+ */
+static void dwc2_do_unreserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+	assert_spin_locked(&hsotg->lock);
+
+	WARN_ON(!qh->unreserve_pending);
+
+	/* No more unreserve pending--we're doing it */
+	qh->unreserve_pending = false;
+
+	if (WARN_ON(!list_empty(&qh->qh_list_entry)))
+		list_del_init(&qh->qh_list_entry);
+
+	/* Update claimed usecs per (micro)frame */
+	hsotg->periodic_usecs -= qh->usecs;
+
+	if (hsotg->core_params->uframe_sched > 0) {
+		int i;
+
+		for (i = 0; i < 8; i++) {
+			hsotg->frame_usecs[i] += qh->frame_usecs[i];
+			qh->frame_usecs[i] = 0;
+		}
+	} else {
+		/* Release periodic channel reservation */
+		hsotg->periodic_channels--;
+	}
+}
+
+/**
+ * dwc2_unreserve_timer_fn() - Timer function to release periodic reservation
+ *
+ * According to the kernel doc for usb_submit_urb() (specifically the part about
+ * "Reserved Bandwidth Transfers"), we need to keep a reservation active as
+ * long as a device driver keeps submitting.  Since we're using HCD_BH to give
+ * back the URB we need to give the driver a little bit of time before we
+ * release the reservation.  This worker is called after the appropriate
+ * delay.
+ *
+ * @work: Pointer to a qh unreserve_work.
+ */
+static void dwc2_unreserve_timer_fn(unsigned long data)
+{
+	struct dwc2_qh *qh = (struct dwc2_qh *)data;
+	struct dwc2_hsotg *hsotg = qh->hsotg;
+	unsigned long flags;
+
+	/*
+	 * Wait for the lock, or for us to be scheduled again.  We
+	 * could be scheduled again if:
+	 * - We started executing but didn't get the lock yet.
+	 * - A new reservation came in, but cancel didn't take effect
+	 *   because we already started executing.
+	 * - The timer has been kicked again.
+	 * In that case cancel and wait for the next call.
+	 */
+	while (!spin_trylock_irqsave(&hsotg->lock, flags)) {
+		if (timer_pending(&qh->unreserve_timer))
+			return;
+	}
+
+	/*
+	 * Might be no more unreserve pending if:
+	 * - We started executing but didn't get the lock yet.
+	 * - A new reservation came in, but cancel didn't take effect
+	 *   because we already started executing.
+	 *
+	 * We can't put this in the loop above because unreserve_pending needs
+	 * to be accessed under lock, so we can only check it once we got the
+	 * lock.
+	 */
+	if (qh->unreserve_pending)
+		dwc2_do_unreserve(hsotg, qh);
+
+	spin_unlock_irqrestore(&hsotg->lock, flags);
+}
+
 /**
  * dwc2_qh_init() - Initializes a QH structure
  *
@@ -71,6 +159,9 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 	dev_vdbg(hsotg->dev, "%s()\n", __func__);
 
 	/* Initialize QH */
+	qh->hsotg = hsotg;
+	setup_timer(&qh->unreserve_timer, dwc2_unreserve_timer_fn,
+		    (unsigned long)qh);
 	qh->ep_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
 	qh->ep_is_in = dwc2_hcd_is_pipe_in(&urb->pipe_info) ? 1 : 0;
 
@@ -240,6 +331,15 @@ struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
  */
 void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
+	/* Make sure any unreserve work is finished. */
+	if (del_timer_sync(&qh->unreserve_timer)) {
+		unsigned long flags;
+
+		spin_lock_irqsave(&hsotg->lock, flags);
+		dwc2_do_unreserve(hsotg, qh);
+		spin_unlock_irqrestore(&hsotg->lock, flags);
+	}
+
 	if (qh->desc_list)
 		dwc2_hcd_qh_free_ddma(hsotg, qh);
 	kfree(qh);
@@ -477,51 +577,74 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
 	int status;
 
-	if (hsotg->core_params->uframe_sched > 0) {
-		int frame = -1;
-
-		status = dwc2_find_uframe(hsotg, qh);
-		if (status == 0)
-			frame = 7;
-		else if (status > 0)
-			frame = status - 1;
-
-		/* Set the new frame up */
-		if (frame >= 0) {
-			qh->sched_frame &= ~0x7;
-			qh->sched_frame |= (frame & 7);
-			dwc2_sch_dbg(hsotg, "QH=%p sched_p sch=%04x, uf=%d\n",
-				     qh, qh->sched_frame, frame);
+	status = dwc2_check_max_xfer_size(hsotg, qh);
+	if (status) {
+		dev_dbg(hsotg->dev,
+			"%s: Channel max transfer size too small for periodic transfer\n",
+			__func__);
+		return status;
+	}
+
+	/* Cancel pending unreserve; if canceled OK, unreserve was pending */
+	if (del_timer(&qh->unreserve_timer))
+		WARN_ON(!qh->unreserve_pending);
+
+	/*
+	 * Only need to reserve if there's not an unreserve pending, since if an
+	 * unreserve is pending then by definition our old reservation is still
+	 * valid.  Unreserve might still be pending even if we didn't cancel if
+	 * dwc2_unreserve_timer_fn() already started.  Code in the timer handles
+	 * that case.
+	 */
+	if (!qh->unreserve_pending) {
+		if (hsotg->core_params->uframe_sched > 0) {
+			int frame = -1;
+
+			status = dwc2_find_uframe(hsotg, qh);
+			if (status == 0)
+				frame = 7;
+			else if (status > 0)
+				frame = status - 1;
+
+			/* Set the new frame up */
+			if (frame >= 0) {
+				qh->sched_frame &= ~0x7;
+				qh->sched_frame |= (frame & 7);
+				dwc2_sch_dbg(hsotg,
+					     "QH=%p sched_p sch=%04x, uf=%d\n",
+					     qh, qh->sched_frame, frame);
+			}
+
+			if (status > 0)
+				status = 0;
+		} else {
+			status = dwc2_periodic_channel_available(hsotg);
+			if (status) {
+				dev_info(hsotg->dev,
+					"%s: No host channel available for periodic transfer\n",
+					__func__);
+				return status;
+			}
+
+			status = dwc2_check_periodic_bandwidth(hsotg, qh);
 		}
 
-		if (status > 0)
-			status = 0;
-	} else {
-		status = dwc2_periodic_channel_available(hsotg);
 		if (status) {
-			dev_info(hsotg->dev,
-				 "%s: No host channel available for periodic transfer\n",
-				 __func__);
+			dev_dbg(hsotg->dev,
+				"%s: Insufficient periodic bandwidth for periodic transfer\n",
+				__func__);
 			return status;
 		}
 
-		status = dwc2_check_periodic_bandwidth(hsotg, qh);
-	}
+		if (hsotg->core_params->uframe_sched <= 0)
+			/* Reserve periodic channel */
+			hsotg->periodic_channels++;
 
-	if (status) {
-		dev_dbg(hsotg->dev,
-			"%s: Insufficient periodic bandwidth for periodic transfer\n",
-			__func__);
-		return status;
+		/* Update claimed usecs per (micro)frame */
+		hsotg->periodic_usecs += qh->usecs;
 	}
 
-	status = dwc2_check_max_xfer_size(hsotg, qh);
-	if (status) {
-		dev_dbg(hsotg->dev,
-			"%s: Channel max transfer size too small for periodic transfer\n",
-			__func__);
-		return status;
-	}
+	qh->unreserve_pending = 0;
 
 	if (hsotg->core_params->dma_desc_enable > 0)
 		/* Don't rely on SOF and start in ready schedule */
@@ -531,13 +654,6 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		list_add_tail(&qh->qh_list_entry,
 			      &hsotg->periodic_sched_inactive);
 
-	if (hsotg->core_params->uframe_sched <= 0)
-		/* Reserve periodic channel */
-		hsotg->periodic_channels++;
-
-	/* Update claimed usecs per (micro)frame */
-	hsotg->periodic_usecs += qh->usecs;
-
 	return status;
 }
 
@@ -551,22 +667,31 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 static void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg,
 				     struct dwc2_qh *qh)
 {
-	int i;
+	bool did_modify;
 
-	list_del_init(&qh->qh_list_entry);
+	assert_spin_locked(&hsotg->lock);
 
-	/* Update claimed usecs per (micro)frame */
-	hsotg->periodic_usecs -= qh->usecs;
+	/*
+	 * Schedule the unreserve to happen in a little bit.  Cases here:
+	 * - Unreserve worker might be sitting there waiting to grab the lock.
+	 *   In this case it will notice it's been schedule again and will
+	 *   quit.
+	 * - Unreserve worker might not be scheduled.
+	 *
+	 * We should never already be scheduled since dwc2_schedule_periodic()
+	 * should have canceled the scheduled unreserve timer (hence the
+	 * warning on did_modify).
+	 *
+	 * We add + 1 to the timer to guarantee that at least 1 jiffy has
+	 * passed (otherwise if the jiffy counter might tick right after we
+	 * read it and we'll get no delay).
+	 */
+	did_modify = mod_timer(&qh->unreserve_timer,
+			       jiffies + DWC2_UNRESERVE_DELAY + 1);
+	WARN_ON(did_modify);
+	qh->unreserve_pending = 1;
 
-	if (hsotg->core_params->uframe_sched > 0) {
-		for (i = 0; i < 8; i++) {
-			hsotg->frame_usecs[i] += qh->frame_usecs[i];
-			qh->frame_usecs[i] = 0;
-		}
-	} else {
-		/* Release periodic channel reservation */
-		hsotg->periodic_channels--;
-	}
+	list_del_init(&qh->qh_list_entry);
 }
 
 /**
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Wed Jan 20 21:04:26 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Wed, 20 Jan 2016 21:04:26 -0800
Subject: [PATCH v4 15/21] usb: dwc2: host: Add scheduler logging for missed
 SOFs
In-Reply-To: <1453352672-27890-1-git-send-email-dianders@chromium.org>
References: <1453352672-27890-1-git-send-email-dianders@chromium.org>
Message-ID: <1453352672-27890-16-git-send-email-dianders@chromium.org>

We'll use the new "scheduler verbose debugging" macro to log missed
SOFs.  This is fast enough (assuming you configure it to use the ftrace
buffer) that we can do it without worrying about the speed hit.  The
overhead hit if the scheduler tracing is set to "no_printk" should be
near zero.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v4:
- Add scheduler logging for missed SOFs new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/core.h     |  3 ++-
 drivers/usb/dwc2/hcd.c      |  2 +-
 drivers/usb/dwc2/hcd_intr.c | 12 ++++++++----
 3 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 18f9e4045643..64d45a2053bb 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -809,9 +809,10 @@ struct dwc2_hsotg {
 	bool bus_suspended;
 	bool new_connection;
 
+	u16 last_frame_num;
+
 #ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
 #define FRAME_NUM_ARRAY_SIZE 1000
-	u16 last_frame_num;
 	u16 *frame_num_array;
 	u16 *last_frame_num_array;
 	int frame_num_idx;
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 350453999bad..32c160e3e13e 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -3077,8 +3077,8 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
 			FRAME_NUM_ARRAY_SIZE, GFP_KERNEL);
 	if (!hsotg->last_frame_num_array)
 		goto error1;
-	hsotg->last_frame_num = HFNUM_MAX_FRNUM;
 #endif
+	hsotg->last_frame_num = HFNUM_MAX_FRNUM;
 
 	/* Check if the bus driver or platform code has setup a dma_mask */
 	if (hsotg->core_params->dma_enable > 0 &&
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index b54a0c41d34f..1875e4590a3c 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -55,12 +55,16 @@
 /* This function is for debug only */
 static void dwc2_track_missed_sofs(struct dwc2_hsotg *hsotg)
 {
-#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
 	u16 curr_frame_number = hsotg->frame_number;
+	u16 expected = dwc2_frame_num_inc(hsotg->last_frame_num, 1);
+
+	if (expected != curr_frame_number)
+		dwc2_sch_vdbg(hsotg, "MISSED SOF %04x != %04x\n",
+			expected, curr_frame_number);
 
+#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
 	if (hsotg->frame_num_idx < FRAME_NUM_ARRAY_SIZE) {
-		if (((hsotg->last_frame_num + 1) & HFNUM_MAX_FRNUM) !=
-		    curr_frame_number) {
+		if (expected != curr_frame_number) {
 			hsotg->frame_num_array[hsotg->frame_num_idx] =
 					curr_frame_number;
 			hsotg->last_frame_num_array[hsotg->frame_num_idx] =
@@ -79,8 +83,8 @@ static void dwc2_track_missed_sofs(struct dwc2_hsotg *hsotg)
 		}
 		hsotg->dumped_frame_num_array = 1;
 	}
-	hsotg->last_frame_num = curr_frame_number;
 #endif
+	hsotg->last_frame_num = curr_frame_number;
 }
 
 static void dwc2_hc_handle_tt_clear(struct dwc2_hsotg *hsotg,
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Wed Jan 20 21:04:29 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Wed, 20 Jan 2016 21:04:29 -0800
Subject: [PATCH v4 18/21] usb: dwc2: host: Add
 dwc2_hcd_get_future_frame_number() call
In-Reply-To: <1453352672-27890-1-git-send-email-dianders@chromium.org>
References: <1453352672-27890-1-git-send-email-dianders@chromium.org>
Message-ID: <1453352672-27890-19-git-send-email-dianders@chromium.org>

As we start getting more exact about our scheduling it's becoming more
and more important to know exactly how far through the current frame we
are.  This lets us make decisions about whether there's still time left
to start a new transaction in the current frame.

We'll add dwc2_hcd_get_future_frame_number() which will tell you what
the frame number will be a certain number of microseconds (us) from
now.  We can use this information to help decide if there's enough time
left in the frame for a transaction that will take a certain duration.

This is expected to be used by a future change ("usb: dwc2: host:
Properly set even/odd frame").

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v4:
- Add dwc2_hcd_get_future_frame_number() call new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/core.h |  4 ++++
 drivers/usb/dwc2/hcd.c  | 29 +++++++++++++++++++++++++++++
 2 files changed, 33 insertions(+)

diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 64d45a2053bb..52cbea28d0e9 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -1235,12 +1235,16 @@ static inline int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg,
 
 #if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
 extern int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg);
+extern int dwc2_hcd_get_future_frame_number(struct dwc2_hsotg *hsotg, int us);
 extern void dwc2_hcd_connect(struct dwc2_hsotg *hsotg);
 extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force);
 extern void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
 #else
 static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
 { return 0; }
+static inline int dwc2_hcd_get_future_frame_number(struct dwc2_hsotg *hsotg,
+						   int us)
+{ return 0; }
 static inline void dwc2_hcd_connect(struct dwc2_hsotg *hsotg) {}
 static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force) {}
 static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {}
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 32c160e3e13e..69a2a175100a 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -1942,6 +1942,35 @@ int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
 	return (hfnum & HFNUM_FRNUM_MASK) >> HFNUM_FRNUM_SHIFT;
 }
 
+int dwc2_hcd_get_future_frame_number(struct dwc2_hsotg *hsotg, int us)
+{
+	u32 hprt = dwc2_readl(hsotg->regs + HPRT0);
+	u32 hfir = dwc2_readl(hsotg->regs + HFIR);
+	u32 hfnum = dwc2_readl(hsotg->regs + HFNUM);
+	unsigned int us_per_frame;
+	unsigned int frame_number;
+	unsigned int remaining;
+	unsigned int interval;
+	unsigned int phy_clks;
+
+	/* High speed has 125 us per (micro) frame; others are 1 ms per */
+	us_per_frame = (hprt & HPRT0_SPD_MASK) ? 1000 : 125;
+
+	/* Extract fields */
+	frame_number = (hfnum & HFNUM_FRNUM_MASK) >> HFNUM_FRNUM_SHIFT;
+	remaining = (hfnum & HFNUM_FRREM_MASK) >> HFNUM_FRREM_SHIFT;
+	interval = (hfir & HFIR_FRINT_MASK) >> HFIR_FRINT_SHIFT;
+
+	/*
+	 * Number of phy clocks since the last tick of the frame number after
+	 * "us" has passed.
+	 */
+	phy_clks = (interval - remaining) +
+		   DIV_ROUND_UP(interval * us, us_per_frame);
+
+	return dwc2_frame_num_inc(frame_number, phy_clks / interval);
+}
+
 int dwc2_hcd_is_b_host(struct dwc2_hsotg *hsotg)
 {
 	return hsotg->op_state == OTG_STATE_B_HOST;
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Wed Jan 20 21:04:28 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Wed, 20 Jan 2016 21:04:28 -0800
Subject: [PATCH v4 17/21] usb: dwc2: host: Schedule periodic right away if
 it's time
In-Reply-To: <1453352672-27890-1-git-send-email-dianders@chromium.org>
References: <1453352672-27890-1-git-send-email-dianders@chromium.org>
Message-ID: <1453352672-27890-18-git-send-email-dianders@chromium.org>

In dwc2_hcd_qh_deactivate() we will put some things on the
periodic_sched_ready list.  These things won't be taken off the ready
list until the next SOF, which might be a little late.  Let's put them
on right away.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v4:
- Schedule periodic right away if it's time new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/hcd_queue.c | 18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index f235402dfc5e..2b6e57ad2661 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -1078,12 +1078,26 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 	 * Note: we purposely use the frame_number from the "hsotg" structure
 	 * since we know SOF interrupt will handle future frames.
 	 */
-	if (dwc2_frame_num_le(qh->next_active_frame, hsotg->frame_number))
+	if (dwc2_frame_num_le(qh->next_active_frame, hsotg->frame_number)) {
+		enum dwc2_transaction_type tr_type;
+
+		/*
+		 * We're bypassing the SOF handler which is normally what puts
+		 * us on the ready list because we're in a hurry and need to
+		 * try to catch up.
+		 */
+		dwc2_sch_vdbg(hsotg, "QH=%p IMM ready fn=%04x, nxt=%04x\n",
+			      qh, frame_number, qh->next_active_frame);
 		list_move_tail(&qh->qh_list_entry,
 			       &hsotg->periodic_sched_ready);
-	else
+
+		tr_type = dwc2_hcd_select_transactions(hsotg);
+		if (tr_type != DWC2_TRANSACTION_NONE)
+			dwc2_hcd_queue_transactions(hsotg, tr_type);
+	} else {
 		list_move_tail(&qh->qh_list_entry,
 			       &hsotg->periodic_sched_inactive);
+	}
 }
 
 /**
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Wed Jan 20 21:04:23 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Wed, 20 Jan 2016 21:04:23 -0800
Subject: [PATCH v4 12/21] usb: dwc2: host: Rename some fields in struct dwc2_qh
In-Reply-To: <1453352672-27890-1-git-send-email-dianders@chromium.org>
References: <1453352672-27890-1-git-send-email-dianders@chromium.org>
Message-ID: <1453352672-27890-13-git-send-email-dianders@chromium.org>

This no-op change just does some renames to simplify a future patch.

1. The "interval" field is renamed to "host_interval" to make it more
   obvious that this interval may be 8 times the interval that the
   device sees (if we're doing split transactions).  A future patch will
   also add the "device_interval" field.
2. The "usecs" field is renamed to "host_us" again to make it more
   obvious that this is the time for the transaction as seen by the
   host.  For split transactions the device may see a much longer
   transaction time.  A future patch will also add "device_us".
3. The "sched_frame" field is renamed to "next_active_frame".  The name
   "sched_frame" kept confusing me because it felt like something more
   permament (the QH's reservation or something).  The name
   "next_active_frame" makes it more obvious that this field is
   constantly changing.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v4:
- Rename some fields in struct dwc2_qh new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/hcd.h       |  20 ++++----
 drivers/usb/dwc2/hcd_ddma.c  |  37 ++++++++-------
 drivers/usb/dwc2/hcd_intr.c  |  10 ++--
 drivers/usb/dwc2/hcd_queue.c | 107 ++++++++++++++++++++++---------------------
 4 files changed, 92 insertions(+), 82 deletions(-)

diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index 79473ea35bd6..10c35585a2bd 100644
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -236,10 +236,14 @@ enum dwc2_transaction_type {
  * @do_split:           Full/low speed endpoint on high-speed hub requires split
  * @td_first:           Index of first activated isochronous transfer descriptor
  * @td_last:            Index of last activated isochronous transfer descriptor
- * @usecs:              Bandwidth in microseconds per (micro)frame
- * @interval:           Interval between transfers in (micro)frames
- * @sched_frame:        (Micro)frame to initialize a periodic transfer.
- *                      The transfer executes in the following (micro)frame.
+ * @host_us:            Bandwidth in microseconds per transfer as seen by host
+ * @host_interval:      Interval between transfers as seen by the host.  If
+ *                      the host is high speed and the device is low speed this
+ *                      will be 8 times device interval.
+ * @next_active_frame:  (Micro)frame before we next need to put something on
+ *                      the bus.  We'll move the qh to active here.  If the
+ *                      host is in high speed mode this will be a uframe.  If
+ *                      the host is in low speed mode this will be a full frame.
  * @frame_usecs:        Internal variable used by the microframe scheduler
  * @start_split_frame:  (Micro)frame at which last start split was initialized
  * @ntd:                Actual number of transfer descriptors in a list
@@ -272,9 +276,9 @@ struct dwc2_qh {
 	u8 do_split;
 	u8 td_first;
 	u8 td_last;
-	u16 usecs;
-	u16 interval;
-	u16 sched_frame;
+	u16 host_us;
+	u16 host_interval;
+	u16 next_active_frame;
 	u16 frame_usecs[8];
 	u16 start_split_frame;
 	u16 ntd;
@@ -651,7 +655,7 @@ static inline u16 dwc2_hcd_get_ep_bandwidth(struct dwc2_hsotg *hsotg,
 		return 0;
 	}
 
-	return qh->usecs;
+	return qh->host_us;
 }
 
 extern void dwc2_hcd_save_data_toggle(struct dwc2_hsotg *hsotg,
diff --git a/drivers/usb/dwc2/hcd_ddma.c b/drivers/usb/dwc2/hcd_ddma.c
index 16b261cfa92d..26b00270ca0b 100644
--- a/drivers/usb/dwc2/hcd_ddma.c
+++ b/drivers/usb/dwc2/hcd_ddma.c
@@ -81,7 +81,7 @@ static u16 dwc2_max_desc_num(struct dwc2_qh *qh)
 static u16 dwc2_frame_incr_val(struct dwc2_qh *qh)
 {
 	return qh->dev_speed == USB_SPEED_HIGH ?
-	       (qh->interval + 8 - 1) / 8 : qh->interval;
+	       (qh->host_interval + 8 - 1) / 8 : qh->host_interval;
 }
 
 static int dwc2_desc_list_alloc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
@@ -252,7 +252,7 @@ static void dwc2_update_frame_list(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 	chan = qh->channel;
 	inc = dwc2_frame_incr_val(qh);
 	if (qh->ep_type == USB_ENDPOINT_XFER_ISOC)
-		i = dwc2_frame_list_idx(qh->sched_frame);
+		i = dwc2_frame_list_idx(qh->next_active_frame);
 	else
 		i = 0;
 
@@ -278,13 +278,13 @@ static void dwc2_update_frame_list(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 		return;
 
 	chan->schinfo = 0;
-	if (chan->speed == USB_SPEED_HIGH && qh->interval) {
+	if (chan->speed == USB_SPEED_HIGH && qh->host_interval) {
 		j = 1;
 		/* TODO - check this */
-		inc = (8 + qh->interval - 1) / qh->interval;
+		inc = (8 + qh->host_interval - 1) / qh->host_interval;
 		for (i = 0; i < inc; i++) {
 			chan->schinfo |= j;
-			j = j << qh->interval;
+			j = j << qh->host_interval;
 		}
 	} else {
 		chan->schinfo = 0xff;
@@ -431,7 +431,10 @@ static u16 dwc2_calc_starting_frame(struct dwc2_hsotg *hsotg,
 
 	hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
 
-	/* sched_frame is always frame number (not uFrame) both in FS and HS! */
+	/*
+	 * next_active_frame is always frame number (not uFrame) both in FS
+	 * and HS!
+	 */
 
 	/*
 	 * skip_frames is used to limit activated descriptors number
@@ -514,13 +517,13 @@ static u16 dwc2_recalc_initial_desc_idx(struct dwc2_hsotg *hsotg,
 		 */
 		fr_idx_tmp = dwc2_frame_list_idx(frame);
 		fr_idx = (FRLISTEN_64_SIZE +
-			  dwc2_frame_list_idx(qh->sched_frame) - fr_idx_tmp)
-			 % dwc2_frame_incr_val(qh);
+			  dwc2_frame_list_idx(qh->next_active_frame) -
+			  fr_idx_tmp) % dwc2_frame_incr_val(qh);
 		fr_idx = (fr_idx + fr_idx_tmp) % FRLISTEN_64_SIZE;
 	} else {
-		qh->sched_frame = dwc2_calc_starting_frame(hsotg, qh,
+		qh->next_active_frame = dwc2_calc_starting_frame(hsotg, qh,
 							   &skip_frames);
-		fr_idx = dwc2_frame_list_idx(qh->sched_frame);
+		fr_idx = dwc2_frame_list_idx(qh->next_active_frame);
 	}
 
 	qh->td_first = qh->td_last = dwc2_frame_to_desc_idx(qh, fr_idx);
@@ -583,7 +586,7 @@ static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
 	u16 next_idx;
 
 	idx = qh->td_last;
-	inc = qh->interval;
+	inc = qh->host_interval;
 	hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
 	cur_idx = dwc2_frame_list_idx(hsotg->frame_number);
 	next_idx = dwc2_desclist_idx_inc(qh->td_last, inc, qh->dev_speed);
@@ -605,11 +608,11 @@ static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
 		}
 	}
 
-	if (qh->interval) {
-		ntd_max = (dwc2_max_desc_num(qh) + qh->interval - 1) /
-				qh->interval;
+	if (qh->host_interval) {
+		ntd_max = (dwc2_max_desc_num(qh) + qh->host_interval - 1) /
+				qh->host_interval;
 		if (skip_frames && !qh->channel)
-			ntd_max -= skip_frames / qh->interval;
+			ntd_max -= skip_frames / qh->host_interval;
 	}
 
 	max_xfer_size = qh->dev_speed == USB_SPEED_HIGH ?
@@ -1029,7 +1032,7 @@ static void dwc2_complete_isoc_xfer_ddma(struct dwc2_hsotg *hsotg,
 							  idx);
 			if (rc < 0)
 				return;
-			idx = dwc2_desclist_idx_inc(idx, qh->interval,
+			idx = dwc2_desclist_idx_inc(idx, qh->host_interval,
 						    chan->speed);
 			if (!rc)
 				continue;
@@ -1039,7 +1042,7 @@ static void dwc2_complete_isoc_xfer_ddma(struct dwc2_hsotg *hsotg,
 
 			/* rc == DWC2_CMPL_STOP */
 
-			if (qh->interval >= 32)
+			if (qh->host_interval >= 32)
 				goto stop_scan;
 
 			qh->td_first = idx;
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index 5d25a5ec9736..b54a0c41d34f 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -138,9 +138,11 @@ static void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
 	while (qh_entry != &hsotg->periodic_sched_inactive) {
 		qh = list_entry(qh_entry, struct dwc2_qh, qh_list_entry);
 		qh_entry = qh_entry->next;
-		if (dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number)) {
-			dwc2_sch_vdbg(hsotg, "QH=%p ready fn=%04x, sch=%04x\n",
-				      qh, hsotg->frame_number, qh->sched_frame);
+		if (dwc2_frame_num_le(qh->next_active_frame,
+				      hsotg->frame_number)) {
+			dwc2_sch_vdbg(hsotg, "QH=%p ready fn=%04x, nxt=%04x\n",
+				      qh, hsotg->frame_number,
+				      qh->next_active_frame);
 
 			/*
 			 * Move QH to the ready list to be executed next
@@ -1350,7 +1352,7 @@ static void dwc2_hc_nyet_intr(struct dwc2_hsotg *hsotg,
 			int frnum = dwc2_hcd_get_frame_number(hsotg);
 
 			if (dwc2_full_frame_num(frnum) !=
-			    dwc2_full_frame_num(chan->qh->sched_frame)) {
+			    dwc2_full_frame_num(chan->qh->next_active_frame)) {
 				/*
 				 * No longer in the same full speed frame.
 				 * Treat this as a transaction error.
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index b9e4867e1afd..39f4de6279f8 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -78,7 +78,7 @@ static void dwc2_do_unreserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		list_del_init(&qh->qh_list_entry);
 
 	/* Update claimed usecs per (micro)frame */
-	hsotg->periodic_usecs -= qh->usecs;
+	hsotg->periodic_usecs -= qh->host_us;
 
 	if (hsotg->core_params->uframe_sched > 0) {
 		int i;
@@ -193,40 +193,40 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 		int bytecount =
 			dwc2_hb_mult(qh->maxp) * dwc2_max_packet(qh->maxp);
 
-		qh->usecs = NS_TO_US(usb_calc_bus_time(qh->do_split ?
-				USB_SPEED_HIGH : dev_speed, qh->ep_is_in,
-				qh->ep_type == USB_ENDPOINT_XFER_ISOC,
-				bytecount));
+		qh->host_us = NS_TO_US(usb_calc_bus_time(qh->do_split ?
+			      USB_SPEED_HIGH : dev_speed, qh->ep_is_in,
+			      qh->ep_type == USB_ENDPOINT_XFER_ISOC,
+			      bytecount));
 
 		/* Ensure frame_number corresponds to the reality */
 		hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
 		/* Start in a slightly future (micro)frame */
-		qh->sched_frame = dwc2_frame_num_inc(hsotg->frame_number,
+		qh->next_active_frame = dwc2_frame_num_inc(hsotg->frame_number,
 						     SCHEDULE_SLOP);
-		qh->interval = urb->interval;
-		dwc2_sch_dbg(hsotg, "QH=%p init sch=%04x, fn=%04x, int=%#x\n",
-			     qh, qh->sched_frame, hsotg->frame_number,
-			     qh->interval);
+		qh->host_interval = urb->interval;
+		dwc2_sch_dbg(hsotg, "QH=%p init nxt=%04x, fn=%04x, int=%#x\n",
+			     qh, qh->next_active_frame, hsotg->frame_number,
+			     qh->host_interval);
 #if 0
 		/* Increase interrupt polling rate for debugging */
 		if (qh->ep_type == USB_ENDPOINT_XFER_INT)
-			qh->interval = 8;
+			qh->host_interval = 8;
 #endif
 		hprt = dwc2_readl(hsotg->regs + HPRT0);
 		prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
 		if (prtspd == HPRT0_SPD_HIGH_SPEED &&
 		    (dev_speed == USB_SPEED_LOW ||
 		     dev_speed == USB_SPEED_FULL)) {
-			qh->interval *= 8;
-			qh->sched_frame |= 0x7;
-			qh->start_split_frame = qh->sched_frame;
+			qh->host_interval *= 8;
+			qh->next_active_frame |= 0x7;
+			qh->start_split_frame = qh->next_active_frame;
 			dwc2_sch_dbg(hsotg,
-				     "QH=%p init*8 sch=%04x, fn=%04x, int=%#x\n",
-				     qh, qh->sched_frame, hsotg->frame_number,
-				     qh->interval);
+				     "QH=%p init*8 nxt=%04x, fn=%04x, int=%#x\n",
+				     qh, qh->next_active_frame,
+				     hsotg->frame_number, qh->host_interval);
 
 		}
-		dev_dbg(hsotg->dev, "interval=%d\n", qh->interval);
+		dev_dbg(hsotg->dev, "interval=%d\n", qh->host_interval);
 	}
 
 	dev_vdbg(hsotg->dev, "DWC OTG HCD QH Initialized\n");
@@ -277,9 +277,9 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 
 	if (qh->ep_type == USB_ENDPOINT_XFER_INT) {
 		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - usecs = %d\n",
-			 qh->usecs);
+			 qh->host_us);
 		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - interval = %d\n",
-			 qh->interval);
+			 qh->host_interval);
 	}
 }
 
@@ -404,19 +404,19 @@ static int dwc2_check_periodic_bandwidth(struct dwc2_hsotg *hsotg,
 		 * High speed mode
 		 * Max periodic usecs is 80% x 125 usec = 100 usec
 		 */
-		max_claimed_usecs = 100 - qh->usecs;
+		max_claimed_usecs = 100 - qh->host_us;
 	} else {
 		/*
 		 * Full speed mode
 		 * Max periodic usecs is 90% x 1000 usec = 900 usec
 		 */
-		max_claimed_usecs = 900 - qh->usecs;
+		max_claimed_usecs = 900 - qh->host_us;
 	}
 
 	if (hsotg->periodic_usecs > max_claimed_usecs) {
 		dev_err(hsotg->dev,
 			"%s: already claimed usecs %d, required usecs %d\n",
-			__func__, hsotg->periodic_usecs, qh->usecs);
+			__func__, hsotg->periodic_usecs, qh->host_us);
 		status = -ENOSPC;
 	}
 
@@ -443,7 +443,7 @@ void dwc2_hcd_init_usecs(struct dwc2_hsotg *hsotg)
 
 static int dwc2_find_single_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
-	unsigned short utime = qh->usecs;
+	unsigned short utime = qh->host_us;
 	int i;
 
 	for (i = 0; i < 8; i++) {
@@ -462,7 +462,7 @@ static int dwc2_find_single_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
  */
 static int dwc2_find_multi_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
-	unsigned short utime = qh->usecs;
+	unsigned short utime = qh->host_us;
 	unsigned short xtime;
 	int t_left;
 	int i;
@@ -608,11 +608,11 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 
 			/* Set the new frame up */
 			if (frame >= 0) {
-				qh->sched_frame &= ~0x7;
-				qh->sched_frame |= (frame & 7);
+				qh->next_active_frame &= ~0x7;
+				qh->next_active_frame |= (frame & 7);
 				dwc2_sch_dbg(hsotg,
-					     "QH=%p sched_p sch=%04x, uf=%d\n",
-					     qh, qh->sched_frame, frame);
+					     "QH=%p sched_p nxt=%04x, uf=%d\n",
+					     qh, qh->next_active_frame, frame);
 			}
 
 			if (status > 0)
@@ -641,7 +641,7 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 			hsotg->periodic_channels++;
 
 		/* Update claimed usecs per (micro)frame */
-		hsotg->periodic_usecs += qh->usecs;
+		hsotg->periodic_usecs += qh->host_us;
 	}
 
 	qh->unreserve_pending = 0;
@@ -716,7 +716,7 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		/* QH already in a schedule */
 		return 0;
 
-	if (!dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number) &&
+	if (!dwc2_frame_num_le(qh->next_active_frame, hsotg->frame_number) &&
 			!hsotg->frame_number) {
 		u16 new_frame;
 
@@ -725,9 +725,9 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		new_frame = dwc2_frame_num_inc(hsotg->frame_number,
 				SCHEDULE_SLOP);
 
-		dwc2_sch_vdbg(hsotg, "QH=%p reset sch=%04x=>%04x\n",
-			      qh, qh->sched_frame, new_frame);
-		qh->sched_frame = new_frame;
+		dwc2_sch_vdbg(hsotg, "QH=%p reset nxt=%04x=>%04x\n",
+			      qh, qh->next_active_frame, new_frame);
+		qh->next_active_frame = new_frame;
 	}
 
 	/* Add the new QH to the appropriate schedule */
@@ -793,10 +793,10 @@ static void dwc2_sched_periodic_split(struct dwc2_hsotg *hsotg,
 				      int sched_next_periodic_split)
 {
 	u16 incr;
-	u16 old_frame = qh->sched_frame;
+	u16 old_frame = qh->next_active_frame;
 
 	if (sched_next_periodic_split) {
-		qh->sched_frame = frame_number;
+		qh->next_active_frame = frame_number;
 		incr = dwc2_frame_num_inc(qh->start_split_frame, 1);
 		if (dwc2_frame_num_le(frame_number, incr)) {
 			/*
@@ -807,23 +807,24 @@ static void dwc2_sched_periodic_split(struct dwc2_hsotg *hsotg,
 			 */
 			if (qh->ep_type != USB_ENDPOINT_XFER_ISOC ||
 			    qh->ep_is_in != 0) {
-				qh->sched_frame =
-					dwc2_frame_num_inc(qh->sched_frame, 1);
+				qh->next_active_frame = dwc2_frame_num_inc(
+					qh->next_active_frame, 1);
 			}
 		}
 	} else {
-		qh->sched_frame = dwc2_frame_num_inc(qh->start_split_frame,
-						     qh->interval);
-		if (dwc2_frame_num_le(qh->sched_frame, frame_number))
-			qh->sched_frame = frame_number;
-		qh->sched_frame |= 0x7;
-		qh->start_split_frame = qh->sched_frame;
+		qh->next_active_frame =
+			dwc2_frame_num_inc(qh->start_split_frame,
+					   qh->host_interval);
+		if (dwc2_frame_num_le(qh->next_active_frame, frame_number))
+			qh->next_active_frame = frame_number;
+		qh->next_active_frame |= 0x7;
+		qh->start_split_frame = qh->next_active_frame;
 	}
 
-	dwc2_sch_vdbg(hsotg, "QH=%p next(%d) fn=%04x, sch=%04x=>%04x (%+d)\n",
+	dwc2_sch_vdbg(hsotg, "QH=%p next(%d) fn=%04x, nxt=%04x=>%04x (%+d)\n",
 		      qh, sched_next_periodic_split, frame_number, old_frame,
-		      qh->sched_frame,
-		      dwc2_frame_num_dec(qh->sched_frame, old_frame));
+		      qh->next_active_frame,
+		      dwc2_frame_num_dec(qh->next_active_frame, old_frame));
 }
 
 /*
@@ -861,10 +862,10 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 		dwc2_sched_periodic_split(hsotg, qh, frame_number,
 					  sched_next_periodic_split);
 	} else {
-		qh->sched_frame = dwc2_frame_num_inc(qh->sched_frame,
-						     qh->interval);
-		if (dwc2_frame_num_le(qh->sched_frame, frame_number))
-			qh->sched_frame = frame_number;
+		qh->next_active_frame = dwc2_frame_num_inc(
+			qh->next_active_frame, qh->host_interval);
+		if (dwc2_frame_num_le(qh->next_active_frame, frame_number))
+			qh->next_active_frame = frame_number;
 	}
 
 	if (list_empty(&qh->qtd_list)) {
@@ -876,9 +877,9 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 	 * appropriate queue
 	 */
 	if ((hsotg->core_params->uframe_sched > 0 &&
-	     dwc2_frame_num_le(qh->sched_frame, frame_number)) ||
+	     dwc2_frame_num_le(qh->next_active_frame, frame_number)) ||
 	    (hsotg->core_params->uframe_sched <= 0 &&
-	     qh->sched_frame == frame_number))
+	     qh->next_active_frame == frame_number))
 		list_move_tail(&qh->qh_list_entry,
 			       &hsotg->periodic_sched_ready);
 	else
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Wed Jan 20 21:04:25 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Wed, 20 Jan 2016 21:04:25 -0800
Subject: [PATCH v4 14/21] usb: dwc2: host: Split code out to make
 dwc2_do_reserve()
In-Reply-To: <1453352672-27890-1-git-send-email-dianders@chromium.org>
References: <1453352672-27890-1-git-send-email-dianders@chromium.org>
Message-ID: <1453352672-27890-15-git-send-email-dianders@chromium.org>

This no-op change splits code out of dwc2_schedule_periodic() into a
dwc2_do_reserve() function.  This makes it a little easier to follow the
logic.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v4:
- Split code out to make dwc2_do_reserve() new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/hcd_queue.c | 112 ++++++++++++++++++++++++++-----------------
 1 file changed, 67 insertions(+), 45 deletions(-)

diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 8a2067bc1e62..9ce407e5017d 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -245,6 +245,70 @@ static int dwc2_find_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 }
 
 /**
+ * dwc2_do_reserve() - Make a periodic reservation
+ *
+ * Try to allocate space in the periodic schedule.  Depending on parameters
+ * this might use the microframe scheduler or the dumb scheduler.
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller
+ * @qh:    QH for the periodic transfer.
+ *
+ * Returns: 0 upon success; error upon failure.
+ */
+static int dwc2_do_reserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+	int status;
+
+	if (hsotg->core_params->uframe_sched > 0) {
+		int frame = -1;
+
+		status = dwc2_find_uframe(hsotg, qh);
+		if (status == 0)
+			frame = 7;
+		else if (status > 0)
+			frame = status - 1;
+
+		/* Set the new frame up */
+		if (frame >= 0) {
+			qh->next_active_frame &= ~0x7;
+			qh->next_active_frame |= (frame & 7);
+			dwc2_sch_dbg(hsotg,
+				     "QH=%p sched_p nxt=%04x, uf=%d\n",
+				     qh, qh->next_active_frame, frame);
+		}
+
+		if (status > 0)
+			status = 0;
+	} else {
+		status = dwc2_periodic_channel_available(hsotg);
+		if (status) {
+			dev_info(hsotg->dev,
+				 "%s: No host channel available for periodic transfer\n",
+				 __func__);
+			return status;
+		}
+
+		status = dwc2_check_periodic_bandwidth(hsotg, qh);
+	}
+
+	if (status) {
+		dev_dbg(hsotg->dev,
+			"%s: Insufficient periodic bandwidth for periodic transfer\n",
+			__func__);
+		return status;
+	}
+
+	if (hsotg->core_params->uframe_sched <= 0)
+		/* Reserve periodic channel */
+		hsotg->periodic_channels++;
+
+	/* Update claimed usecs per (micro)frame */
+	hsotg->periodic_usecs += qh->host_us;
+
+	return 0;
+}
+
+/**
  * dwc2_do_unreserve() - Actually release the periodic reservation
  *
  * This function actually releases the periodic bandwidth that was reserved
@@ -393,51 +457,9 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 	 * that case.
 	 */
 	if (!qh->unreserve_pending) {
-		if (hsotg->core_params->uframe_sched > 0) {
-			int frame = -1;
-
-			status = dwc2_find_uframe(hsotg, qh);
-			if (status == 0)
-				frame = 7;
-			else if (status > 0)
-				frame = status - 1;
-
-			/* Set the new frame up */
-			if (frame >= 0) {
-				qh->next_active_frame &= ~0x7;
-				qh->next_active_frame |= (frame & 7);
-				dwc2_sch_dbg(hsotg,
-					     "QH=%p sched_p nxt=%04x, uf=%d\n",
-					     qh, qh->next_active_frame, frame);
-			}
-
-			if (status > 0)
-				status = 0;
-		} else {
-			status = dwc2_periodic_channel_available(hsotg);
-			if (status) {
-				dev_info(hsotg->dev,
-					"%s: No host channel available for periodic transfer\n",
-					__func__);
-				return status;
-			}
-
-			status = dwc2_check_periodic_bandwidth(hsotg, qh);
-		}
-
-		if (status) {
-			dev_dbg(hsotg->dev,
-				"%s: Insufficient periodic bandwidth for periodic transfer\n",
-				__func__);
+		status = dwc2_do_reserve(hsotg, qh);
+		if (status)
 			return status;
-		}
-
-		if (hsotg->core_params->uframe_sched <= 0)
-			/* Reserve periodic channel */
-			hsotg->periodic_channels++;
-
-		/* Update claimed usecs per (micro)frame */
-		hsotg->periodic_usecs += qh->host_us;
 	}
 
 	qh->unreserve_pending = 0;
@@ -450,7 +472,7 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		list_add_tail(&qh->qh_list_entry,
 			      &hsotg->periodic_sched_inactive);
 
-	return status;
+	return 0;
 }
 
 /**
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Wed Jan 20 21:04:24 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Wed, 20 Jan 2016 21:04:24 -0800
Subject: [PATCH v4 13/21] usb: dwc2: host: Reorder things in hcd_queue.c
In-Reply-To: <1453352672-27890-1-git-send-email-dianders@chromium.org>
References: <1453352672-27890-1-git-send-email-dianders@chromium.org>
Message-ID: <1453352672-27890-14-git-send-email-dianders@chromium.org>

This no-op change just reorders a few functions in hcd_queue.c in order
to prepare for future changes.  Motivations here:

The functions dwc2_hcd_qh_free() and dwc2_hcd_qh_create() are exported
functions.  They are not called within the file.  That means that they
should be near the bottom so that they can easily call static helpers.

The function dwc2_qh_init() is only called by dwc2_hcd_qh_create() and
should move near the bottom with it.

The only reason that the dwc2_unreserve_timer_fn() timer function (and
its subroutine dwc2_do_unreserve()) were so high in the file was that
they needed to be above dwc2_qh_init().  Now that dwc2_qh_init() has
been moved down it can be moved down a bit.  A later patch will split
the reserve code out of dwc2_schedule_periodic() and the reserve
function should be near the unreserve function.  The reserve function
needs to be below dwc2_find_uframe() since it calls that.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v4:
- Reorder things in hcd_queue.c new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/hcd_queue.c | 600 +++++++++++++++++++++----------------------
 1 file changed, 300 insertions(+), 300 deletions(-)

diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 39f4de6279f8..8a2067bc1e62 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -57,295 +57,6 @@
 #define DWC2_UNRESERVE_DELAY (msecs_to_jiffies(5))
 
 /**
- * dwc2_do_unreserve() - Actually release the periodic reservation
- *
- * This function actually releases the periodic bandwidth that was reserved
- * by the given qh.
- *
- * @hsotg: The HCD state structure for the DWC OTG controller
- * @qh:    QH for the periodic transfer.
- */
-static void dwc2_do_unreserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
-{
-	assert_spin_locked(&hsotg->lock);
-
-	WARN_ON(!qh->unreserve_pending);
-
-	/* No more unreserve pending--we're doing it */
-	qh->unreserve_pending = false;
-
-	if (WARN_ON(!list_empty(&qh->qh_list_entry)))
-		list_del_init(&qh->qh_list_entry);
-
-	/* Update claimed usecs per (micro)frame */
-	hsotg->periodic_usecs -= qh->host_us;
-
-	if (hsotg->core_params->uframe_sched > 0) {
-		int i;
-
-		for (i = 0; i < 8; i++) {
-			hsotg->frame_usecs[i] += qh->frame_usecs[i];
-			qh->frame_usecs[i] = 0;
-		}
-	} else {
-		/* Release periodic channel reservation */
-		hsotg->periodic_channels--;
-	}
-}
-
-/**
- * dwc2_unreserve_timer_fn() - Timer function to release periodic reservation
- *
- * According to the kernel doc for usb_submit_urb() (specifically the part about
- * "Reserved Bandwidth Transfers"), we need to keep a reservation active as
- * long as a device driver keeps submitting.  Since we're using HCD_BH to give
- * back the URB we need to give the driver a little bit of time before we
- * release the reservation.  This worker is called after the appropriate
- * delay.
- *
- * @work: Pointer to a qh unreserve_work.
- */
-static void dwc2_unreserve_timer_fn(unsigned long data)
-{
-	struct dwc2_qh *qh = (struct dwc2_qh *)data;
-	struct dwc2_hsotg *hsotg = qh->hsotg;
-	unsigned long flags;
-
-	/*
-	 * Wait for the lock, or for us to be scheduled again.  We
-	 * could be scheduled again if:
-	 * - We started executing but didn't get the lock yet.
-	 * - A new reservation came in, but cancel didn't take effect
-	 *   because we already started executing.
-	 * - The timer has been kicked again.
-	 * In that case cancel and wait for the next call.
-	 */
-	while (!spin_trylock_irqsave(&hsotg->lock, flags)) {
-		if (timer_pending(&qh->unreserve_timer))
-			return;
-	}
-
-	/*
-	 * Might be no more unreserve pending if:
-	 * - We started executing but didn't get the lock yet.
-	 * - A new reservation came in, but cancel didn't take effect
-	 *   because we already started executing.
-	 *
-	 * We can't put this in the loop above because unreserve_pending needs
-	 * to be accessed under lock, so we can only check it once we got the
-	 * lock.
-	 */
-	if (qh->unreserve_pending)
-		dwc2_do_unreserve(hsotg, qh);
-
-	spin_unlock_irqrestore(&hsotg->lock, flags);
-}
-
-/**
- * dwc2_qh_init() - Initializes a QH structure
- *
- * @hsotg: The HCD state structure for the DWC OTG controller
- * @qh:    The QH to init
- * @urb:   Holds the information about the device/endpoint needed to initialize
- *         the QH
- */
-#define SCHEDULE_SLOP 10
-static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
-			 struct dwc2_hcd_urb *urb)
-{
-	int dev_speed, hub_addr, hub_port;
-	char *speed, *type;
-
-	dev_vdbg(hsotg->dev, "%s()\n", __func__);
-
-	/* Initialize QH */
-	qh->hsotg = hsotg;
-	setup_timer(&qh->unreserve_timer, dwc2_unreserve_timer_fn,
-		    (unsigned long)qh);
-	qh->ep_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
-	qh->ep_is_in = dwc2_hcd_is_pipe_in(&urb->pipe_info) ? 1 : 0;
-
-	qh->data_toggle = DWC2_HC_PID_DATA0;
-	qh->maxp = dwc2_hcd_get_mps(&urb->pipe_info);
-	INIT_LIST_HEAD(&qh->qtd_list);
-	INIT_LIST_HEAD(&qh->qh_list_entry);
-
-	/* FS/LS Endpoint on HS Hub, NOT virtual root hub */
-	dev_speed = dwc2_host_get_speed(hsotg, urb->priv);
-
-	dwc2_host_hub_info(hsotg, urb->priv, &hub_addr, &hub_port);
-
-	if ((dev_speed == USB_SPEED_LOW || dev_speed == USB_SPEED_FULL) &&
-	    hub_addr != 0 && hub_addr != 1) {
-		dev_vdbg(hsotg->dev,
-			 "QH init: EP %d: TT found at hub addr %d, for port %d\n",
-			 dwc2_hcd_get_ep_num(&urb->pipe_info), hub_addr,
-			 hub_port);
-		qh->do_split = 1;
-	}
-
-	if (qh->ep_type == USB_ENDPOINT_XFER_INT ||
-	    qh->ep_type == USB_ENDPOINT_XFER_ISOC) {
-		/* Compute scheduling parameters once and save them */
-		u32 hprt, prtspd;
-
-		/* Todo: Account for split transfers in the bus time */
-		int bytecount =
-			dwc2_hb_mult(qh->maxp) * dwc2_max_packet(qh->maxp);
-
-		qh->host_us = NS_TO_US(usb_calc_bus_time(qh->do_split ?
-			      USB_SPEED_HIGH : dev_speed, qh->ep_is_in,
-			      qh->ep_type == USB_ENDPOINT_XFER_ISOC,
-			      bytecount));
-
-		/* Ensure frame_number corresponds to the reality */
-		hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
-		/* Start in a slightly future (micro)frame */
-		qh->next_active_frame = dwc2_frame_num_inc(hsotg->frame_number,
-						     SCHEDULE_SLOP);
-		qh->host_interval = urb->interval;
-		dwc2_sch_dbg(hsotg, "QH=%p init nxt=%04x, fn=%04x, int=%#x\n",
-			     qh, qh->next_active_frame, hsotg->frame_number,
-			     qh->host_interval);
-#if 0
-		/* Increase interrupt polling rate for debugging */
-		if (qh->ep_type == USB_ENDPOINT_XFER_INT)
-			qh->host_interval = 8;
-#endif
-		hprt = dwc2_readl(hsotg->regs + HPRT0);
-		prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
-		if (prtspd == HPRT0_SPD_HIGH_SPEED &&
-		    (dev_speed == USB_SPEED_LOW ||
-		     dev_speed == USB_SPEED_FULL)) {
-			qh->host_interval *= 8;
-			qh->next_active_frame |= 0x7;
-			qh->start_split_frame = qh->next_active_frame;
-			dwc2_sch_dbg(hsotg,
-				     "QH=%p init*8 nxt=%04x, fn=%04x, int=%#x\n",
-				     qh, qh->next_active_frame,
-				     hsotg->frame_number, qh->host_interval);
-
-		}
-		dev_dbg(hsotg->dev, "interval=%d\n", qh->host_interval);
-	}
-
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH Initialized\n");
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - qh = %p\n", qh);
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Device Address = %d\n",
-		 dwc2_hcd_get_dev_addr(&urb->pipe_info));
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Endpoint %d, %s\n",
-		 dwc2_hcd_get_ep_num(&urb->pipe_info),
-		 dwc2_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT");
-
-	qh->dev_speed = dev_speed;
-
-	switch (dev_speed) {
-	case USB_SPEED_LOW:
-		speed = "low";
-		break;
-	case USB_SPEED_FULL:
-		speed = "full";
-		break;
-	case USB_SPEED_HIGH:
-		speed = "high";
-		break;
-	default:
-		speed = "?";
-		break;
-	}
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Speed = %s\n", speed);
-
-	switch (qh->ep_type) {
-	case USB_ENDPOINT_XFER_ISOC:
-		type = "isochronous";
-		break;
-	case USB_ENDPOINT_XFER_INT:
-		type = "interrupt";
-		break;
-	case USB_ENDPOINT_XFER_CONTROL:
-		type = "control";
-		break;
-	case USB_ENDPOINT_XFER_BULK:
-		type = "bulk";
-		break;
-	default:
-		type = "?";
-		break;
-	}
-
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Type = %s\n", type);
-
-	if (qh->ep_type == USB_ENDPOINT_XFER_INT) {
-		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - usecs = %d\n",
-			 qh->host_us);
-		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - interval = %d\n",
-			 qh->host_interval);
-	}
-}
-
-/**
- * dwc2_hcd_qh_create() - Allocates and initializes a QH
- *
- * @hsotg:        The HCD state structure for the DWC OTG controller
- * @urb:          Holds the information about the device/endpoint needed
- *                to initialize the QH
- * @atomic_alloc: Flag to do atomic allocation if needed
- *
- * Return: Pointer to the newly allocated QH, or NULL on error
- */
-struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
-					  struct dwc2_hcd_urb *urb,
-					  gfp_t mem_flags)
-{
-	struct dwc2_qh *qh;
-
-	if (!urb->priv)
-		return NULL;
-
-	/* Allocate memory */
-	qh = kzalloc(sizeof(*qh), mem_flags);
-	if (!qh)
-		return NULL;
-
-	dwc2_qh_init(hsotg, qh, urb);
-
-	if (hsotg->core_params->dma_desc_enable > 0 &&
-	    dwc2_hcd_qh_init_ddma(hsotg, qh, mem_flags) < 0) {
-		dwc2_hcd_qh_free(hsotg, qh);
-		return NULL;
-	}
-
-	return qh;
-}
-
-/**
- * dwc2_hcd_qh_free() - Frees the QH
- *
- * @hsotg: HCD instance
- * @qh:    The QH to free
- *
- * QH should already be removed from the list. QTD list should already be empty
- * if called from URB Dequeue.
- *
- * Must NOT be called with interrupt disabled or spinlock held
- */
-void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
-{
-	/* Make sure any unreserve work is finished. */
-	if (del_timer_sync(&qh->unreserve_timer)) {
-		unsigned long flags;
-
-		spin_lock_irqsave(&hsotg->lock, flags);
-		dwc2_do_unreserve(hsotg, qh);
-		spin_unlock_irqrestore(&hsotg->lock, flags);
-	}
-
-	if (qh->desc_list)
-		dwc2_hcd_qh_free_ddma(hsotg, qh);
-	kfree(qh);
-}
-
-/**
  * dwc2_periodic_channel_available() - Checks that a channel is available for a
  * periodic transfer
  *
@@ -518,19 +229,104 @@ static int dwc2_find_multi_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 
 static int dwc2_find_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
-	int ret;
+	int ret;
+
+	if (qh->dev_speed == USB_SPEED_HIGH) {
+		/* if this is a hs transaction we need a full frame */
+		ret = dwc2_find_single_uframe(hsotg, qh);
+	} else {
+		/*
+		 * if this is a fs transaction we may need a sequence
+		 * of frames
+		 */
+		ret = dwc2_find_multi_uframe(hsotg, qh);
+	}
+	return ret;
+}
+
+/**
+ * dwc2_do_unreserve() - Actually release the periodic reservation
+ *
+ * This function actually releases the periodic bandwidth that was reserved
+ * by the given qh.
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller
+ * @qh:    QH for the periodic transfer.
+ */
+static void dwc2_do_unreserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+	assert_spin_locked(&hsotg->lock);
+
+	WARN_ON(!qh->unreserve_pending);
+
+	/* No more unreserve pending--we're doing it */
+	qh->unreserve_pending = false;
+
+	if (WARN_ON(!list_empty(&qh->qh_list_entry)))
+		list_del_init(&qh->qh_list_entry);
+
+	/* Update claimed usecs per (micro)frame */
+	hsotg->periodic_usecs -= qh->host_us;
+
+	if (hsotg->core_params->uframe_sched > 0) {
+		int i;
+
+		for (i = 0; i < 8; i++) {
+			hsotg->frame_usecs[i] += qh->frame_usecs[i];
+			qh->frame_usecs[i] = 0;
+		}
+	} else {
+		/* Release periodic channel reservation */
+		hsotg->periodic_channels--;
+	}
+}
+
+/**
+ * dwc2_unreserve_timer_fn() - Timer function to release periodic reservation
+ *
+ * According to the kernel doc for usb_submit_urb() (specifically the part about
+ * "Reserved Bandwidth Transfers"), we need to keep a reservation active as
+ * long as a device driver keeps submitting.  Since we're using HCD_BH to give
+ * back the URB we need to give the driver a little bit of time before we
+ * release the reservation.  This worker is called after the appropriate
+ * delay.
+ *
+ * @work: Pointer to a qh unreserve_work.
+ */
+static void dwc2_unreserve_timer_fn(unsigned long data)
+{
+	struct dwc2_qh *qh = (struct dwc2_qh *)data;
+	struct dwc2_hsotg *hsotg = qh->hsotg;
+	unsigned long flags;
 
-	if (qh->dev_speed == USB_SPEED_HIGH) {
-		/* if this is a hs transaction we need a full frame */
-		ret = dwc2_find_single_uframe(hsotg, qh);
-	} else {
-		/*
-		 * if this is a fs transaction we may need a sequence
-		 * of frames
-		 */
-		ret = dwc2_find_multi_uframe(hsotg, qh);
+	/*
+	 * Wait for the lock, or for us to be scheduled again.  We
+	 * could be scheduled again if:
+	 * - We started executing but didn't get the lock yet.
+	 * - A new reservation came in, but cancel didn't take effect
+	 *   because we already started executing.
+	 * - The timer has been kicked again.
+	 * In that case cancel and wait for the next call.
+	 */
+	while (!spin_trylock_irqsave(&hsotg->lock, flags)) {
+		if (timer_pending(&qh->unreserve_timer))
+			return;
 	}
-	return ret;
+
+	/*
+	 * Might be no more unreserve pending if:
+	 * - We started executing but didn't get the lock yet.
+	 * - A new reservation came in, but cancel didn't take effect
+	 *   because we already started executing.
+	 *
+	 * We can't put this in the loop above because unreserve_pending needs
+	 * to be accessed under lock, so we can only check it once we got the
+	 * lock.
+	 */
+	if (qh->unreserve_pending)
+		dwc2_do_unreserve(hsotg, qh);
+
+	spin_unlock_irqrestore(&hsotg->lock, flags);
 }
 
 /**
@@ -695,6 +491,210 @@ static void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg,
 }
 
 /**
+ * dwc2_qh_init() - Initializes a QH structure
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller
+ * @qh:    The QH to init
+ * @urb:   Holds the information about the device/endpoint needed to initialize
+ *         the QH
+ */
+#define SCHEDULE_SLOP 10
+static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
+			 struct dwc2_hcd_urb *urb)
+{
+	int dev_speed, hub_addr, hub_port;
+	char *speed, *type;
+
+	dev_vdbg(hsotg->dev, "%s()\n", __func__);
+
+	/* Initialize QH */
+	qh->hsotg = hsotg;
+	setup_timer(&qh->unreserve_timer, dwc2_unreserve_timer_fn,
+		    (unsigned long)qh);
+	qh->ep_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
+	qh->ep_is_in = dwc2_hcd_is_pipe_in(&urb->pipe_info) ? 1 : 0;
+
+	qh->data_toggle = DWC2_HC_PID_DATA0;
+	qh->maxp = dwc2_hcd_get_mps(&urb->pipe_info);
+	INIT_LIST_HEAD(&qh->qtd_list);
+	INIT_LIST_HEAD(&qh->qh_list_entry);
+
+	/* FS/LS Endpoint on HS Hub, NOT virtual root hub */
+	dev_speed = dwc2_host_get_speed(hsotg, urb->priv);
+
+	dwc2_host_hub_info(hsotg, urb->priv, &hub_addr, &hub_port);
+
+	if ((dev_speed == USB_SPEED_LOW || dev_speed == USB_SPEED_FULL) &&
+	    hub_addr != 0 && hub_addr != 1) {
+		dev_vdbg(hsotg->dev,
+			 "QH init: EP %d: TT found at hub addr %d, for port %d\n",
+			 dwc2_hcd_get_ep_num(&urb->pipe_info), hub_addr,
+			 hub_port);
+		qh->do_split = 1;
+	}
+
+	if (qh->ep_type == USB_ENDPOINT_XFER_INT ||
+	    qh->ep_type == USB_ENDPOINT_XFER_ISOC) {
+		/* Compute scheduling parameters once and save them */
+		u32 hprt, prtspd;
+
+		/* Todo: Account for split transfers in the bus time */
+		int bytecount =
+			dwc2_hb_mult(qh->maxp) * dwc2_max_packet(qh->maxp);
+
+		qh->host_us = NS_TO_US(usb_calc_bus_time(qh->do_split ?
+			      USB_SPEED_HIGH : dev_speed, qh->ep_is_in,
+			      qh->ep_type == USB_ENDPOINT_XFER_ISOC,
+			      bytecount));
+
+		/* Ensure frame_number corresponds to the reality */
+		hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
+		/* Start in a slightly future (micro)frame */
+		qh->next_active_frame = dwc2_frame_num_inc(hsotg->frame_number,
+						     SCHEDULE_SLOP);
+		qh->host_interval = urb->interval;
+		dwc2_sch_dbg(hsotg, "QH=%p init nxt=%04x, fn=%04x, int=%#x\n",
+			     qh, qh->next_active_frame, hsotg->frame_number,
+			     qh->host_interval);
+#if 0
+		/* Increase interrupt polling rate for debugging */
+		if (qh->ep_type == USB_ENDPOINT_XFER_INT)
+			qh->host_interval = 8;
+#endif
+		hprt = dwc2_readl(hsotg->regs + HPRT0);
+		prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
+		if (prtspd == HPRT0_SPD_HIGH_SPEED &&
+		    (dev_speed == USB_SPEED_LOW ||
+		     dev_speed == USB_SPEED_FULL)) {
+			qh->host_interval *= 8;
+			qh->next_active_frame |= 0x7;
+			qh->start_split_frame = qh->next_active_frame;
+			dwc2_sch_dbg(hsotg,
+				     "QH=%p init*8 nxt=%04x, fn=%04x, int=%#x\n",
+				     qh, qh->next_active_frame,
+				     hsotg->frame_number, qh->host_interval);
+
+		}
+		dev_dbg(hsotg->dev, "interval=%d\n", qh->host_interval);
+	}
+
+	dev_vdbg(hsotg->dev, "DWC OTG HCD QH Initialized\n");
+	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - qh = %p\n", qh);
+	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Device Address = %d\n",
+		 dwc2_hcd_get_dev_addr(&urb->pipe_info));
+	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Endpoint %d, %s\n",
+		 dwc2_hcd_get_ep_num(&urb->pipe_info),
+		 dwc2_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT");
+
+	qh->dev_speed = dev_speed;
+
+	switch (dev_speed) {
+	case USB_SPEED_LOW:
+		speed = "low";
+		break;
+	case USB_SPEED_FULL:
+		speed = "full";
+		break;
+	case USB_SPEED_HIGH:
+		speed = "high";
+		break;
+	default:
+		speed = "?";
+		break;
+	}
+	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Speed = %s\n", speed);
+
+	switch (qh->ep_type) {
+	case USB_ENDPOINT_XFER_ISOC:
+		type = "isochronous";
+		break;
+	case USB_ENDPOINT_XFER_INT:
+		type = "interrupt";
+		break;
+	case USB_ENDPOINT_XFER_CONTROL:
+		type = "control";
+		break;
+	case USB_ENDPOINT_XFER_BULK:
+		type = "bulk";
+		break;
+	default:
+		type = "?";
+		break;
+	}
+
+	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Type = %s\n", type);
+
+	if (qh->ep_type == USB_ENDPOINT_XFER_INT) {
+		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - usecs = %d\n",
+			 qh->host_us);
+		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - interval = %d\n",
+			 qh->host_interval);
+	}
+}
+
+/**
+ * dwc2_hcd_qh_create() - Allocates and initializes a QH
+ *
+ * @hsotg:        The HCD state structure for the DWC OTG controller
+ * @urb:          Holds the information about the device/endpoint needed
+ *                to initialize the QH
+ * @atomic_alloc: Flag to do atomic allocation if needed
+ *
+ * Return: Pointer to the newly allocated QH, or NULL on error
+ */
+struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
+					  struct dwc2_hcd_urb *urb,
+					  gfp_t mem_flags)
+{
+	struct dwc2_qh *qh;
+
+	if (!urb->priv)
+		return NULL;
+
+	/* Allocate memory */
+	qh = kzalloc(sizeof(*qh), mem_flags);
+	if (!qh)
+		return NULL;
+
+	dwc2_qh_init(hsotg, qh, urb);
+
+	if (hsotg->core_params->dma_desc_enable > 0 &&
+	    dwc2_hcd_qh_init_ddma(hsotg, qh, mem_flags) < 0) {
+		dwc2_hcd_qh_free(hsotg, qh);
+		return NULL;
+	}
+
+	return qh;
+}
+
+/**
+ * dwc2_hcd_qh_free() - Frees the QH
+ *
+ * @hsotg: HCD instance
+ * @qh:    The QH to free
+ *
+ * QH should already be removed from the list. QTD list should already be empty
+ * if called from URB Dequeue.
+ *
+ * Must NOT be called with interrupt disabled or spinlock held
+ */
+void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+	/* Make sure any unreserve work is finished. */
+	if (del_timer_sync(&qh->unreserve_timer)) {
+		unsigned long flags;
+
+		spin_lock_irqsave(&hsotg->lock, flags);
+		dwc2_do_unreserve(hsotg, qh);
+		spin_unlock_irqrestore(&hsotg->lock, flags);
+	}
+
+	if (qh->desc_list)
+		dwc2_hcd_qh_free_ddma(hsotg, qh);
+	kfree(qh);
+}
+
+/**
  * dwc2_hcd_qh_add() - Adds a QH to either the non periodic or periodic
  * schedule if it is not already in the schedule. If the QH is already in
  * the schedule, no action is taken.
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Wed Jan 20 21:04:32 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Wed, 20 Jan 2016 21:04:32 -0800
Subject: [PATCH v4 21/21] usb: dwc2: host: If using uframe scheduler,
 end splits better
In-Reply-To: <1453352672-27890-1-git-send-email-dianders@chromium.org>
References: <1453352672-27890-1-git-send-email-dianders@chromium.org>
Message-ID: <1453352672-27890-22-git-send-email-dianders@chromium.org>

The microframe scheduler figured out exactly how many transfers we need
for a split transaction.  Let's use this knowledge to know when to end
things.

Without this I found that certain devices would just keep responding
with tons of NYET resonses on their INT_IN endpoint.  These would just
keep going and going and eventually we'd decide to terminate the
transfer (because the whole frame changed), but by that time the
scheduler would decide that we "missed" the start of the next transfer.
I can also imagine that if we blow past the end of our scheduled time we
may mess up other things that were scheduled to happen.

No known test cases are improved by this patch except that the scheduler
code doesn't yell about MISSES constantly anymore.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v4:
- If using uframe scheduler, end splits better new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/hcd_intr.c | 48 +++++++++++++++++++++++++++++++++++++++------
 1 file changed, 42 insertions(+), 6 deletions(-)

diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index 1875e4590a3c..2519e1bdad1a 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -1353,14 +1353,50 @@ static void dwc2_hc_nyet_intr(struct dwc2_hsotg *hsotg,
 
 		if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
 		    chan->ep_type == USB_ENDPOINT_XFER_ISOC) {
-			int frnum = dwc2_hcd_get_frame_number(hsotg);
+			struct dwc2_qh *qh = chan->qh;
+			bool past_end;
+
+			if (hsotg->core_params->uframe_sched <= 0) {
+				int frnum = dwc2_hcd_get_frame_number(hsotg);
+
+				/* Don't have num_hs_transfers; simple logic */
+				past_end = dwc2_full_frame_num(frnum) !=
+				     dwc2_full_frame_num(qh->next_active_frame);
+			} else {
+				int end_frnum;
 
-			if (dwc2_full_frame_num(frnum) !=
-			    dwc2_full_frame_num(chan->qh->next_active_frame)) {
 				/*
-				 * No longer in the same full speed frame.
-				 * Treat this as a transaction error.
-				 */
+				* Figure out the end frame based on schedule.
+				*
+				* We don't want to go on trying again and again
+				* forever.  Let's stop when we've done all the
+				* transfers that were scheduled.
+				*
+				* We're going to be comparing start_active_frame
+				* and next_active_frame, both of which are 1
+				* before the time the packet goes on the wire,
+				* so that cancels out.  Basically if had 1
+				* transfer and we saw 1 NYET then we're done.
+				* We're getting a NYET here so if next >=
+				* (start + num_transfers) we're done. The
+				* complexity is that for all but ISOC_OUT we
+				* skip one slot.
+				*/
+				end_frnum = dwc2_frame_num_inc(
+					qh->start_active_frame,
+					qh->num_hs_transfers);
+
+				if (qh->ep_type != USB_ENDPOINT_XFER_ISOC ||
+				    qh->ep_is_in)
+					end_frnum =
+					       dwc2_frame_num_inc(end_frnum, 1);
+
+				past_end = dwc2_frame_num_le(
+					end_frnum, qh->next_active_frame);
+			}
+
+			if (past_end) {
+				/* Treat this as a transaction error. */
 #if 0
 				/*
 				 * Todo: Fix system performance so this can
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Wed Jan 20 21:04:30 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Wed, 20 Jan 2016 21:04:30 -0800
Subject: [PATCH v4 19/21] usb: dwc2: host: Properly set even/odd frame
In-Reply-To: <1453352672-27890-1-git-send-email-dianders@chromium.org>
References: <1453352672-27890-1-git-send-email-dianders@chromium.org>
Message-ID: <1453352672-27890-20-git-send-email-dianders@chromium.org>

When setting up ISO and INT transfers dwc2 needs to specify whether the
transfer is for an even or an odd frame (or microframe if the controller
is running in high speed mode).

The controller appears to use this as a simple way to figure out if a
transfer should happen right away (in the current microframe) or should
happen at the start of the next microframe.  Said another way:

- If you set "odd" and the current frame number is odd it appears that
  the controller will try to transfer right away.  Same thing if you set
  "even" and the current frame number is even.
- If the oddness you set and the oddness of the frame number are
  _different_, the transfer will be delayed until the frame number
  changes.

As I understand it, the above technique allows you to plan ahead of time
where possible by always working on the next frame.  ...but it still
allows you to properly respond immediately to things that happened in
the previous frame.

The old dwc2_hc_set_even_odd_frame() didn't really handle this concept.
It always looked at the frame number and setup the transfer to happen in
the next frame.  In some cases that meant that certain transactions
would be transferred in the wrong frame.

We'll try our best to set the even / odd to do the transfer in the
scheduled frame.  If that fails then we'll do an ugly "schedule ASAP".
We'll also modify the scheduler code to handle this and not try to
schedule a second transfer for the same frame.

Note that this change relies on the work to redo the microframe
scheduler.  It can work atop ("usb: dwc2: host: Manage frame nums better
in scheduler") but it works even better after ("usb: dwc2: host: Totally
redo the microframe scheduler").

With this change my stressful USB test (USB webcam + USB audio +
keyboards) has less audio crackling than before.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v4:
- Properly set even/odd frame new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/core.c      | 92 +++++++++++++++++++++++++++++++++++++++++++-
 drivers/usb/dwc2/hcd_queue.c | 11 +++++-
 2 files changed, 100 insertions(+), 3 deletions(-)

diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index 29f4bfe5970f..edc005e6a3b9 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -1703,9 +1703,97 @@ static void dwc2_hc_set_even_odd_frame(struct dwc2_hsotg *hsotg,
 {
 	if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
 	    chan->ep_type == USB_ENDPOINT_XFER_ISOC) {
-		/* 1 if _next_ frame is odd, 0 if it's even */
-		if (!(dwc2_hcd_get_frame_number(hsotg) & 0x1))
+		int host_speed;
+		int xfer_ns;
+		int xfer_us;
+		int bytes_in_fifo;
+		u16 fifo_space;
+		u16 frame_number;
+		u16 wire_frame;
+
+		/*
+		 * Try to figure out if we're an even or odd frame. If we set
+		 * even and the current frame number is even the the transfer
+		 * will happen immediately.  Similar if both are odd. If one is
+		 * even and the other is odd then the transfer will happen when
+		 * the frame number ticks.
+		 *
+		 * There's a bit of a balancing act to get this right.
+		 * Sometimes we may want to send data in the current frame (AK
+		 * right away).  We might want to do this if the frame number
+		 * _just_ ticked, but we might also want to do this in order
+		 * to continue a split transaction that happened late in a
+		 * microframe (so we didn't know to queue the next transfer
+		 * until the frame number had ticked).  The problem is that we
+		 * need a lot of knowledge to know if there's actually still
+		 * time to send things or if it would be better to wait until
+		 * the next frame.
+		 *
+		 * We can look at how much time is left in the current frame
+		 * and make a guess about whether we'll have time to transfer.
+		 * We'll do that.
+		 */
+
+		/* Get speed host is running at */
+		host_speed = (chan->speed != USB_SPEED_HIGH &&
+			      !chan->do_split) ? chan->speed : USB_SPEED_HIGH;
+
+		/* See how many bytes are in the periodic FIFO right now */
+		fifo_space = (dwc2_readl(hsotg->regs + HPTXSTS) &
+			      TXSTS_FSPCAVAIL_MASK) >> TXSTS_FSPCAVAIL_SHIFT;
+		bytes_in_fifo = sizeof(u32) *
+				(hsotg->core_params->host_perio_tx_fifo_size -
+				 fifo_space);
+
+		/*
+		 * Roughly estimate bus time for everything in the periodic
+		 * queue + our new transfer.  This is "rough" because we're
+		 * using a function that makes takes into account IN/OUT
+		 * and INT/ISO and we're just slamming in one value for all
+		 * transfers.  This should be an over-estimate and that should
+		 * be OK, but we can probably tighten it.
+		 */
+		xfer_ns = usb_calc_bus_time(host_speed, false, false,
+					    chan->xfer_len + bytes_in_fifo);
+		xfer_us = NS_TO_US(xfer_ns);
+
+		/* See what frame number we'll be at by the time we finish */
+		frame_number = dwc2_hcd_get_future_frame_number(hsotg, xfer_us);
+
+		/* This is when we were scheduled to be on the wire */
+		wire_frame = dwc2_frame_num_inc(chan->qh->next_active_frame, 1);
+
+		/*
+		 * If we'd finish _after_ the frame we're scheduled in then
+		 * it's hopeless.  Just schedule right away and hope for the
+		 * best.  Note that it _might_ be wise to call back into the
+		 * scheduler to pick a better frame, but this is better than
+		 * nothing.
+		 */
+		if (dwc2_frame_num_gt(frame_number, wire_frame)) {
+			dwc2_sch_vdbg(hsotg,
+				      "QH=%p EO MISS fr=%04x=>%04x (%+d)\n",
+				      chan->qh, wire_frame, frame_number,
+				      dwc2_frame_num_dec(frame_number,
+							 wire_frame));
+			wire_frame = frame_number;
+
+			/*
+			 * We picked a different frame number; communicate this
+			 * back to the scheduler so it doesn't try to schedule
+			 * another in the same frame.
+			 *
+			 * Remember that next_active_frame is 1 before the wire
+			 * frame.
+			 */
+			chan->qh->next_active_frame =
+				dwc2_frame_num_dec(frame_number, 1);
+		}
+
+		if (wire_frame & 1)
 			*hcchar |= HCCHAR_ODDFRM;
+		else
+			*hcchar &= ~HCCHAR_ODDFRM;
 	}
 }
 
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 2b6e57ad2661..7f54eeb2bf5f 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -983,6 +983,14 @@ static int dwc2_next_periodic_start(struct dwc2_hsotg *hsotg,
 	 *   and next_active_frame are always 1 frame before we want things
 	 *   to be active and we assume we can still get scheduled in the
 	 *   current frame number.
+	 * - It's possible for start_active_frame (now incremented) to be
+	 *   next_active_frame if we got an EO MISS (even_odd miss) which
+	 *   basically means that we detected there wasn't enough time for
+	 *   the last packet and dwc2_hc_set_even_odd_frame() rescheduled us
+	 *   at the last second.  We want to make sure we don't schedule
+	 *   another transfer for the same frame.  My test webcam doesn't seem
+	 *   terribly upset by missing a transfer but really doesn't like when
+	 *   we do two transfers in the same frame.
 	 * - Some misses are expected.  Specifically, in order to work
 	 *   perfectly dwc2 really needs quite spectacular interrupt latency
 	 *   requirements.  It needs to be able to handle its interrupts
@@ -993,7 +1001,8 @@ static int dwc2_next_periodic_start(struct dwc2_hsotg *hsotg,
 	 *   guarantee that a system will have interrupt latency < 125 us, so
 	 *   we have to be robust to some misses.
 	 */
-	if (dwc2_frame_num_gt(prev_frame_number, qh->start_active_frame)) {
+	if (qh->start_active_frame == qh->next_active_frame ||
+	    dwc2_frame_num_gt(prev_frame_number, qh->start_active_frame)) {
 		u16 ideal_start = qh->start_active_frame;
 
 		/* Adjust interval as per gcd with plan length. */
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Wed Jan 20 21:04:27 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Wed, 20 Jan 2016 21:04:27 -0800
Subject: [PATCH v4 16/21] usb: dwc2: host: Manage frame nums better in
 scheduler
In-Reply-To: <1453352672-27890-1-git-send-email-dianders@chromium.org>
References: <1453352672-27890-1-git-send-email-dianders@chromium.org>
Message-ID: <1453352672-27890-17-git-send-email-dianders@chromium.org>

The dwc2 scheduler (contained in hcd_queue.c) was a bit confusing in the
way it initted / kept track of which frames a QH was going to be active
in.  Let's clean things up a little bit in preparation for a rewrite of
the microframe scheduler.

Specifically:
* Old code would pick a frame number in dwc2_qh_init() and would try to
  pick it "in a slightly future (micro)frame".  As far as I can tell the
  reason for this was that there was a delay between dwc2_qh_init() and
  when we actually wanted to dwc2_hcd_qh_add().  ...but apparently this
  attempt to be slightly in the future wasn't enough because
  dwc2_hcd_qh_add() then had code to reset things if the frame _wasn't_
  in the future.  There's no reason not to just pick the frame later.
  For non-periodic QH we now pick the frame in dwc2_hcd_qh_add().  For
  periodic QH we pick the frame at dwc2_schedule_periodic() time.
* The old "dwc2_qh_init() actually assigned to "hsotg->frame_number".
  This doesn't seem like a great idea since that variable is supposed to
  be used to keep track of which SOF the interrupt handler has seen.
  Let's be clean: anyone who wants the current frame number (instead of
  the one as of the last interrupt) should ask for it.
* The old code wasn't terribly consistent about trying to use the frame
  that the microframe scheduler assigned to it.  In
  dwc2_sched_periodic_split() when it was scheduling the first frame it
  always "ORed" in 0x7 (!).  Since the frame goes on the wire 1 uFrame
  after next_active_frame it meant that the SSPLIT would always try for
  uFrame 0 and the transaction would happen on the low speed bus during
  uFrame 1.  This is irregardless of what the microframe scheduler
  said.
* The old code assumed it would get called to schedule the next in a
  periodic split very quickly.  That is if next_active_frame was
  0 (transfer on wire in uFrame 1) it assumed it was getting called to
  schedule the next uFrame during uFrame 1 too (so it could queue
  something up for uFrame 2).  It should be possible to actually queue
  something up for uFrame 2 while in uFrame 2 (AKA queue up ASAP).  To
  do this, code needs to look at the previously scheduled frame when
  deciding when to next be active, not look at the current frame number.
* If there was no microframe scheduler, the old code would check for
  whether we should be active using "qh->next_active_frame ==
  frame_number".  This seemed like a race waiting to happen.  ...plus
  there's no way that you wouldn't want to schedule if next_active_frame
  was actually less than frame number.

Note that this change doesn't make 100% sense on its own since it's
expecting some sanity in the frame numbers assigned by the microframe
scheduler and (as per the future patch which rewries it) I think that
the current microframe scheduler is quite insane.  However, it seems
like splitting this up from the microframe scheduler patch makes things
into smaller chunks and hopefully adds to clarity rather than reduces
it.  The two patches could certainly be squashed.  Not that in the very
least, I don't see any obvious bad behavior introduced with just this
patch.

I've attempted to keep the config parameter to disable the microframe
scheduler in tact in this change, though I'm not sure it's worth it.
Obviously the code is touched a lot so it's possible I regressed
something when the microframe scheduler is disabled, though I did some
basic testing and it seemed to work OK.  I'm still not 100% sure why you
wouldn't want the microframe scheduler (presuming it works), so maybe a
future patch (or a future version of this patch?) could remove that
parameter.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v4:
- Manage frame nums better in scheduler new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/hcd.h       |  10 +-
 drivers/usb/dwc2/hcd_queue.c | 355 ++++++++++++++++++++++++++++++++-----------
 2 files changed, 273 insertions(+), 92 deletions(-)

diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index 10c35585a2bd..fd266ac53a28 100644
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -244,8 +244,11 @@ enum dwc2_transaction_type {
  *                      the bus.  We'll move the qh to active here.  If the
  *                      host is in high speed mode this will be a uframe.  If
  *                      the host is in low speed mode this will be a full frame.
+ * @start_active_frame: If we are partway through a split transfer, this will be
+ *			what next_active_frame was when we started.  Otherwise
+ *			it should always be the same as next_active_frame.
+ * @assigned_uframe:    The uframe (0 -7) assigned by dwc2_find_uframe().
  * @frame_usecs:        Internal variable used by the microframe scheduler
- * @start_split_frame:  (Micro)frame at which last start split was initialized
  * @ntd:                Actual number of transfer descriptors in a list
  * @qtd_list:           List of QTDs for this QH
  * @channel:            Host channel currently processing transfers for this QH
@@ -279,8 +282,9 @@ struct dwc2_qh {
 	u16 host_us;
 	u16 host_interval;
 	u16 next_active_frame;
+	u16 start_active_frame;
+	u16 assigned_uframe;
 	u16 frame_usecs[8];
-	u16 start_split_frame;
 	u16 ntd;
 	struct list_head qtd_list;
 	struct dwc2_host_chan *channel;
@@ -746,7 +750,7 @@ do {									\
 	_qtd_ = list_entry((_qh_)->qtd_list.next, struct dwc2_qtd,	\
 			   qtd_list_entry);				\
 	if (usb_pipeint(_qtd_->urb->pipe) &&				\
-	    (_qh_)->start_split_frame != 0 && !_qtd_->complete_split) {	\
+	    (_qh_)->start_active_frame != 0 && !_qtd_->complete_split) { \
 		_hfnum_.d32 = dwc2_readl((_hcd_)->regs + HFNUM);	\
 		switch (_hfnum_.b.frnum & 0x7) {			\
 		case 7:							\
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 9ce407e5017d..f235402dfc5e 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -38,6 +38,7 @@
  * This file contains the functions to manage Queue Heads and Queue
  * Transfer Descriptors for Host mode
  */
+#include <linux/gcd.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/spinlock.h>
@@ -245,6 +246,96 @@ static int dwc2_find_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 }
 
 /**
+ * dwc2_pick_first_frame() - Choose 1st frame for qh that's already scheduled
+ *
+ * Takes a qh that has already been scheduled (which means we know we have the
+ * bandwdith reserved for us) and set the next_active_frame and the
+ * start_active_frame.
+ *
+ * This is expected to be called on qh's that weren't previously actively
+ * running.  It just picks the next frame that we can fit into without any
+ * thought about the past.
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller
+ * @qh:    QH for a periodic endpoint
+ *
+ */
+static void dwc2_pick_first_frame(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+	u16 frame_number;
+	u16 earliest_frame;
+	u16 next_active_frame;
+	u16 interval;
+
+	/*
+	 * Use the real frame number rather than the cached value as of the
+	 * last SOF to give us a little extra slop.
+	 */
+	frame_number = dwc2_hcd_get_frame_number(hsotg);
+
+	/*
+	 * We wouldn't want to start any earlier than the next frame just in
+	 * case the frame number ticks as we're doing this calculation.
+	 *
+	 * NOTE: if we could quantify how long till we actually get scheduled
+	 * we might be able to avoid the "+ 1" by looking at the upper part of
+	 * HFNUM (the FRREM field).  For now we'll just use the + 1 though.
+	 */
+	earliest_frame = dwc2_frame_num_inc(frame_number, 1);
+	next_active_frame = earliest_frame;
+
+	/* Get the "no microframe schduler" out of the way... */
+	if (hsotg->core_params->uframe_sched <= 0) {
+		if (qh->do_split)
+			/* Splits are active at microframe 0 minus 1 */
+			next_active_frame |= 0x7;
+		goto exit;
+	}
+
+	/* Adjust interval as per high speed schedule which has 8 uFrame */
+	interval = gcd(qh->host_interval, 8);
+
+	/*
+	 * We know interval must divide (HFNUM_MAX_FRNUM + 1) now that we've
+	 * done the gcd(), so it's safe to move to the beginning of the current
+	 * interval like this.
+	 *
+	 * After this we might be before earliest_frame, but don't worry,
+	 * we'll fix it...
+	 */
+	next_active_frame = (next_active_frame / interval) * interval;
+
+	/*
+	 * Actually choose to start at the frame number we've been
+	 * scheduled for.
+	 */
+	next_active_frame = dwc2_frame_num_inc(next_active_frame,
+					       qh->assigned_uframe);
+
+	/*
+	 * We actually need 1 frame before since the next_active_frame is
+	 * the frame number we'll be put on the ready list and we won't be on
+	 * the bus until 1 frame later.
+	 */
+	next_active_frame = dwc2_frame_num_dec(next_active_frame, 1);
+
+	/*
+	 * By now we might actually be before the earliest_frame.  Let's move
+	 * up intervals until we're not.
+	 */
+	while (dwc2_frame_num_gt(earliest_frame, next_active_frame))
+		next_active_frame = dwc2_frame_num_inc(next_active_frame,
+						       interval);
+
+exit:
+	qh->next_active_frame = next_active_frame;
+	qh->start_active_frame = next_active_frame;
+
+	dwc2_sch_vdbg(hsotg, "QH=%p First fn=%04x nxt=%04x\n",
+		     qh, frame_number, qh->next_active_frame);
+}
+
+/**
  * dwc2_do_reserve() - Make a periodic reservation
  *
  * Try to allocate space in the periodic schedule.  Depending on parameters
@@ -260,25 +351,9 @@ static int dwc2_do_reserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 	int status;
 
 	if (hsotg->core_params->uframe_sched > 0) {
-		int frame = -1;
-
 		status = dwc2_find_uframe(hsotg, qh);
-		if (status == 0)
-			frame = 7;
-		else if (status > 0)
-			frame = status - 1;
-
-		/* Set the new frame up */
-		if (frame >= 0) {
-			qh->next_active_frame &= ~0x7;
-			qh->next_active_frame |= (frame & 7);
-			dwc2_sch_dbg(hsotg,
-				     "QH=%p sched_p nxt=%04x, uf=%d\n",
-				     qh, qh->next_active_frame, frame);
-		}
-
-		if (status > 0)
-			status = 0;
+		if (status >= 0)
+			qh->assigned_uframe = status;
 	} else {
 		status = dwc2_periodic_channel_available(hsotg);
 		if (status) {
@@ -305,6 +380,8 @@ static int dwc2_do_reserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 	/* Update claimed usecs per (micro)frame */
 	hsotg->periodic_usecs += qh->host_us;
 
+	dwc2_pick_first_frame(hsotg, qh);
+
 	return 0;
 }
 
@@ -460,6 +537,14 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		status = dwc2_do_reserve(hsotg, qh);
 		if (status)
 			return status;
+	} else {
+		/*
+		 * It might have been a while, so make sure that frame_number
+		 * is still good.  Note: we could also try to use the similar
+		 * dwc2_next_periodic_start() but that schedules much more
+		 * tightly and we might need to hurry and queue things up.
+		 */
+		dwc2_pick_first_frame(hsotg, qh);
 	}
 
 	qh->unreserve_pending = 0;
@@ -520,7 +605,6 @@ static void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg,
  * @urb:   Holds the information about the device/endpoint needed to initialize
  *         the QH
  */
-#define SCHEDULE_SLOP 10
 static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 			 struct dwc2_hcd_urb *urb)
 {
@@ -569,11 +653,6 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 			      qh->ep_type == USB_ENDPOINT_XFER_ISOC,
 			      bytecount));
 
-		/* Ensure frame_number corresponds to the reality */
-		hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
-		/* Start in a slightly future (micro)frame */
-		qh->next_active_frame = dwc2_frame_num_inc(hsotg->frame_number,
-						     SCHEDULE_SLOP);
 		qh->host_interval = urb->interval;
 		dwc2_sch_dbg(hsotg, "QH=%p init nxt=%04x, fn=%04x, int=%#x\n",
 			     qh, qh->next_active_frame, hsotg->frame_number,
@@ -589,8 +668,6 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 		    (dev_speed == USB_SPEED_LOW ||
 		     dev_speed == USB_SPEED_FULL)) {
 			qh->host_interval *= 8;
-			qh->next_active_frame |= 0x7;
-			qh->start_split_frame = qh->next_active_frame;
 			dwc2_sch_dbg(hsotg,
 				     "QH=%p init*8 nxt=%04x, fn=%04x, int=%#x\n",
 				     qh, qh->next_active_frame,
@@ -738,22 +815,12 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		/* QH already in a schedule */
 		return 0;
 
-	if (!dwc2_frame_num_le(qh->next_active_frame, hsotg->frame_number) &&
-			!hsotg->frame_number) {
-		u16 new_frame;
-
-		dev_dbg(hsotg->dev,
-				"reset frame number counter\n");
-		new_frame = dwc2_frame_num_inc(hsotg->frame_number,
-				SCHEDULE_SLOP);
-
-		dwc2_sch_vdbg(hsotg, "QH=%p reset nxt=%04x=>%04x\n",
-			      qh, qh->next_active_frame, new_frame);
-		qh->next_active_frame = new_frame;
-	}
-
 	/* Add the new QH to the appropriate schedule */
 	if (dwc2_qh_is_non_per(qh)) {
+		/* Schedule right away */
+		qh->start_active_frame = hsotg->frame_number;
+		qh->next_active_frame = qh->start_active_frame;
+
 		/* Always start in inactive schedule */
 		list_add_tail(&qh->qh_list_entry,
 			      &hsotg->non_periodic_sched_inactive);
@@ -807,46 +874,145 @@ void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 	}
 }
 
-/*
- * Schedule the next continuing periodic split transfer
+/**
+ * dwc2_next_for_periodic_split() - Set next_active_frame midway thru a split.
+ *
+ * This is called for setting next_active_frame for periodic splits for all but
+ * the first packet of the split.  Confusing?  I thought so...
+ *
+ * Periodic splits are single low/full speed transfers that we end up splitting
+ * up into several high speed transfers.  They always fit into one full (1 ms)
+ * frame but might be split over several microframes (125 us each).  We to put
+ * each of the parts on a very specific high speed frame.
+ *
+ * This function figures out where the next active uFrame needs to be.
+ *
+ * @hsotg:        The HCD state structure
+ * @qh:           QH for the periodic transfer.
+ * @frame_number: The current frame number.
+ *
+ * Return: number missed by (or 0 if we didn't miss).
  */
-static void dwc2_sched_periodic_split(struct dwc2_hsotg *hsotg,
-				      struct dwc2_qh *qh, u16 frame_number,
-				      int sched_next_periodic_split)
+static int dwc2_next_for_periodic_split(struct dwc2_hsotg *hsotg,
+					 struct dwc2_qh *qh, u16 frame_number)
 {
-	u16 incr;
 	u16 old_frame = qh->next_active_frame;
+	u16 prev_frame_number = dwc2_frame_num_dec(frame_number, 1);
+	int missed = 0;
+	u16 incr;
+
+	/*
+	 * Basically: increment 1 normally, but 2 right after the start split
+	 * (except for ISOC out).
+	 */
+	if (old_frame == qh->start_active_frame &&
+	    !(qh->ep_type == USB_ENDPOINT_XFER_ISOC && !qh->ep_is_in))
+		incr = 2;
+	else
+		incr = 1;
+
+	qh->next_active_frame = dwc2_frame_num_inc(old_frame, incr);
 
-	if (sched_next_periodic_split) {
+	/*
+	 * Note that it's OK for frame_number to be 1 frame past
+	 * next_active_frame.  Remember that next_active_frame is supposed to
+	 * be 1 frame _before_ when we want to be scheduled.  If we're 1 frame
+	 * past it just means schedule ASAP.
+	 *
+	 * It's _not_ OK, however, if we're more than one frame past.
+	 */
+	if (dwc2_frame_num_gt(prev_frame_number, qh->next_active_frame)) {
+		/*
+		 * OOPS, we missed.  That's actually pretty bad since
+		 * the hub will be unhappy; try ASAP I guess.
+		 */
+		missed = dwc2_frame_num_dec(prev_frame_number,
+					    qh->next_active_frame);
 		qh->next_active_frame = frame_number;
-		incr = dwc2_frame_num_inc(qh->start_split_frame, 1);
-		if (dwc2_frame_num_le(frame_number, incr)) {
-			/*
-			 * Allow one frame to elapse after start split
-			 * microframe before scheduling complete split, but
-			 * DON'T if we are doing the next start split in the
-			 * same frame for an ISOC out
-			 */
-			if (qh->ep_type != USB_ENDPOINT_XFER_ISOC ||
-			    qh->ep_is_in != 0) {
-				qh->next_active_frame = dwc2_frame_num_inc(
-					qh->next_active_frame, 1);
-			}
-		}
-	} else {
-		qh->next_active_frame =
-			dwc2_frame_num_inc(qh->start_split_frame,
-					   qh->host_interval);
-		if (dwc2_frame_num_le(qh->next_active_frame, frame_number))
-			qh->next_active_frame = frame_number;
-		qh->next_active_frame |= 0x7;
-		qh->start_split_frame = qh->next_active_frame;
 	}
 
-	dwc2_sch_vdbg(hsotg, "QH=%p next(%d) fn=%04x, nxt=%04x=>%04x (%+d)\n",
-		      qh, sched_next_periodic_split, frame_number, old_frame,
-		      qh->next_active_frame,
-		      dwc2_frame_num_dec(qh->next_active_frame, old_frame));
+	return missed;
+}
+
+/**
+ * dwc2_next_periodic_start() - Set next_active_frame for next transfer start
+ *
+ * This is called for setting next_active_frame for a periodic transfer for
+ * all cases other than midway through a periodic split.  This will also update
+ * start_active_frame.
+ *
+ * Since we _always_ keep start_active_frame as the start of the previous
+ * transfer this is normally pretty easy: we just add our interval to
+ * start_active_frame and we've got our answer.
+ *
+ * The tricks come into play if we miss.  In that case we'll look for the next
+ * slot we can fit into.
+ *
+ * @hsotg:        The HCD state structure
+ * @qh:           QH for the periodic transfer.
+ * @frame_number: The current frame number.
+ *
+ * Return: number missed by (or 0 if we didn't miss).
+ */
+static int dwc2_next_periodic_start(struct dwc2_hsotg *hsotg,
+				     struct dwc2_qh *qh, u16 frame_number)
+{
+	int missed = 0;
+	u16 interval = qh->host_interval;
+	u16 prev_frame_number = dwc2_frame_num_dec(frame_number, 1);
+
+	qh->start_active_frame = dwc2_frame_num_inc(qh->start_active_frame,
+						    interval);
+
+	/*
+	 * The dwc2_frame_num_gt() function used below won't work terribly well
+	 * with if we just incremented by a really large intervals since the
+	 * frame counter only goes to 0x3fff.  It's terribly unlikely that we
+	 * will have missed in this case anyway.  Just go to exit.  If we want
+	 * to try to do better we'll need to keep track of a bigger counter
+	 * somewhere in the driver and handle overflows.
+	 */
+	if (interval >= 0x1000)
+		goto exit;
+
+	/*
+	 * Test for misses, which is when it's too late to schedule.
+	 *
+	 * A few things to note:
+	 * - We compare against prev_frame_number since start_active_frame
+	 *   and next_active_frame are always 1 frame before we want things
+	 *   to be active and we assume we can still get scheduled in the
+	 *   current frame number.
+	 * - Some misses are expected.  Specifically, in order to work
+	 *   perfectly dwc2 really needs quite spectacular interrupt latency
+	 *   requirements.  It needs to be able to handle its interrupts
+	 *   completely within 125 us of them being asserted. That not only
+	 *   means that the dwc2 interrupt handler needs to be fast but it
+	 *   means that nothing else in the system has to block dwc2 for a long
+	 *   time.  We can help with the dwc2 parts of this, but it's hard to
+	 *   guarantee that a system will have interrupt latency < 125 us, so
+	 *   we have to be robust to some misses.
+	 */
+	if (dwc2_frame_num_gt(prev_frame_number, qh->start_active_frame)) {
+		u16 ideal_start = qh->start_active_frame;
+
+		/* Adjust interval as per gcd with plan length. */
+		interval = gcd(interval, 8);
+
+		do {
+			qh->start_active_frame = dwc2_frame_num_inc(
+				qh->start_active_frame, interval);
+		} while (dwc2_frame_num_gt(prev_frame_number,
+					   qh->start_active_frame));
+
+		missed = dwc2_frame_num_dec(qh->start_active_frame,
+					    ideal_start);
+	}
+
+exit:
+	qh->next_active_frame = qh->start_active_frame;
+
+	return missed;
 }
 
 /*
@@ -865,7 +1031,9 @@ static void dwc2_sched_periodic_split(struct dwc2_hsotg *hsotg,
 void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 			    int sched_next_periodic_split)
 {
+	u16 old_frame = qh->next_active_frame;
 	u16 frame_number;
+	int missed;
 
 	if (dbg_qh(qh))
 		dev_vdbg(hsotg->dev, "%s()\n", __func__);
@@ -878,30 +1046,39 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 		return;
 	}
 
-	frame_number = dwc2_hcd_get_frame_number(hsotg);
-
-	if (qh->do_split) {
-		dwc2_sched_periodic_split(hsotg, qh, frame_number,
-					  sched_next_periodic_split);
-	} else {
-		qh->next_active_frame = dwc2_frame_num_inc(
-			qh->next_active_frame, qh->host_interval);
-		if (dwc2_frame_num_le(qh->next_active_frame, frame_number))
-			qh->next_active_frame = frame_number;
-	}
-
 	if (list_empty(&qh->qtd_list)) {
 		dwc2_hcd_qh_unlink(hsotg, qh);
 		return;
 	}
+
+	/*
+	 * Use the real frame number rather than the cached value as of the
+	 * last SOF just to get us a little closer to reality.  Note that
+	 * means we don't actually know if we've already handled the SOF
+	 * interrupt for this frame.
+	 */
+	frame_number = dwc2_hcd_get_frame_number(hsotg);
+
+	if (sched_next_periodic_split)
+		missed = dwc2_next_for_periodic_split(hsotg, qh, frame_number);
+	else
+		missed = dwc2_next_periodic_start(hsotg, qh, frame_number);
+
+	dwc2_sch_vdbg(hsotg,
+		     "QH=%p next(%d) fn=%04x, sch=%04x=>%04x (%+d) miss=%d %s\n",
+		     qh, sched_next_periodic_split, frame_number, old_frame,
+		     qh->next_active_frame,
+		     dwc2_frame_num_dec(qh->next_active_frame, old_frame),
+		missed, missed ? "MISS" : "");
+
 	/*
 	 * Remove from periodic_sched_queued and move to
 	 * appropriate queue
+	 *
+	 * Note: we purposely use the frame_number from the "hsotg" structure
+	 * since we know SOF interrupt will handle future frames.
 	 */
-	if ((hsotg->core_params->uframe_sched > 0 &&
-	     dwc2_frame_num_le(qh->next_active_frame, frame_number)) ||
-	    (hsotg->core_params->uframe_sched <= 0 &&
-	     qh->next_active_frame == frame_number))
+	if (dwc2_frame_num_le(qh->next_active_frame, hsotg->frame_number))
 		list_move_tail(&qh->qh_list_entry,
 			       &hsotg->periodic_sched_ready);
 	else
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Wed Jan 20 21:04:31 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Wed, 20 Jan 2016 21:04:31 -0800
Subject: [PATCH v4 20/21] usb: dwc2: host: Totally redo the microframe
 scheduler
In-Reply-To: <1453352672-27890-1-git-send-email-dianders@chromium.org>
References: <1453352672-27890-1-git-send-email-dianders@chromium.org>
Message-ID: <1453352672-27890-21-git-send-email-dianders@chromium.org>

This totally reimplements the microframe scheduler in dwc2 to attempt to
handle periodic splits properly.  The old code didn't even try, so this
was a significant effort since periodic splits are one of the most
complicated things in USB.

I've attempted to keep the old "don't use the microframe" schduler
around for now, but not sure it's needed.  It has also only been lightly
tested.

I think it's pretty certain that this scheduler isn't perfect and might
have some bugs, but it seems much better than what was there before.
With this change my stressful USB test (USB webcam + USB audio + some
keyboards) crackles less.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v4:
- Figured out what the microframe scheduler was supposed to do.
- Microframe rewrite is totally different from v3, hopefully more right.
- Microframe rewrite is later in the series now.

Changes in v3:
- The uframe scheduler patch is folded into optimization series.
- Optimize uframe scheduler "single uframe" case a little.
- uframe scheduler now atop logging patches.
- uframe scheduler now before delayed bandwidth release patches.
- Add defines like EARLY_FRAME_USEC
- Reorder dwc2_deschedule_periodic() in prep for future patches.
- uframe scheduler now shows real usefulness w/ future patches!
- Assuming single_tt is new for v3; not terribly well tested (yet).
- Keep track and use our uframe new for v3.

Changes in v2:
- Totally rewrote uframe scheduler again after writing test code.
- uframe scheduler atop delayed bandwidth release patches.

 drivers/usb/dwc2/core.h      |   86 ++-
 drivers/usb/dwc2/hcd.c       |   87 ++-
 drivers/usb/dwc2/hcd.h       |   79 ++-
 drivers/usb/dwc2/hcd_queue.c | 1208 +++++++++++++++++++++++++++++++++++-------
 4 files changed, 1272 insertions(+), 188 deletions(-)

diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 52cbea28d0e9..d8da912311b7 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -682,7 +682,9 @@ struct dwc2_hregs_backup {
  *                      This value is in microseconds per (micro)frame. The
  *                      assumption is that all periodic transfers may occur in
  *                      the same (micro)frame.
- * @frame_usecs:        Internal variable used by the microframe scheduler
+ * @hs_periodic_bitmap: Bitmap used by the microframe scheduler any time the
+ *                      host is in high speed mode; low speed schedules are
+ *                      stored elsewhere since we need one per TT.
  * @frame_number:       Frame number read from the core at SOF. The value ranges
  *                      from 0 to HFNUM_MAX_FRNUM.
  * @periodic_qh_count:  Count of periodic QHs, if using several eps. Used for
@@ -780,6 +782,85 @@ struct dwc2_hsotg {
 #define DWC2_CORE_REV_3_00a	0x4f54300a
 
 #if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
+
+/*
+ * Constants related to high speed periodic scheduling
+ *
+ * We have a periodic schedule that is DWC2_HS_SCHEDULE_UFRAMES long.  From a
+ * reservation point of view it's assumed that the schedule goes right back to
+ * the beginning after the end of the schedule.
+ *
+ * What does that mean for scheduling things with a long interval?  It means
+ * we'll reserve time for them in every possible microframe that they could
+ * ever be scheduled in.  ...but we'll still only actually schedule them as
+ * often as they were requested.
+ *
+ * We keep our schedule in a "bitmap" structure.  This simplifies having
+ * to keep track of and merge intervals: we just let the bitmap code do most
+ * of the heavy lifting.  In a way scheduling is much like memory allocation.
+ *
+ * We schedule 100us per uframe or 80% of 125us (the maximum amount you're
+ * supposed to schedule for periodic transfers).  That's according to spec.
+ *
+ * Note that though we only schedule 80% of each microframe, the bitmap that we
+ * keep the schedule in is tightly packed (AKA it doesn't have 100us worth of
+ * space for each uFrame).
+ *
+ * Requirements:
+ * - DWC2_HS_SCHEDULE_UFRAMES must even divide 0x4000 (HFNUM_MAX_FRNUM + 1)
+ * - DWC2_HS_SCHEDULE_UFRAMES must be 8 times DWC2_LS_SCHEDULE_FRAMES (probably
+ *   could be any multiple of 8 times DWC2_LS_SCHEDULE_FRAMES, but there might
+ *   be bugs).  The 8 comes from the USB spec: number of microframes per frame.
+ */
+#define DWC2_US_PER_UFRAME		125
+#define DWC2_HS_PERIODIC_US_PER_UFRAME	100
+
+#define DWC2_HS_SCHEDULE_UFRAMES	8
+#define DWC2_HS_SCHEDULE_US		(DWC2_HS_SCHEDULE_UFRAMES * \
+					 DWC2_HS_PERIODIC_US_PER_UFRAME)
+
+/*
+ * Constants related to low speed scheduling
+ *
+ * For high speed we schedule every 1us.  For low speed that's a bit overkill,
+ * so we make up a unit called a "slice" that's worth 25us.  There are 40
+ * slices in a full frame and we can schedule 36 of those (90%) for periodic
+ * transfers.
+ *
+ * Our low speed schedule can be as short as 1 frame or could be longer.  When
+ * we only schedule 1 frame it means that we'll need to reserve a time every
+ * frame even for things that only transfer very rarely, so something that runs
+ * every 2048 frames will get time reserved in every frame.  Our low speed
+ * schedule can be longer and we'll be able to handle more overlap, but that
+ * will come at increased memory cost and increased time to schedule.
+ *
+ * Note: one other advantage of a short low speed schedule is that if we mess
+ * up and miss scheduling we can jump in and use any of the slots that we
+ * happened to reserve.
+ *
+ * With 25 us per slice and 1 frame in the schedule, we only need 4 bytes for
+ * the schedule.  There will be one schedule per TT.
+ *
+ * Requirements:
+ * - DWC2_US_PER_SLICE must evenly divide DWC2_LS_PERIODIC_US_PER_FRAME.
+ */
+#define DWC2_US_PER_SLICE	25
+#define DWC2_SLICES_PER_UFRAME	(DWC2_US_PER_UFRAME / DWC2_US_PER_SLICE)
+
+#define DWC2_ROUND_US_TO_SLICE(us) \
+				(DIV_ROUND_UP((us), DWC2_US_PER_SLICE) * \
+				 DWC2_US_PER_SLICE)
+
+#define DWC2_LS_PERIODIC_US_PER_FRAME \
+				900
+#define DWC2_LS_PERIODIC_SLICES_PER_FRAME \
+				(DWC2_LS_PERIODIC_US_PER_FRAME / \
+				 DWC2_US_PER_SLICE)
+
+#define DWC2_LS_SCHEDULE_FRAMES	1
+#define DWC2_LS_SCHEDULE_SLICES	(DWC2_LS_SCHEDULE_FRAMES * \
+				 DWC2_LS_PERIODIC_SLICES_PER_FRAME)
+
 	union dwc2_hcd_internal_flags {
 		u32 d32;
 		struct {
@@ -803,7 +884,8 @@ struct dwc2_hsotg {
 	struct list_head periodic_sched_queued;
 	struct list_head split_order;
 	u16 periodic_usecs;
-	u16 frame_usecs[8];
+	unsigned long hs_periodic_bitmap[
+		DIV_ROUND_UP(DWC2_HS_SCHEDULE_US, BITS_PER_LONG)];
 	u16 frame_number;
 	u16 periodic_qh_count;
 	bool bus_suspended;
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 69a2a175100a..9d17759b46df 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -2247,6 +2247,90 @@ void dwc2_host_hub_info(struct dwc2_hsotg *hsotg, void *context, int *hub_addr,
 	*hub_port = urb->dev->ttport;
 }
 
+/**
+ * dwc2_host_get_tt_info() - Get the dwc2_tt associated with context
+ *
+ * This will get the dwc2_tt structure (and ttport) associated with the given
+ * context (which is really just a struct urb pointer).
+ *
+ * The first time this is called for a given TT we allocate memory for our
+ * structure.  When everyone is done and has called dwc2_host_put_tt_info()
+ * then the refcount for the structure will go to 0 and we'll free it.
+ *
+ * @hsotg:     The HCD state structure for the DWC OTG controller.
+ * @qh:        The QH structure.
+ * @context:   The priv pointer from a struct dwc2_hcd_urb.
+ * @mem_flags: Flags for allocating memory.
+ * @ttport:    We'll return this device's port number here.  That's used to
+ *             reference into the bitmap if we're on a multi_tt hub.
+ *
+ * Return: a pointer to a struct dwc2_tt.  Don't forget to call
+ *         dwc2_host_put_tt_info()!  Returns NULL upon memory alloc failure.
+ */
+
+struct dwc2_tt *dwc2_host_get_tt_info(struct dwc2_hsotg *hsotg, void *context,
+				      gfp_t mem_flags, int *ttport)
+{
+	struct urb *urb = context;
+	struct dwc2_tt *dwc_tt = NULL;
+
+	if (urb->dev->tt) {
+		*ttport = urb->dev->ttport;
+
+		dwc_tt = urb->dev->tt->hcpriv;
+		if (dwc_tt == NULL) {
+			size_t bitmap_size;
+
+			/*
+			 * For single_tt we need one schedule.  For multi_tt
+			 * we need one per port.
+			 */
+			bitmap_size = DWC2_ELEMENTS_PER_LS_BITMAP *
+				      sizeof(dwc_tt->periodic_bitmaps[0]);
+			if (urb->dev->tt->multi)
+				bitmap_size *= urb->dev->tt->hub->maxchild;
+
+			dwc_tt = kzalloc(sizeof(*dwc_tt) + bitmap_size,
+					 mem_flags);
+			if (dwc_tt == NULL)
+				return NULL;
+
+			dwc_tt->usb_tt = urb->dev->tt;
+			dwc_tt->usb_tt->hcpriv = dwc_tt;
+		}
+
+		dwc_tt->refcount++;
+	}
+
+	return dwc_tt;
+}
+
+/**
+ * dwc2_host_put_tt_info() - Put the dwc2_tt from dwc2_host_get_tt_info()
+ *
+ * Frees resources allocated by dwc2_host_get_tt_info() if all current holders
+ * of the structure are done.
+ *
+ * It's OK to call this with NULL.
+ *
+ * @hsotg:     The HCD state structure for the DWC OTG controller.
+ * @dwc_tt:    The pointer returned by dwc2_host_get_tt_info.
+ */
+void dwc2_host_put_tt_info(struct dwc2_hsotg *hsotg, struct dwc2_tt *dwc_tt)
+{
+	/* Model kfree and make put of NULL a no-op */
+	if (dwc_tt == NULL)
+		return;
+
+	WARN_ON(dwc_tt->refcount < 1);
+
+	dwc_tt->refcount--;
+	if (!dwc_tt->refcount) {
+		dwc_tt->usb_tt->hcpriv = NULL;
+		kfree(dwc_tt);
+	}
+}
+
 int dwc2_host_get_speed(struct dwc2_hsotg *hsotg, void *context)
 {
 	struct urb *urb = context;
@@ -3190,9 +3274,6 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
 		hsotg->hc_ptr_array[i] = channel;
 	}
 
-	if (hsotg->core_params->uframe_sched > 0)
-		dwc2_hcd_init_usecs(hsotg);
-
 	/* Initialize hsotg start work */
 	INIT_DELAYED_WORK(&hsotg->start_work, dwc2_hcd_start_func);
 
diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index fd266ac53a28..140b1511a131 100644
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -212,6 +212,43 @@ enum dwc2_transaction_type {
 	DWC2_TRANSACTION_ALL,
 };
 
+/* The number of elements per LS bitmap (per port on multi_tt) */
+#define DWC2_ELEMENTS_PER_LS_BITMAP	DIV_ROUND_UP(DWC2_LS_SCHEDULE_SLICES, \
+						     BITS_PER_LONG)
+
+/**
+ * struct dwc2_tt - dwc2 data associated with a usb_tt
+ *
+ * @refcount:           Number of Queue Heads (QHs) holding a reference.
+ * @usb_tt:             Pointer back to the official usb_tt.
+ * @periodic_bitmaps:   Bitmap for which parts of the 1ms frame are accounted
+ *                      for already.  Each is DWC2_ELEMENTS_PER_LS_BITMAP
+ *			elements (so sizeof(long) times that in bytes).
+ *
+ * This structure is stored in the hcpriv of the official usb_tt.
+ */
+struct dwc2_tt {
+	int refcount;
+	struct usb_tt *usb_tt;
+	unsigned long periodic_bitmaps[];
+};
+
+/**
+ * struct dwc2_hs_transfer_time - Info about a transfer on the high speed bus.
+ *
+ * @start_schedule_usecs:  The start time on the main bus schedule.  Note that
+ *                         the main bus schedule is tightly packed and this
+ *			   time should be interpreted as tightly packed (so
+ *			   uFrame 0 starts at 0 us, uFrame 1 starts at 100 us
+ *			   instead of 125 us).
+ * @duration_us:           How long this transfer goes.
+ */
+
+struct dwc2_hs_transfer_time {
+	u32 start_schedule_us;
+	u16 duration_us;
+};
+
 /**
  * struct dwc2_qh - Software queue head structure
  *
@@ -237,18 +274,33 @@ enum dwc2_transaction_type {
  * @td_first:           Index of first activated isochronous transfer descriptor
  * @td_last:            Index of last activated isochronous transfer descriptor
  * @host_us:            Bandwidth in microseconds per transfer as seen by host
+ * @device_us:          Bandwidth in microseconds per transfer as seen by device
  * @host_interval:      Interval between transfers as seen by the host.  If
  *                      the host is high speed and the device is low speed this
  *                      will be 8 times device interval.
- * @next_active_frame:  (Micro)frame before we next need to put something on
+ * @device_interval:    Interval between transfers as seen by the device.
+ *                      interval.
+ * @next_active_frame:  (Micro)frame _before_ we next need to put something on
  *                      the bus.  We'll move the qh to active here.  If the
  *                      host is in high speed mode this will be a uframe.  If
  *                      the host is in low speed mode this will be a full frame.
  * @start_active_frame: If we are partway through a split transfer, this will be
  *			what next_active_frame was when we started.  Otherwise
  *			it should always be the same as next_active_frame.
- * @assigned_uframe:    The uframe (0 -7) assigned by dwc2_find_uframe().
- * @frame_usecs:        Internal variable used by the microframe scheduler
+ * @num_hs_transfers:   Number of transfers in hs_transfers.
+ *                      Normally this is 1 but can be more than one for splits.
+ *                      Always >= 1 unless the host is in low/full speed mode.
+ * @hs_transfers:       Transfers that are scheduled as seen by the high speed
+ *                      bus.  Not used if host is in low or full speed mode (but
+ *                      note that it IS USED if the device is low or full speed
+ *                      as long as the HOST is in high speed mode).
+ * @ls_start_schedule_slice: Start time (in slices) on the low speed bus
+ *                           schedule that's being used by this device.  This
+ *			     will be on the periodic_bitmap in a
+ *                           "struct dwc2_tt".  Not used if this device is high
+ *                           speed.  Note that this is in "schedule slice" which
+ *                           is tightly packed.
+ * @ls_duration_us:     Duration on the low speed bus schedule.
  * @ntd:                Actual number of transfer descriptors in a list
  * @qtd_list:           List of QTDs for this QH
  * @channel:            Host channel currently processing transfers for this QH
@@ -261,8 +313,12 @@ enum dwc2_transaction_type {
  *                      descriptor and indicates original XferSize value for the
  *                      descriptor
  * @unreserve_timer:    Timer for releasing periodic reservation.
+ * @dwc2_tt:            Pointer to our tt info (or NULL if no tt).
+ * @ttport:             Port number within our tt.
  * @tt_buffer_dirty     True if clear_tt_buffer_complete is pending
  * @unreserve_pending:  True if we planned to unreserve but haven't yet.
+ * @schedule_low_speed: True if we have a low/full speed component (either the
+ *			host is in low/full speed mode or do_split).
  *
  * A Queue Head (QH) holds the static characteristics of an endpoint and
  * maintains a list of transfers (QTDs) for that endpoint. A QH structure may
@@ -280,11 +336,14 @@ struct dwc2_qh {
 	u8 td_first;
 	u8 td_last;
 	u16 host_us;
+	u16 device_us;
 	u16 host_interval;
+	u16 device_interval;
 	u16 next_active_frame;
 	u16 start_active_frame;
-	u16 assigned_uframe;
-	u16 frame_usecs[8];
+	s16 num_hs_transfers;
+	struct dwc2_hs_transfer_time hs_transfers[DWC2_HS_SCHEDULE_UFRAMES];
+	u32 ls_start_schedule_slice;
 	u16 ntd;
 	struct list_head qtd_list;
 	struct dwc2_host_chan *channel;
@@ -294,8 +353,11 @@ struct dwc2_qh {
 	u32 desc_list_sz;
 	u32 *n_bytes;
 	struct timer_list unreserve_timer;
+	struct dwc2_tt *dwc_tt;
+	int ttport;
 	unsigned tt_buffer_dirty:1;
 	unsigned unreserve_pending:1;
+	unsigned schedule_low_speed:1;
 };
 
 /**
@@ -462,7 +524,6 @@ extern void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg,
 
 /* Schedule Queue Functions */
 /* Implemented in hcd_queue.c */
-extern void dwc2_hcd_init_usecs(struct dwc2_hsotg *hsotg);
 extern struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
 					  struct dwc2_hcd_urb *urb,
 					  gfp_t mem_flags);
@@ -728,6 +789,12 @@ extern void dwc2_host_start(struct dwc2_hsotg *hsotg);
 extern void dwc2_host_disconnect(struct dwc2_hsotg *hsotg);
 extern void dwc2_host_hub_info(struct dwc2_hsotg *hsotg, void *context,
 			       int *hub_addr, int *hub_port);
+extern struct dwc2_tt *dwc2_host_get_tt_info(struct dwc2_hsotg *hsotg,
+					     void *context, gfp_t mem_flags,
+					     int *ttport);
+
+extern void dwc2_host_put_tt_info(struct dwc2_hsotg *hsotg,
+				  struct dwc2_tt *dwc_tt);
 extern int dwc2_host_get_speed(struct dwc2_hsotg *hsotg, void *context);
 extern void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
 			       int status);
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 7f54eeb2bf5f..83c467cd6045 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -136,116 +136,933 @@ static int dwc2_check_periodic_bandwidth(struct dwc2_hsotg *hsotg,
 }
 
 /**
- * Microframe scheduler
- * track the total use in hsotg->frame_usecs
- * keep each qh use in qh->frame_usecs
- * when surrendering the qh then donate the time back
+ * pmap_schedule() - Schedule time in a periodic bitmap (pmap).
+ *
+ * @map:             The bitmap representing the schedule; will be updated
+ *                   upon success.
+ * @bits_per_period: The schedule represents several periods.  This is how many
+ *                   bits are in each period.  It's assumed that the beginning
+ *                   of the schedule will repeat after its end.
+ * @periods_in_map:  The number of periods in the schedule.
+ * @num_bits:        The number of bits we need per period we want to reserve
+ *                   in this function call.
+ * @interval:        How often we need to be scheduled for the reservation this
+ *                   time.  1 means every period.  2 means every other period.
+ *                   ...you get the picture?
+ * @start:           The bit number to start at.  Normally 0.  Must be within
+ *                   the interval or we return failure right away.
+ * @only_one_period: Normally we'll allow picking a start anywhere within the
+ *                   first interval, since we can still make all repetition
+ *                   requirements by doing that.  However, if you pass true
+ *                   here then we'll return failure if we can't fit within
+ *                   the period that "start" is in.
+ *
+ * The idea here is that we want to schedule time for repeating events that all
+ * want the same resource.  The resource is divided into fixed-sized periods
+ * and the events want to repeat every "interval" periods.  The schedule
+ * granularity is one bit.
+ *
+ * To keep things "simple", we'll represent our schedule with a bitmap that
+ * contains a fixed number of periods.  This gets rid of a lot of complexity
+ * but does mean that we need to handle things specially (and non-ideally) if
+ * the number of the periods in the schedule doesn't match well with the
+ * intervals that we're trying to schedule.
+ *
+ * Here's an explanation of the scheme we'll implement, assuming 8 periods.
+ * - If interval is 1, we need to take up space in each of the 8
+ *   periods we're scheduling.  Easy.
+ * - If interval is 2, we need to take up space in half of the
+ *   periods.  Again, easy.
+ * - If interval is 3, we actually need to fall back to interval 1.
+ *   Why?  Because we might need time in any period.  AKA for the
+ *   first 8 periods, we'll be in slot 0, 3, 6.  Then we'll be
+ *   in slot 1, 4, 7.  Then we'll be in 2, 5.  Then we'll be back to
+ *   0, 3, and 6.  Since we could be in any frame we need to reserve
+ *   for all of them.  Sucks, but that's what you gotta do.  Note that
+ *   if we were instead scheduling 8 * 3 = 24 we'd do much better, but
+ *   then we need more memory and time to do scheduling.
+ * - If interval is 4, easy.
+ * - If interval is 5, we again need interval 1.  The schedule will be
+ *   0, 5, 2, 7, 4, 1, 6, 3, 0
+ * - If interval is 6, we need interval 2.  0, 6, 4, 2.
+ * - If interval is 7, we need interval 1.
+ * - If interval is 8, we need interval 8.
+ *
+ * If you do the math, you'll see that we need to pretend that interval is
+ * equal to the greatest_common_divisor(interval, periods_in_map).
+ *
+ * Note that at the moment this function tends to front-pack the schedule.
+ * In some cases that's really non-ideal (it's hard to schedule things that
+ * need to repeat every period).  In other cases it's perfect (you can easily
+ * schedule bigger, less often repeating things).
+ *
+ * Here's the algorithm in action (8 periods, 5 bits per period):
+ *  |**   |     |**   |     |**   |     |**   |     |   OK 2 bits, intv 2 at 0
+ *  |*****|  ***|*****|  ***|*****|  ***|*****|  ***|   OK 3 bits, intv 3 at 2
+ *  |*****|* ***|*****|  ***|*****|* ***|*****|  ***|   OK 1 bits, intv 4 at 5
+ *  |**   |*    |**   |     |**   |*    |**   |     | Remv 3 bits, intv 3 at 2
+ *  |***  |*    |***  |     |***  |*    |***  |     |   OK 1 bits, intv 6 at 2
+ *  |**** |*  * |**** |   * |**** |*  * |**** |   * |   OK 1 bits, intv 1 at 3
+ *  |**** |**** |**** | *** |**** |**** |**** | *** |   OK 2 bits, intv 2 at 6
+ *  |*****|*****|*****| ****|*****|*****|*****| ****|   OK 1 bits, intv 1 at 4
+ *  |*****|*****|*****| ****|*****|*****|*****| ****| FAIL 1 bits, intv 1
+ *  |  ***|*****|  ***| ****|  ***|*****|  ***| ****| Remv 2 bits, intv 2 at 0
+ *  |  ***| ****|  ***| ****|  ***| ****|  ***| ****| Remv 1 bits, intv 4 at 5
+ *  |   **| ****|   **| ****|   **| ****|   **| ****| Remv 1 bits, intv 6 at 2
+ *  |    *| ** *|    *| ** *|    *| ** *|    *| ** *| Remv 1 bits, intv 1 at 3
+ *  |    *|    *|    *|    *|    *|    *|    *|    *| Remv 2 bits, intv 2 at 6
+ *  |     |     |     |     |     |     |     |     | Remv 1 bits, intv 1 at 4
+ *  |**   |     |**   |     |**   |     |**   |     |   OK 2 bits, intv 2 at 0
+ *  |***  |     |**   |     |***  |     |**   |     |   OK 1 bits, intv 4 at 2
+ *  |*****|     |** **|     |*****|     |** **|     |   OK 2 bits, intv 2 at 3
+ *  |*****|*    |** **|     |*****|*    |** **|     |   OK 1 bits, intv 4 at 5
+ *  |*****|***  |** **| **  |*****|***  |** **| **  |   OK 2 bits, intv 2 at 6
+ *  |*****|*****|** **| ****|*****|*****|** **| ****|   OK 2 bits, intv 2 at 8
+ *  |*****|*****|*****| ****|*****|*****|*****| ****|   OK 1 bits, intv 4 at 12
+ *
+ * This function is pretty generic and could be easily abstracted if anything
+ * needed similar scheduling.
+ *
+ * Returns either -ENOSPC or a >= 0 start bit which should be passed to the
+ * unschedule routine.  The map bitmap will be updated on a non-error result.
  */
-static const unsigned short max_uframe_usecs[] = {
-	100, 100, 100, 100, 100, 100, 30, 0
-};
+static int pmap_schedule(unsigned long *map, int bits_per_period,
+			 int periods_in_map, int num_bits,
+			 int interval, int start, bool only_one_period)
+{
+	int interval_bits;
+	int to_reserve;
+	int first_end;
+	int i;
+
+	if (num_bits > bits_per_period)
+		return -ENOSPC;
+
+	/* Adjust interval as per description */
+	interval = gcd(interval, periods_in_map);
+
+	interval_bits = bits_per_period * interval;
+	to_reserve = periods_in_map / interval;
 
-void dwc2_hcd_init_usecs(struct dwc2_hsotg *hsotg)
+	/* If start has gotten us past interval then we can't schedule */
+	if (start >= interval_bits)
+		return -ENOSPC;
+
+	if (only_one_period)
+		/* Must fit within same period as start; end at begin of next */
+		first_end = (start / bits_per_period + 1) * bits_per_period;
+	else
+		/* Can fit anywhere in the first interval */
+		first_end = interval_bits;
+
+	/*
+	 * We'll try to pick the first repetition, then see if that time
+	 * is free for each of the subsequent repetitions.  If it's not
+	 * we'll adjust the start time for the next search of the first
+	 * repetition.
+	 */
+	while (start + num_bits <= first_end) {
+		int end;
+
+		/* Need to stay within this period */
+		end = (start / bits_per_period + 1) * bits_per_period;
+
+		/* Look for num_bits us in this microframe starting at start */
+		start = bitmap_find_next_zero_area(map, end, start, num_bits,
+						   0);
+
+		/*
+		 * We should get start >= end if we fail.  We might be
+		 * able to check the next microframe depending on the
+		 * interval, so continue on (start already updated).
+		 */
+		if (start >= end) {
+			start = end;
+			continue;
+		}
+
+		/* At this point we have a valid point for first one */
+		for (i = 1; i < to_reserve; i++) {
+			int ith_start = start + interval_bits * i;
+			int ith_end = end + interval_bits * i;
+			int ret;
+
+			/* Use this as a dumb "check if bits are 0" */
+			ret = bitmap_find_next_zero_area(
+				map, ith_start + num_bits, ith_start, num_bits,
+				0);
+
+			/* We got the right place, continue checking */
+			if (ret == ith_start)
+				continue;
+
+			/* Move start up for next time and exit for loop */
+			ith_start = bitmap_find_next_zero_area(
+				map, ith_end, ith_start, num_bits, 0);
+			if (ith_start >= ith_end)
+				/* Need a while new period next time */
+				start = end;
+			else
+				start = ith_start - interval_bits * i;
+			break;
+		}
+
+		/* If didn't exit the for loop with a break, we have success */
+		if (i == to_reserve)
+			break;
+	}
+
+	if (start + num_bits > first_end)
+		return -ENOSPC;
+
+	for (i = 0; i < to_reserve; i++) {
+		int ith_start = start + interval_bits * i;
+
+		bitmap_set(map, ith_start, num_bits);
+	}
+
+	return start;
+}
+
+/**
+ * pmap_unschedule() - Undo work done by pmap_schedule()
+ *
+ * @map:             See pmap_schedule().
+ * @bits_per_period: See pmap_schedule().
+ * @periods_in_map:  See pmap_schedule().
+ * @num_bits:        The number of bits that was passed to schedule.
+ * @interval:        The interval that was passed to schedule.
+ * @start:           The return value from pmap_schedule().
+ */
+static void pmap_unschedule(unsigned long *map, int bits_per_period,
+			    int periods_in_map, int num_bits,
+			    int interval, int start)
 {
+	int interval_bits;
+	int to_release;
 	int i;
 
-	for (i = 0; i < 8; i++)
-		hsotg->frame_usecs[i] = max_uframe_usecs[i];
+	/* Adjust interval as per description in pmap_schedule() */
+	interval = gcd(interval, periods_in_map);
+
+	interval_bits = bits_per_period * interval;
+	to_release = periods_in_map / interval;
+
+	for (i = 0; i < to_release; i++) {
+		int ith_start = start + interval_bits * i;
+
+		bitmap_clear(map, ith_start, num_bits);
+	}
 }
 
-static int dwc2_find_single_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+/*
+ * cat_printf() - A printf() + strcat() helper
+ *
+ * This is useful for concatenating a bunch of strings where each string is
+ * constructed using printf.
+ *
+ * @buf:   The destination buffer; will be updated to point after the printed
+ *         data.
+ * @size:  The number of bytes in the buffer (includes space for '\0').
+ * @fmt:   The format for printf.
+ * @...:   The args for printf.
+ */
+static void cat_printf(char **buf, size_t *size, const char *fmt, ...)
 {
-	unsigned short utime = qh->host_us;
+	va_list args;
 	int i;
 
-	for (i = 0; i < 8; i++) {
-		/* At the start hsotg->frame_usecs[i] = max_uframe_usecs[i] */
-		if (utime <= hsotg->frame_usecs[i]) {
-			hsotg->frame_usecs[i] -= utime;
-			qh->frame_usecs[i] += utime;
-			return i;
-		}
+	if (*size == 0)
+		return;
+
+	va_start(args, fmt);
+	i = vsnprintf(*buf, *size, fmt, args);
+	va_end(args);
+
+	if (i >= *size) {
+		*buf[*size - 1] = '\0';
+		*buf += *size;
+		*size = 0;
+	} else {
+		*buf += i;
+		*size -= i;
 	}
-	return -ENOSPC;
 }
 
 /*
- * use this for FS apps that can span multiple uframes
+ * pmap_print() - Print the given periodic map
+ *
+ * Will attempt to print out the periodic schedule.
+ *
+ * @map:             See pmap_schedule().
+ * @bits_per_period: See pmap_schedule().
+ * @periods_in_map:  See pmap_schedule().
+ * @period_name:     The name of 1 period, like "uFrame"
+ * @units:           The name of the units, like "us".
+ * @print_fn:        The function to call for printing.
+ * @print_data:      Opaque data to pass to the print function.
+ */
+static void pmap_print(unsigned long *map, int bits_per_period,
+		       int periods_in_map, const char *period_name,
+		       const char *units,
+		       void (*print_fn)(const char *str, void *data),
+		       void *print_data)
+{
+	int period;
+
+	for (period = 0; period < periods_in_map; period++) {
+		char tmp[64];
+		char *buf = tmp;
+		size_t buf_size = sizeof(tmp);
+		int period_start = period * bits_per_period;
+		int period_end = period_start + bits_per_period;
+		int start = 0;
+		int count = 0;
+		bool printed = false;
+		int i;
+
+		for (i = period_start; i < period_end + 1; i++) {
+			/* Handle case when ith bit is set */
+			if (i < period_end &&
+			    bitmap_find_next_zero_area(map, i + 1,
+						       i, 1, 0) != i) {
+				if (count == 0)
+					start = i - period_start;
+				count++;
+				continue;
+			}
+
+			/* ith bit isn't set; don't care if count == 0 */
+			if (count == 0)
+				continue;
+
+			if (!printed)
+				cat_printf(&buf, &buf_size, "%s %d: ",
+					   period_name, period);
+			else
+				cat_printf(&buf, &buf_size, ", ");
+			printed = true;
+
+			cat_printf(&buf, &buf_size, "%d %s -%3d %s", start,
+				   units, start + count - 1, units);
+			count = 0;
+		}
+
+		if (printed)
+			print_fn(tmp, print_data);
+	}
+}
+
+/**
+ * dwc2_get_ls_map() - Get the map used for the given qh
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller.
+ * @qh:    QH for the periodic transfer.
+ *
+ * We'll always get the periodic map out of our TT.  Note that even if we're
+ * running the host straight in low speed / full speed mode it appears as if
+ * a TT is allocated for us, so we'll use it.  If that ever changes we can
+ * add logic here to get a map out of "hsotg" if !qh->do_split.
+ *
+ * Returns: the map or NULL if a map couldn't be found.
+ */
+static unsigned long *dwc2_get_ls_map(struct dwc2_hsotg *hsotg,
+				      struct dwc2_qh *qh)
+{
+	unsigned long *map;
+
+	/* Don't expect to be missing a TT and be doing low speed scheduling */
+	if (WARN_ON(!qh->dwc_tt))
+		return NULL;
+
+	/* Get the map and adjust if this is a multi_tt hub */
+	map = qh->dwc_tt->periodic_bitmaps;
+	if (qh->dwc_tt->usb_tt->multi)
+		map += DWC2_ELEMENTS_PER_LS_BITMAP * qh->ttport;
+
+	return map;
+}
+
+struct dwc2_qh_print_data {
+	struct dwc2_hsotg *hsotg;
+	struct dwc2_qh *qh;
+};
+
+/**
+ * dwc2_qh_print() - Helper function for dwc2_qh_schedule_print()
+ *
+ * @str:  The string to print
+ * @data: A pointer to a struct dwc2_qh_print_data
  */
-static int dwc2_find_multi_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+static void dwc2_qh_print(const char *str, void *data)
 {
-	unsigned short utime = qh->host_us;
-	unsigned short xtime;
-	int t_left;
+	struct dwc2_qh_print_data *print_data = data;
+
+	dwc2_sch_dbg(print_data->hsotg, "QH=%p ...%s\n", print_data->qh, str);
+}
+
+/**
+ * dwc2_qh_schedule_print() - Print the periodic schedule
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller.
+ * @qh:    QH to print.
+ */
+static void dwc2_qh_schedule_print(struct dwc2_hsotg *hsotg,
+				   struct dwc2_qh *qh)
+{
+	struct dwc2_qh_print_data print_data = { hsotg, qh };
 	int i;
-	int j;
-	int k;
 
-	for (i = 0; i < 8; i++) {
-		if (hsotg->frame_usecs[i] <= 0)
+	/*
+	 * The printing functions are quite slow and inefficient.
+	 * If we don't have tracing turned on, don't run unless the special
+	 * define is turned on.
+	 */
+#ifndef DWC2_PRINT_SCHEDULE
+	return;
+#endif
+
+	if (qh->schedule_low_speed) {
+		unsigned long *map = dwc2_get_ls_map(hsotg, qh);
+
+		dwc2_sch_dbg(hsotg, "QH=%p LS/FS trans: %d=>%d us @ %d us",
+			     qh, qh->device_us,
+			     DWC2_ROUND_US_TO_SLICE(qh->device_us),
+			     DWC2_US_PER_SLICE * qh->ls_start_schedule_slice);
+
+		if (map) {
+			dwc2_sch_dbg(hsotg,
+				     "QH=%p Whole low/full speed map %p now:\n",
+				     qh, map);
+			pmap_print(map, DWC2_LS_PERIODIC_SLICES_PER_FRAME,
+				   DWC2_LS_SCHEDULE_FRAMES, "Frame ", "slices",
+				   dwc2_qh_print, &print_data);
+		}
+	}
+
+	for (i = 0; i < qh->num_hs_transfers; i++) {
+		struct dwc2_hs_transfer_time *trans_time = qh->hs_transfers + i;
+		int uframe = trans_time->start_schedule_us /
+			     DWC2_HS_PERIODIC_US_PER_UFRAME;
+		int rel_us = trans_time->start_schedule_us %
+			     DWC2_HS_PERIODIC_US_PER_UFRAME;
+
+		dwc2_sch_dbg(hsotg,
+			     "QH=%p HS trans #%d: %d us @ uFrame %d + %d us\n",
+			     qh, i, trans_time->duration_us, uframe, rel_us);
+	}
+	if (qh->num_hs_transfers) {
+		dwc2_sch_dbg(hsotg, "QH=%p Whole high speed map now:\n", qh);
+		pmap_print(hsotg->hs_periodic_bitmap,
+			   DWC2_HS_PERIODIC_US_PER_UFRAME,
+			   DWC2_HS_SCHEDULE_UFRAMES, "uFrame", "us",
+			   dwc2_qh_print, &print_data);
+	}
+
+}
+
+/**
+ * dwc2_ls_pmap_schedule() - Schedule a low speed QH
+ *
+ * @hsotg:        The HCD state structure for the DWC OTG controller.
+ * @qh:           QH for the periodic transfer.
+ * @search_slice: We'll start trying to schedule at the passed slice.
+ *                Remember that slices are the units of the low speed
+ *                schedule (think 25us or so).
+ *
+ * Wraps pmap_schedule() with the right parameters for low speed scheduling.
+ *
+ * Normally we schedule low speed devices on the map associated with the TT.
+ *
+ * Returns: 0 for success or an error code.
+ */
+static int dwc2_ls_pmap_schedule(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
+				 int search_slice)
+{
+	int slices = DIV_ROUND_UP(qh->device_us, DWC2_US_PER_SLICE);
+	unsigned long *map = dwc2_get_ls_map(hsotg, qh);
+	int slice;
+
+	if (map == NULL)
+		return -EINVAL;
+
+	/*
+	 * Schedule on the proper low speed map with our low speed scheduling
+	 * parameters.  Note that we use the "device_interval" here since
+	 * we want the low speed interval and the only way we'd be in this
+	 * function is if the device is low speed.
+	 *
+	 * If we happen to be doing low speed and high speed scheduling for the
+	 * same transaction (AKA we have a split) we always do low speed first.
+	 * That means we can always pass "false" for only_one_period (that
+	 * parameters is only useful when we're trying to get one schedule to
+	 * match what we already planned in the other schedule).
+	 */
+	slice = pmap_schedule(map, DWC2_LS_PERIODIC_SLICES_PER_FRAME,
+			      DWC2_LS_SCHEDULE_FRAMES, slices,
+			      qh->device_interval, search_slice, false);
+
+	if (slice < 0)
+		return slice;
+
+	qh->ls_start_schedule_slice = slice;
+	return 0;
+}
+
+/**
+ * dwc2_ls_pmap_unschedule() - Undo work done by dwc2_ls_pmap_schedule()
+ *
+ * @hsotg:       The HCD state structure for the DWC OTG controller.
+ * @qh:          QH for the periodic transfer.
+ */
+static void dwc2_ls_pmap_unschedule(struct dwc2_hsotg *hsotg,
+				    struct dwc2_qh *qh)
+{
+	int slices = DIV_ROUND_UP(qh->device_us, DWC2_US_PER_SLICE);
+	unsigned long *map = dwc2_get_ls_map(hsotg, qh);
+
+	/* Schedule should have failed, so no worries about no error code */
+	if (map == NULL)
+		return;
+
+	pmap_unschedule(map, DWC2_LS_PERIODIC_SLICES_PER_FRAME,
+			DWC2_LS_SCHEDULE_FRAMES, slices, qh->device_interval,
+			qh->ls_start_schedule_slice);
+}
+
+/**
+ * dwc2_hs_pmap_schedule - Schedule in the main high speed schedule
+ *
+ * This will schedule something on the main dwc2 schedule.
+ *
+ * We'll start looking in qh->hs_transfers[index].start_schedule_us.  We'll
+ * update this with the result upon success.  We also use the duration from
+ * the same structure.
+ *
+ * @hsotg:           The HCD state structure for the DWC OTG controller.
+ * @qh:              QH for the periodic transfer.
+ * @only_one_period: If true we will limit ourselves to just looking at
+ *                   one period (aka one 100us chunk).  This is used if we have
+ *                   already scheduled something on the low speed schedule and
+ *                   need to find something that matches on the high speed one.
+ * @index:           The index into qh->hs_transfers that we're working with.
+ *
+ * Returns: 0 for success or an error code.  Upon success the
+ *          dwc2_hs_transfer_time specified by "index" will be updated.
+ */
+static int dwc2_hs_pmap_schedule(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
+				 bool only_one_period, int index)
+{
+	struct dwc2_hs_transfer_time *trans_time = qh->hs_transfers + index;
+	int us;
+
+	us = pmap_schedule(hsotg->hs_periodic_bitmap,
+			   DWC2_HS_PERIODIC_US_PER_UFRAME,
+			   DWC2_HS_SCHEDULE_UFRAMES, trans_time->duration_us,
+			   qh->host_interval, trans_time->start_schedule_us,
+			   only_one_period);
+
+	if (us < 0)
+		return us;
+
+	trans_time->start_schedule_us = us;
+	return 0;
+}
+
+/**
+ * dwc2_ls_pmap_unschedule() - Undo work done by dwc2_hs_pmap_schedule()
+ *
+ * @hsotg:       The HCD state structure for the DWC OTG controller.
+ * @qh:          QH for the periodic transfer.
+ */
+static void dwc2_hs_pmap_unschedule(struct dwc2_hsotg *hsotg,
+				    struct dwc2_qh *qh, int index)
+{
+	struct dwc2_hs_transfer_time *trans_time = qh->hs_transfers + index;
+
+	pmap_unschedule(hsotg->hs_periodic_bitmap,
+			DWC2_HS_PERIODIC_US_PER_UFRAME,
+			DWC2_HS_SCHEDULE_UFRAMES, trans_time->duration_us,
+			qh->host_interval, trans_time->start_schedule_us);
+}
+
+/**
+ * dwc2_uframe_schedule_split - Schedule a QH for a periodic split xfer.
+ *
+ * This is the most complicated thing in USB.  We have to find matching time
+ * in both the global high speed schedule for the port and the low speed
+ * schedule for the TT associated with the given device.
+ *
+ * Being here means that the host must be running in high speed mode and the
+ * device is in low or full speed mode (and behind a hub).
+ *
+ * @hsotg:       The HCD state structure for the DWC OTG controller.
+ * @qh:          QH for the periodic transfer.
+ */
+static int dwc2_uframe_schedule_split(struct dwc2_hsotg *hsotg,
+				      struct dwc2_qh *qh)
+{
+	int bytecount = dwc2_hb_mult(qh->maxp) * dwc2_max_packet(qh->maxp);
+	int ls_search_slice;
+	int err = 0;
+	int host_interval_in_sched;
+
+	/*
+	 * The interval (how often to repeat) in the actual host schedule.
+	 * See pmap_schedule() for gcd() explanation.
+	 */
+	host_interval_in_sched = gcd(qh->host_interval,
+				     DWC2_HS_SCHEDULE_UFRAMES);
+
+	/*
+	 * We always try to find space in the low speed schedule first, then
+	 * try to find high speed time that matches.  If we don't, we'll bump
+	 * up the place we start searching in the low speed schedule and try
+	 * again.  To start we'll look right at the beginning of the low speed
+	 * schedule.
+	 *
+	 * Note that this will tend to front-load the high speed schedule.
+	 * We may eventually want to try to avoid this by either considering
+	 * both schedules together or doing some sort of round robin.
+	 */
+	ls_search_slice = 0;
+
+	while (ls_search_slice < DWC2_LS_SCHEDULE_SLICES) {
+		int start_s_uframe;
+		int ssplit_s_uframe;
+		int second_s_uframe;
+		int rel_uframe;
+		int first_count;
+		int middle_count;
+		int end_count;
+		int first_data_bytes;
+		int other_data_bytes;
+		int i;
+
+		if (qh->schedule_low_speed) {
+			err = dwc2_ls_pmap_schedule(hsotg, qh, ls_search_slice);
+
+			/*
+			 * If we got an error here there's no other magic we
+			 * can do, so bail.  All the looping above is only
+			 * helpful to redo things if we got a low speed slot
+			 * and then couldn't find a matching high speed slot.
+			 */
+			if (err)
+				return err;
+		} else {
+			/* Must be missing the tt structure?  Why? */
+			WARN_ON_ONCE(1);
+		}
+
+		/*
+		 * This will give us a number 0 - 7 if
+		 * DWC2_LS_SCHEDULE_FRAMES == 1, or 0 - 15 if == 2, or ...
+		 */
+		start_s_uframe = qh->ls_start_schedule_slice /
+				 DWC2_SLICES_PER_UFRAME;
+
+		/* Get a number that's always 0 - 7 */
+		rel_uframe = (start_s_uframe % 8);
+
+		/*
+		 * If we were going to start in uframe 7 then we would need to
+		 * issue a start split in uframe 6, which spec says is not OK.
+		 * Move on to the next full frame (assuming there is one).
+		 *
+		 * See 11.18.4 Host Split Transaction Scheduling Requirements
+		 * bullet 1.
+		 */
+		if (rel_uframe == 7) {
+			if (qh->schedule_low_speed)
+				dwc2_ls_pmap_unschedule(hsotg, qh);
+			ls_search_slice =
+				(qh->ls_start_schedule_slice /
+				 DWC2_LS_PERIODIC_SLICES_PER_FRAME + 1) *
+				DWC2_LS_PERIODIC_SLICES_PER_FRAME;
 			continue;
+		}
 
 		/*
-		 * we need n consecutive slots so use j as a start slot
-		 * j plus j+1 must be enough time (for now)
+		 * For ISOC in:
+		 * - start split            (frame -1)
+		 * - complete split w/ data (frame +1)
+		 * - complete split w/ data (frame +2)
+		 * - ...
+		 * - complete split w/ data (frame +num_data_packets)
+		 * - complete split w/ data (frame +num_data_packets+1)
+		 * - complete split w/ data (frame +num_data_packets+2, max 8)
+		 *   ...though if frame was "0" then max is 7...
+		 *
+		 * For ISOC out we might need to do:
+		 * - start split w/ data    (frame -1)
+		 * - start split w/ data    (frame +0)
+		 * - ...
+		 * - start split w/ data    (frame +num_data_packets-2)
+		 *
+		 * For INTERRUPT in we might need to do:
+		 * - start split            (frame -1)
+		 * - complete split w/ data (frame +1)
+		 * - complete split w/ data (frame +2)
+		 * - complete split w/ data (frame +3, max 8)
+		 *
+		 * For INTERRUPT out we might need to do:
+		 * - start split w/ data    (frame -1)
+		 * - complete split         (frame +1)
+		 * - complete split         (frame +2)
+		 * - complete split         (frame +3, max 8)
+		 *
+		 * Start adjusting!
 		 */
-		xtime = hsotg->frame_usecs[i];
-		for (j = i + 1; j < 8; j++) {
-			/*
-			 * if we add this frame remaining time to xtime we may
-			 * be OK, if not we need to test j for a complete frame
-			 */
-			if (xtime + hsotg->frame_usecs[j] < utime) {
-				if (hsotg->frame_usecs[j] <
-							max_uframe_usecs[j])
-					continue;
+		ssplit_s_uframe = (start_s_uframe +
+				   host_interval_in_sched - 1) %
+				  host_interval_in_sched;
+		if (qh->ep_type == USB_ENDPOINT_XFER_ISOC && !qh->ep_is_in)
+			second_s_uframe = start_s_uframe;
+		else
+			second_s_uframe = start_s_uframe + 1;
+
+		/* First data transfer might not be all 188 bytes. */
+		first_data_bytes = 188 -
+			DIV_ROUND_UP(188 * (qh->ls_start_schedule_slice %
+					    DWC2_SLICES_PER_UFRAME),
+				     DWC2_SLICES_PER_UFRAME);
+		if (first_data_bytes > bytecount)
+			first_data_bytes = bytecount;
+		other_data_bytes = bytecount - first_data_bytes;
+
+		/*
+		 * For now, skip OUT xfers where first xfer is partial
+		 *
+		 * Main dwc2 code assumes:
+		 * - INT transfers never get split in two.
+		 * - ISOC transfers can always transfer 188 bytes the first
+		 *   time.
+		 *
+		 * Until that code is fixed, try again if the first transfer
+		 * couldn't transfer everything.
+		 *
+		 * This code can be removed if/when the rest of dwc2 handles
+		 * the above cases.  Until it's fixed we just won't be able
+		 * to schedule quite as tightly.
+		 */
+		if (!qh->ep_is_in &&
+		    (first_data_bytes != min_t(int, 188, bytecount))) {
+			dwc2_sch_dbg(hsotg,
+				     "QH=%p avoiding broken 1st xfer (%d, %d)\n",
+				     qh, first_data_bytes, bytecount);
+			if (qh->schedule_low_speed)
+				dwc2_ls_pmap_unschedule(hsotg, qh);
+			ls_search_slice = (start_s_uframe + 1) *
+				DWC2_SLICES_PER_UFRAME;
+			continue;
+		}
+
+		/* Start by assuming transfers for the bytes */
+		qh->num_hs_transfers = 1 + DIV_ROUND_UP(other_data_bytes, 188);
+
+		/*
+		 * Everything except ISOC OUT has extra transfers.  Rules are
+		 * complicated.  See 11.18.4 Host Split Transaction Scheduling
+		 * Requirements bullet 3.
+		 */
+		if (qh->ep_type == USB_ENDPOINT_XFER_INT) {
+			if (rel_uframe == 6)
+				qh->num_hs_transfers += 2;
+			else
+				qh->num_hs_transfers += 3;
+
+			if (qh->ep_is_in) {
+				/*
+				 * First is start split, middle/end is data.
+				 * Allocate full data bytes for all data.
+				 */
+				first_count = 4;
+				middle_count = bytecount;
+				end_count = bytecount;
+			} else {
+				/*
+				 * First is data, middle/end is complete.
+				 * First transfer and second can have data.
+				 * Rest should just have complete split.
+				 */
+				first_count = first_data_bytes;
+				middle_count = max_t(int, 4, other_data_bytes);
+				end_count = 4;
 			}
-			if (xtime >= utime) {
-				t_left = utime;
-				for (k = i; k < 8; k++) {
-					t_left -= hsotg->frame_usecs[k];
-					if (t_left <= 0) {
-						qh->frame_usecs[k] +=
-							hsotg->frame_usecs[k]
-								+ t_left;
-						hsotg->frame_usecs[k] = -t_left;
-						return i;
-					} else {
-						qh->frame_usecs[k] +=
-							hsotg->frame_usecs[k];
-						hsotg->frame_usecs[k] = 0;
-					}
-				}
+		} else {
+			if (qh->ep_is_in) {
+				int last;
+
+				/* Account for the start split */
+				qh->num_hs_transfers++;
+
+				/* Calculate "L" value from spec */
+				last = rel_uframe + qh->num_hs_transfers + 1;
+
+				/* Start with basic case */
+				if (last <= 6)
+					qh->num_hs_transfers += 2;
+				else
+					qh->num_hs_transfers += 1;
+
+				/* Adjust downwards */
+				if (last >= 6 && rel_uframe == 0)
+					qh->num_hs_transfers--;
+
+				/* 1st = start; rest can contain data */
+				first_count = 4;
+				middle_count = min_t(int, 188, bytecount);
+				end_count = middle_count;
+			} else {
+				/* All contain data, last might be smaller */
+				first_count = first_data_bytes;
+				middle_count = min_t(int, 188,
+						     other_data_bytes);
+				end_count = other_data_bytes % 188;
 			}
-			/* add the frame time to x time */
-			xtime += hsotg->frame_usecs[j];
-			/* we must have a fully available next frame or break */
-			if (xtime < utime &&
-			   hsotg->frame_usecs[j] == max_uframe_usecs[j])
-				continue;
 		}
+
+		/* Assign durations per uFrame */
+		qh->hs_transfers[0].duration_us = HS_USECS_ISO(first_count);
+		for (i = 1; i < qh->num_hs_transfers - 1; i++)
+			qh->hs_transfers[i].duration_us =
+				HS_USECS_ISO(middle_count);
+		if (qh->num_hs_transfers > 1)
+			qh->hs_transfers[qh->num_hs_transfers - 1].duration_us =
+				HS_USECS_ISO(end_count);
+
+		/*
+		 * Assign start us.  The call below to dwc2_hs_pmap_schedule()
+		 * will start with these numbers but may adjust within the same
+		 * microframe.
+		 */
+		qh->hs_transfers[0].start_schedule_us =
+			ssplit_s_uframe * DWC2_HS_PERIODIC_US_PER_UFRAME;
+		for (i = 1; i < qh->num_hs_transfers; i++)
+			qh->hs_transfers[i].start_schedule_us =
+				((second_s_uframe + i - 1) %
+				 DWC2_HS_SCHEDULE_UFRAMES) *
+				DWC2_HS_PERIODIC_US_PER_UFRAME;
+
+		/* Try to schedule with filled in hs_transfers above */
+		for (i = 0; i < qh->num_hs_transfers; i++) {
+			err = dwc2_hs_pmap_schedule(hsotg, qh, true, i);
+			if (err)
+				break;
+		}
+
+		/* If we scheduled all w/out breaking out then we're all good */
+		if (i == qh->num_hs_transfers)
+			break;
+
+		for (; i >= 0; i--)
+			dwc2_hs_pmap_unschedule(hsotg, qh, i);
+
+		if (qh->schedule_low_speed)
+			dwc2_ls_pmap_unschedule(hsotg, qh);
+
+		/* Try again starting in the next microframe */
+		ls_search_slice = (start_s_uframe + 1) * DWC2_SLICES_PER_UFRAME;
 	}
-	return -ENOSPC;
+
+	if (ls_search_slice >= DWC2_LS_SCHEDULE_SLICES)
+		return -ENOSPC;
+
+	return 0;
 }
 
-static int dwc2_find_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+/**
+ * dwc2_uframe_schedule_hs - Schedule a QH for a periodic high speed xfer.
+ *
+ * Basically this just wraps dwc2_hs_pmap_schedule() to provide a clean
+ * interface.
+ *
+ * @hsotg:       The HCD state structure for the DWC OTG controller.
+ * @qh:          QH for the periodic transfer.
+ */
+static int dwc2_uframe_schedule_hs(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+	/* In non-split host and device time are the same */
+	WARN_ON(qh->host_us != qh->device_us);
+	WARN_ON(qh->host_interval != qh->device_interval);
+	WARN_ON(qh->num_hs_transfers != 1);
+
+	/* We'll have one transfer; init start to 0 before calling scheduler */
+	qh->hs_transfers[0].start_schedule_us = 0;
+	qh->hs_transfers[0].duration_us = qh->host_us;
+
+	return dwc2_hs_pmap_schedule(hsotg, qh, false, 0);
+}
+
+/**
+ * dwc2_uframe_schedule_ls - Schedule a QH for a periodic low/full speed xfer.
+ *
+ * Basically this just wraps dwc2_ls_pmap_schedule() to provide a clean
+ * interface.
+ *
+ * @hsotg:       The HCD state structure for the DWC OTG controller.
+ * @qh:          QH for the periodic transfer.
+ */
+static int dwc2_uframe_schedule_ls(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+	/* In non-split host and device time are the same */
+	WARN_ON(qh->host_us != qh->device_us);
+	WARN_ON(qh->host_interval != qh->device_interval);
+	WARN_ON(!qh->schedule_low_speed);
+
+	/* Run on the main low speed schedule (no split = no hub = no TT) */
+	return dwc2_ls_pmap_schedule(hsotg, qh, 0);
+}
+
+/**
+ * dwc2_uframe_schedule - Schedule a QH for a periodic xfer.
+ *
+ * Calls one of the 3 sub-function depending on what type of transfer this QH
+ * is for.  Also adds some printing.
+ *
+ * @hsotg:       The HCD state structure for the DWC OTG controller.
+ * @qh:          QH for the periodic transfer.
+ */
+static int dwc2_uframe_schedule(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
 	int ret;
 
-	if (qh->dev_speed == USB_SPEED_HIGH) {
-		/* if this is a hs transaction we need a full frame */
-		ret = dwc2_find_single_uframe(hsotg, qh);
-	} else {
-		/*
-		 * if this is a fs transaction we may need a sequence
-		 * of frames
-		 */
-		ret = dwc2_find_multi_uframe(hsotg, qh);
-	}
+	if (qh->dev_speed == USB_SPEED_HIGH)
+		ret = dwc2_uframe_schedule_hs(hsotg, qh);
+	else if (!qh->do_split)
+		ret = dwc2_uframe_schedule_ls(hsotg, qh);
+	else
+		ret = dwc2_uframe_schedule_split(hsotg, qh);
+
+	if (ret)
+		dwc2_sch_dbg(hsotg, "QH=%p Failed to schedule %d\n", qh, ret);
+	else
+		dwc2_qh_schedule_print(hsotg, qh);
+
 	return ret;
 }
 
 /**
+ * dwc2_uframe_unschedule - Undoes dwc2_uframe_schedule().
+ *
+ * @hsotg:       The HCD state structure for the DWC OTG controller.
+ * @qh:          QH for the periodic transfer.
+ */
+static void dwc2_uframe_unschedule(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+	int i;
+
+	for (i = 0; i < qh->num_hs_transfers; i++)
+		dwc2_hs_pmap_unschedule(hsotg, qh, i);
+
+	if (qh->schedule_low_speed)
+		dwc2_ls_pmap_unschedule(hsotg, qh);
+
+	dwc2_sch_dbg(hsotg, "QH=%p Unscheduled\n", qh);
+}
+
+/**
  * dwc2_pick_first_frame() - Choose 1st frame for qh that's already scheduled
  *
  * Takes a qh that has already been scheduled (which means we know we have the
@@ -265,6 +1082,7 @@ static void dwc2_pick_first_frame(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 	u16 frame_number;
 	u16 earliest_frame;
 	u16 next_active_frame;
+	u16 relative_frame;
 	u16 interval;
 
 	/*
@@ -292,8 +1110,36 @@ static void dwc2_pick_first_frame(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		goto exit;
 	}
 
-	/* Adjust interval as per high speed schedule which has 8 uFrame */
-	interval = gcd(qh->host_interval, 8);
+	if (qh->dev_speed == USB_SPEED_HIGH || qh->do_split) {
+		/*
+		 * We're either at high speed or we're doing a split (which
+		 * means we're talking high speed to a hub).  In any case
+		 * the first frame should be based on when the first scheduled
+		 * event is.
+		 */
+		WARN_ON(qh->num_hs_transfers < 1);
+
+		relative_frame = qh->hs_transfers[0].start_schedule_us /
+				 DWC2_HS_PERIODIC_US_PER_UFRAME;
+
+		/* Adjust interval as per high speed schedule */
+		interval = gcd(qh->host_interval, DWC2_HS_SCHEDULE_UFRAMES);
+
+	} else {
+		/*
+		 * Low or full speed directly on dwc2.  Just about the same
+		 * as high speed but on a different schedule and with slightly
+		 * different adjustments.  Note that this works because when
+		 * the host and device are both low speed then frames in the
+		 * controller tick at low speed.
+		 */
+		relative_frame = qh->ls_start_schedule_slice /
+				 DWC2_LS_PERIODIC_SLICES_PER_FRAME;
+		interval = gcd(qh->host_interval, DWC2_LS_SCHEDULE_FRAMES);
+	}
+
+	/* Scheduler messed up if frame is past interval */
+	WARN_ON(relative_frame >= interval);
 
 	/*
 	 * We know interval must divide (HFNUM_MAX_FRNUM + 1) now that we've
@@ -310,7 +1156,7 @@ static void dwc2_pick_first_frame(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 	 * scheduled for.
 	 */
 	next_active_frame = dwc2_frame_num_inc(next_active_frame,
-					       qh->assigned_uframe);
+					       relative_frame);
 
 	/*
 	 * We actually need 1 frame before since the next_active_frame is
@@ -350,21 +1196,23 @@ static int dwc2_do_reserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
 	int status;
 
-	if (hsotg->core_params->uframe_sched > 0) {
-		status = dwc2_find_uframe(hsotg, qh);
-		if (status >= 0)
-			qh->assigned_uframe = status;
-	} else {
-		status = dwc2_periodic_channel_available(hsotg);
-		if (status) {
-			dev_info(hsotg->dev,
-				 "%s: No host channel available for periodic transfer\n",
-				 __func__);
-			return status;
-		}
+	/*
+	 * NOTE: need to figure out if we're using the microframe scheduler
+	 * whether can we actually use more channels as long as they're not
+	 * all active in the same microframe.  For now, don't allow.
+	 */
+	status = dwc2_periodic_channel_available(hsotg);
+	if (status) {
+		dev_info(hsotg->dev,
+			"%s: No host channel available for periodic transfer\n",
+			__func__);
+		return status;
+	}
 
+	if (hsotg->core_params->uframe_sched <= 0)
 		status = dwc2_check_periodic_bandwidth(hsotg, qh);
-	}
+	else
+		status = dwc2_uframe_schedule(hsotg, qh);
 
 	if (status) {
 		dev_dbg(hsotg->dev,
@@ -373,9 +1221,8 @@ static int dwc2_do_reserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		return status;
 	}
 
-	if (hsotg->core_params->uframe_sched <= 0)
-		/* Reserve periodic channel */
-		hsotg->periodic_channels++;
+	/* Reserve periodic channel */
+	hsotg->periodic_channels++;
 
 	/* Update claimed usecs per (micro)frame */
 	hsotg->periodic_usecs += qh->host_us;
@@ -409,17 +1256,13 @@ static void dwc2_do_unreserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 	/* Update claimed usecs per (micro)frame */
 	hsotg->periodic_usecs -= qh->host_us;
 
-	if (hsotg->core_params->uframe_sched > 0) {
-		int i;
+	/* Release periodic channel reservation */
+	hsotg->periodic_channels--;
 
-		for (i = 0; i < 8; i++) {
-			hsotg->frame_usecs[i] += qh->frame_usecs[i];
-			qh->frame_usecs[i] = 0;
-		}
-	} else {
-		/* Release periodic channel reservation */
-		hsotg->periodic_channels--;
-	}
+	if (hsotg->core_params->uframe_sched <= 0)
+		return;
+
+	dwc2_uframe_unschedule(hsotg, qh);
 }
 
 /**
@@ -604,88 +1447,81 @@ static void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg,
  * @qh:    The QH to init
  * @urb:   Holds the information about the device/endpoint needed to initialize
  *         the QH
+ * @mem_flags: Flags for allocating memory.
  */
 static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
-			 struct dwc2_hcd_urb *urb)
+			 struct dwc2_hcd_urb *urb, gfp_t mem_flags)
 {
-	int dev_speed, hub_addr, hub_port;
+	int dev_speed = dwc2_host_get_speed(hsotg, urb->priv);
+	u8 ep_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
+	bool ep_is_in = !!dwc2_hcd_is_pipe_in(&urb->pipe_info);
+	bool ep_is_isoc = (ep_type == USB_ENDPOINT_XFER_ISOC);
+	bool ep_is_int = (ep_type == USB_ENDPOINT_XFER_INT);
+	u32 hprt = dwc2_readl(hsotg->regs + HPRT0);
+	u32 prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
+	bool do_split = (prtspd == HPRT0_SPD_HIGH_SPEED &&
+			 dev_speed != USB_SPEED_HIGH);
+	int maxp = dwc2_hcd_get_mps(&urb->pipe_info);
+	int bytecount = dwc2_hb_mult(maxp) * dwc2_max_packet(maxp);
 	char *speed, *type;
 
-	dev_vdbg(hsotg->dev, "%s()\n", __func__);
-
 	/* Initialize QH */
 	qh->hsotg = hsotg;
 	setup_timer(&qh->unreserve_timer, dwc2_unreserve_timer_fn,
 		    (unsigned long)qh);
-	qh->ep_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
-	qh->ep_is_in = dwc2_hcd_is_pipe_in(&urb->pipe_info) ? 1 : 0;
+	qh->ep_type = ep_type;
+	qh->ep_is_in = ep_is_in;
 
 	qh->data_toggle = DWC2_HC_PID_DATA0;
-	qh->maxp = dwc2_hcd_get_mps(&urb->pipe_info);
+	qh->maxp = maxp;
 	INIT_LIST_HEAD(&qh->qtd_list);
 	INIT_LIST_HEAD(&qh->qh_list_entry);
 
-	/* FS/LS Endpoint on HS Hub, NOT virtual root hub */
-	dev_speed = dwc2_host_get_speed(hsotg, urb->priv);
+	qh->do_split = do_split;
+	qh->dev_speed = dev_speed;
 
-	dwc2_host_hub_info(hsotg, urb->priv, &hub_addr, &hub_port);
+	if (ep_is_int || ep_is_isoc) {
+		/* Compute scheduling parameters once and save them */
+		int host_speed = do_split ? USB_SPEED_HIGH : dev_speed;
+		struct dwc2_tt *dwc_tt = dwc2_host_get_tt_info(hsotg, urb->priv,
+							       mem_flags,
+							       &qh->ttport);
+		int device_ns;
 
-	if ((dev_speed == USB_SPEED_LOW || dev_speed == USB_SPEED_FULL) &&
-	    hub_addr != 0 && hub_addr != 1) {
-		dev_vdbg(hsotg->dev,
-			 "QH init: EP %d: TT found at hub addr %d, for port %d\n",
-			 dwc2_hcd_get_ep_num(&urb->pipe_info), hub_addr,
-			 hub_port);
-		qh->do_split = 1;
-	}
+		qh->dwc_tt = dwc_tt;
 
-	if (qh->ep_type == USB_ENDPOINT_XFER_INT ||
-	    qh->ep_type == USB_ENDPOINT_XFER_ISOC) {
-		/* Compute scheduling parameters once and save them */
-		u32 hprt, prtspd;
-
-		/* Todo: Account for split transfers in the bus time */
-		int bytecount =
-			dwc2_hb_mult(qh->maxp) * dwc2_max_packet(qh->maxp);
-
-		qh->host_us = NS_TO_US(usb_calc_bus_time(qh->do_split ?
-			      USB_SPEED_HIGH : dev_speed, qh->ep_is_in,
-			      qh->ep_type == USB_ENDPOINT_XFER_ISOC,
-			      bytecount));
-
-		qh->host_interval = urb->interval;
-		dwc2_sch_dbg(hsotg, "QH=%p init nxt=%04x, fn=%04x, int=%#x\n",
-			     qh, qh->next_active_frame, hsotg->frame_number,
-			     qh->host_interval);
-#if 0
-		/* Increase interrupt polling rate for debugging */
-		if (qh->ep_type == USB_ENDPOINT_XFER_INT)
-			qh->host_interval = 8;
-#endif
-		hprt = dwc2_readl(hsotg->regs + HPRT0);
-		prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
-		if (prtspd == HPRT0_SPD_HIGH_SPEED &&
-		    (dev_speed == USB_SPEED_LOW ||
-		     dev_speed == USB_SPEED_FULL)) {
-			qh->host_interval *= 8;
-			dwc2_sch_dbg(hsotg,
-				     "QH=%p init*8 nxt=%04x, fn=%04x, int=%#x\n",
-				     qh, qh->next_active_frame,
-				     hsotg->frame_number, qh->host_interval);
+		qh->host_us = NS_TO_US(usb_calc_bus_time(host_speed, ep_is_in,
+				       ep_is_isoc, bytecount));
+		device_ns = usb_calc_bus_time(dev_speed, ep_is_in,
+					      ep_is_isoc, bytecount);
 
-		}
-		dev_dbg(hsotg->dev, "interval=%d\n", qh->host_interval);
-	}
+		if (do_split && dwc_tt)
+			device_ns += dwc_tt->usb_tt->think_time;
+		qh->device_us = NS_TO_US(device_ns);
 
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH Initialized\n");
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - qh = %p\n", qh);
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Device Address = %d\n",
-		 dwc2_hcd_get_dev_addr(&urb->pipe_info));
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Endpoint %d, %s\n",
-		 dwc2_hcd_get_ep_num(&urb->pipe_info),
-		 dwc2_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT");
 
-	qh->dev_speed = dev_speed;
+		qh->device_interval = urb->interval;
+		qh->host_interval = urb->interval * (do_split ? 8 : 1);
+
+		/*
+		 * Schedule low speed if we're running the host in low or
+		 * full speed OR if we've got a "TT" to deal with to access this
+		 * device.
+		 */
+		qh->schedule_low_speed = prtspd != HPRT0_SPD_HIGH_SPEED ||
+					 dwc_tt;
+
+		if (do_split) {
+			/* We won't know num transfers until we schedule */
+			qh->num_hs_transfers = -1;
+		} else if (dev_speed == USB_SPEED_HIGH) {
+			qh->num_hs_transfers = 1;
+		} else {
+			qh->num_hs_transfers = 0;
+		}
+
+		/* We'll schedule later when we have something to do */
+	}
 
 	switch (dev_speed) {
 	case USB_SPEED_LOW:
@@ -701,7 +1537,6 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 		speed = "?";
 		break;
 	}
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Speed = %s\n", speed);
 
 	switch (qh->ep_type) {
 	case USB_ENDPOINT_XFER_ISOC:
@@ -721,13 +1556,21 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 		break;
 	}
 
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Type = %s\n", type);
-
-	if (qh->ep_type == USB_ENDPOINT_XFER_INT) {
-		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - usecs = %d\n",
-			 qh->host_us);
-		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - interval = %d\n",
-			 qh->host_interval);
+	dwc2_sch_dbg(hsotg, "QH=%p Init %s, %s speed, %d bytes:\n", qh, type,
+		     speed, bytecount);
+	dwc2_sch_dbg(hsotg, "QH=%p ...addr=%d, ep=%d, %s\n", qh,
+		     dwc2_hcd_get_dev_addr(&urb->pipe_info),
+		     dwc2_hcd_get_ep_num(&urb->pipe_info),
+		     ep_is_in ? "IN" : "OUT");
+	if (ep_is_int || ep_is_isoc) {
+		dwc2_sch_dbg(hsotg,
+			     "QH=%p ...duration: host=%d us, device=%d us\n",
+			     qh, qh->host_us, qh->device_us);
+		dwc2_sch_dbg(hsotg, "QH=%p ...interval: host=%d, device=%d\n",
+			     qh, qh->host_interval, qh->device_interval);
+		if (qh->schedule_low_speed)
+			dwc2_sch_dbg(hsotg, "QH=%p ...low speed schedule=%p\n",
+				     qh, dwc2_get_ls_map(hsotg, qh));
 	}
 }
 
@@ -755,7 +1598,7 @@ struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
 	if (!qh)
 		return NULL;
 
-	dwc2_qh_init(hsotg, qh, urb);
+	dwc2_qh_init(hsotg, qh, urb, mem_flags);
 
 	if (hsotg->core_params->dma_desc_enable > 0 &&
 	    dwc2_hcd_qh_init_ddma(hsotg, qh, mem_flags) < 0) {
@@ -787,6 +1630,7 @@ void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		dwc2_do_unreserve(hsotg, qh);
 		spin_unlock_irqrestore(&hsotg->lock, flags);
 	}
+	dwc2_host_put_tt_info(hsotg, qh->dwc_tt);
 
 	if (qh->desc_list)
 		dwc2_hcd_qh_free_ddma(hsotg, qh);
@@ -902,6 +1746,8 @@ static int dwc2_next_for_periodic_split(struct dwc2_hsotg *hsotg,
 	u16 incr;
 
 	/*
+	 * See dwc2_uframe_schedule_split() for split scheduling.
+	 *
 	 * Basically: increment 1 normally, but 2 right after the start split
 	 * (except for ISOC out).
 	 */
@@ -1004,9 +1850,17 @@ static int dwc2_next_periodic_start(struct dwc2_hsotg *hsotg,
 	if (qh->start_active_frame == qh->next_active_frame ||
 	    dwc2_frame_num_gt(prev_frame_number, qh->start_active_frame)) {
 		u16 ideal_start = qh->start_active_frame;
+		int periods_in_map;
 
-		/* Adjust interval as per gcd with plan length. */
-		interval = gcd(interval, 8);
+		/*
+		 * Adjust interval as per gcd with map size.
+		 * See pmap_schedule() for more details here.
+		 */
+		if (qh->do_split || qh->dev_speed == USB_SPEED_HIGH)
+			periods_in_map = DWC2_HS_SCHEDULE_UFRAMES;
+		else
+			periods_in_map = DWC2_LS_SCHEDULE_FRAMES;
+		interval = gcd(interval, periods_in_map);
 
 		do {
 			qh->start_active_frame = dwc2_frame_num_inc(
-- 
2.7.0.rc3.207.g0ac5344



From lkp at intel.com  Wed Jan 20 21:24:31 2016
From: lkp at intel.com (kbuild test robot)
Date: Thu, 21 Jan 2016 13:24:31 +0800
Subject: [PATCH v4 07/21] usb: dwc2: hcd: fix split transfer schedule
 sequence
In-Reply-To: <1453352672-27890-8-git-send-email-dianders@chromium.org>
Message-ID: <201601211352.VXgeFvup%fengguang.wu@intel.com>

Hi Douglas,

[auto build test ERROR on next-20160120]
[cannot apply to v4.4-rc8 v4.4-rc7 v4.4-rc6 v4.4]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]

url:    https://github.com/0day-ci/linux/commits/Douglas-Anderson/usb-dwc2-host-Fix-and-speed-up-all-the-stuff-especially-with-splits/20160121-131414
config: x86_64-randconfig-x019-01201142 (attached as .config)
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All errors (new ones prefixed by >>):

   drivers/usb/dwc2/core.c: In function 'dwc2_hc_start_transfer':
>> drivers/usb/dwc2/core.c:1876:17: error: 'struct dwc2_hsotg' has no member named 'split_order'
              &hsotg->split_order);
                    ^
--
>> /bin/bash: line 0: [: -ge: unary operator expected

vim +1876 drivers/usb/dwc2/core.c

  1870				ec_mc = 3;
  1871			else
  1872				ec_mc = 1;
  1873	
  1874			/* Put ourselves on the list to keep order straight */
  1875			list_move_tail(&chan->split_order_list_entry,
> 1876				       &hsotg->split_order);
  1877		} else {
  1878			if (dbg_hc(chan))
  1879				dev_vdbg(hsotg->dev, "no split\n");

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
-------------- next part --------------
A non-text attachment was scrubbed...
Name: .config.gz
Type: application/octet-stream
Size: 22712 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160121/5e0e33ca/attachment-0001.obj>

From lkp at intel.com  Wed Jan 20 21:49:56 2016
From: lkp at intel.com (kbuild test robot)
Date: Thu, 21 Jan 2016 13:49:56 +0800
Subject: [PATCH v4 20/21] usb: dwc2: host: Totally redo the microframe
 scheduler
In-Reply-To: <1453352672-27890-21-git-send-email-dianders@chromium.org>
Message-ID: <201601211347.ZCkJhHmY%fengguang.wu@intel.com>

Hi Douglas,

[auto build test ERROR on next-20160120]
[cannot apply to v4.4-rc8 v4.4-rc7 v4.4-rc6 v4.4]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]

url:    https://github.com/0day-ci/linux/commits/Douglas-Anderson/usb-dwc2-host-Fix-and-speed-up-all-the-stuff-especially-with-splits/20160121-131414
config: x86_64-randconfig-x019-01201142 (attached as .config)
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All errors (new ones prefixed by >>):

   In file included from drivers/usb/dwc2/core.c:57:0:
>> drivers/usb/dwc2/hcd.h:345:44: error: 'DWC2_HS_SCHEDULE_UFRAMES' undeclared here (not in a function)
     struct dwc2_hs_transfer_time hs_transfers[DWC2_HS_SCHEDULE_UFRAMES];
                                               ^
   drivers/usb/dwc2/core.c: In function 'dwc2_hc_start_transfer':
   drivers/usb/dwc2/core.c:1964:17: error: 'struct dwc2_hsotg' has no member named 'split_order'
              &hsotg->split_order);
                    ^
--
   In file included from drivers/usb/dwc2/core_intr.c:54:0:
>> drivers/usb/dwc2/hcd.h:345:44: error: 'DWC2_HS_SCHEDULE_UFRAMES' undeclared here (not in a function)
     struct dwc2_hs_transfer_time hs_transfers[DWC2_HS_SCHEDULE_UFRAMES];
                                               ^

vim +/DWC2_HS_SCHEDULE_UFRAMES +345 drivers/usb/dwc2/hcd.h

   339		u16 device_us;
   340		u16 host_interval;
   341		u16 device_interval;
   342		u16 next_active_frame;
   343		u16 start_active_frame;
   344		s16 num_hs_transfers;
 > 345		struct dwc2_hs_transfer_time hs_transfers[DWC2_HS_SCHEDULE_UFRAMES];
   346		u32 ls_start_schedule_slice;
   347		u16 ntd;
   348		struct list_head qtd_list;

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
-------------- next part --------------
A non-text attachment was scrubbed...
Name: .config.gz
Type: application/octet-stream
Size: 22712 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160121/5aa22088/attachment-0001.obj>

From andy.yan at rock-chips.com  Wed Jan 20 22:27:57 2016
From: andy.yan at rock-chips.com (Andy Yan)
Date: Thu, 21 Jan 2016 14:27:57 +0800
Subject: [PATCH v2 1/4] dt-bindings: power: reset: add document for
 reboot-mode driver
In-Reply-To: <20160120182840.GA15741@rob-hp-laptop>
References: <1452598029-8222-1-git-send-email-andy.yan@rock-chips.com>
 <1452598189-8272-1-git-send-email-andy.yan@rock-chips.com>
 <20160120182840.GA15741@rob-hp-laptop>
Message-ID: <56A07A6D.3010406@rock-chips.com>

Hi Rob:
    thanks for your review.
On 2016?01?21? 02:28, Rob Herring wrote:
> On Tue, Jan 12, 2016 at 07:29:49PM +0800, Andy Yan wrote:
>> add device tree binding document for reboot-mode driver
>>
>> Signed-off-by: Andy Yan <andy.yan at rock-chips.com>
>>
>> ---
>>
>> Changes in v2: None
>> Changes in v1: None
>>
>>   .../bindings/power/reset/reboot-mode.txt           | 41 +++++++++++++++++
>>   .../bindings/power/reset/syscon-reboot-mode.txt    | 52 ++++++++++++++++++++++
>>   2 files changed, 93 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/power/reset/reboot-mode.txt
>>   create mode 100644 Documentation/devicetree/bindings/power/reset/syscon-reboot-mode.txt
>>
>> diff --git a/Documentation/devicetree/bindings/power/reset/reboot-mode.txt b/Documentation/devicetree/bindings/power/reset/reboot-mode.txt
>> new file mode 100644
>> index 0000000..81d9f66
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/power/reset/reboot-mode.txt
>> @@ -0,0 +1,41 @@
>> +Generic reboot mode core map driver
>> +
>> +This driver get reboot mode arguments and call the write
>> +interface to stores the magic value in special register
>> +or ram . Then the bootloader can read it and take different
>> +action according to the argument stored.
>> +
>> +Required properties:
>> +- compatible: only support "syscon-reboot-mode" now.
>> +
>> +Each mode is represented as a sub-node of reboot_mode:
>> +
>> +Subnode required properties:
>> +- linux,mode: reboot mode command,such as "loader", "recovery", "fastboot".
>> +- loader,magic: magic number for the mode, this is vendor specific.
>> +
>> +Example:
>> +	reboot_mode {
> reboot-mode instead please.
>

     Sorry, I have already correct it in DT file, forget it here. It 
will be changed in next version.

>> +		compatible = "syscon-reboot-mode";
>> +		offset = <0x40>;
> This doc by itself is a little confusing. For example, is a child of the
> syscon node? I would remove offset (and perhaps compatible) from this
> example.

    Yes, is a child of a syscon mapped node. For example, Rockchip 
platform use a register of PMU(rk3066/rk3288) or GRF(rk3036), PMU and 
GRF are aleady mapped by syscon.
    offset and compatible are used by write interface driver like 
syscon-reboot-mode.c. If you don't like it appear in the core map doc, I 
will move it to the syscon-reboot-mode.txt?
>> +
>> +		loader {
>> +			linux,mode = "loader";
>> +			loader,magic = <BOOT_LOADER>;
>> +		};
> Sorry, my previous suggestion was not clear. I'm suggesting get rid of
> the subnodes and just do properties like this:
>
> loader = <BOOT_LOADER>;
> maskrom = <BOOT_MASKROM>;
>
> That's the same amount of information unless node names and linux,mode
> values are going to diverge. Do they need to? I can't see a reason.

     Because the command"linux,mode" and value"loader,magic" is vendor 
specific. I don't know what commands and how many mode other platform 
will use. So as John says in his reply, this sort of flexibility help us 
adapt the driver to different hardware/system environments.
>
> We need to be clear what loader means. More specifically, it is boot
> into bootloader shell.
     Actually, Rockchip platform will reboot into a bootloader download 
mode with this command. This mode can download faster than maskrom 
download mode.
>> +
>> +		maskrom {
> In theory, the bootrom could have multiple modes. This typically means a
> USB download mode. So perhaps a more precise name would be
> "rom-download".
>
> In chips I'm familiar with the bootrom mode is selected via a different
> mechanism than the secondary bootloader modes, but I suppose the same
> mechanism could be used.
    Yes , they use the same mechanism.
>> +			linux,mode = "maskrom";
>> +			loader,magic = <BOOT_MASKROM>;
>> +		};
>> +
>> +		recovery {
>> +			linux,mode = "recovery";
>> +			loader,magic = <BOOT_RECOVERY>;
>> +		};
>> +
>> +		fastboot {
>> +			linux,mode = "fastboot";
>> +			loader,magic = <BOOT_FASTBOOT>;
>> +		};
>
>
>




From zhengxing at rock-chips.com  Wed Jan 20 22:49:43 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Thu, 21 Jan 2016 14:49:43 +0800
Subject: [PATCH v2] clk: rockchip: Add support for multiple clock providers
In-Reply-To: <3395528.oy9qVuyESf@phil>
References: <1453280809-3120-1-git-send-email-zhengxing@rock-chips.com>
 <3395528.oy9qVuyESf@phil>
Message-ID: <56A07F87.2060400@rock-chips.com>

Hi, Heiko,
     Thank you for your reply.

On 2016?01?21? 07:38, Heiko Stuebner wrote:
> Hi,
>
> Am Mittwoch, 20. Januar 2016, 17:06:49 schrieb Xing Zheng:
>> There are need to support Multi-CRUs probability in future, but
>> it is not supported on the current Rockchip Clock Framework.
>>
>> Therefore, this patch add support a provider as the parameter
>> handler when we call the clock register functions for per CRU.
>>
>> Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
> overall this looks really nice. Thanks for following up on our talk so
> quickly :-) .
>
>> ---
>>
>> Changes in v2:
>> - Fix missed to call rockchip_clk_common_cru_init when SoCs clock init.
>>
>>   drivers/clk/rockchip/clk-rk3036.c |   15 +++--
>>   drivers/clk/rockchip/clk-rk3188.c |   40 ++++++++-----
>>   drivers/clk/rockchip/clk-rk3228.c |   15 +++--
>>   drivers/clk/rockchip/clk-rk3288.c |   17 ++++--
>>   drivers/clk/rockchip/clk-rk3368.c |   19 +++---
>>   drivers/clk/rockchip/clk.c        |  120
>> +++++++++++++++++++++++-------------- drivers/clk/rockchip/clk.h        |
>>    35 ++++++++---
>>   7 files changed, 170 insertions(+), 91 deletions(-)
>>
>> diff --git a/drivers/clk/rockchip/clk-rk3036.c
>> b/drivers/clk/rockchip/clk-rk3036.c index 483913b..050ad13 100644
>> --- a/drivers/clk/rockchip/clk-rk3036.c
>> +++ b/drivers/clk/rockchip/clk-rk3036.c
>> @@ -434,6 +434,7 @@ static const char *const rk3036_critical_clocks[]
>> __initconst = {
>>
>>   static void __init rk3036_clk_init(struct device_node *np)
>>   {
>> +	struct rockchip_clk_provider *ctx;
>>   	void __iomem *reg_base;
>>   	struct clk *clk;
>>
>> @@ -443,7 +444,9 @@ static void __init rk3036_clk_init(struct device_node
>> *np) return;
>>   	}
>>
>> -	rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
>> +	ctx = rockchip_clk_init(reg_base, CLK_NR_CLKS);
>> +
>> +	rockchip_clk_common_cru_init(np);
>>
>>   	/* xin12m is created by an cru-internal divider */
>>   	clk = clk_register_fixed_factor(NULL, "xin12m", "xin24m", 0, 1, 2);
>> @@ -473,15 +476,15 @@ static void __init rk3036_clk_init(struct
>> device_node *np) pr_warn("%s: could not register clock sclk_macref_out:
>> %ld\n", __func__, PTR_ERR(clk));
>>
>> -	rockchip_clk_register_plls(rk3036_pll_clks,
>> +	rockchip_clk_register_plls(ctx, rk3036_pll_clks,
>>   				   ARRAY_SIZE(rk3036_pll_clks),
>>   				   RK3036_GRF_SOC_STATUS0);
>> -	rockchip_clk_register_branches(rk3036_clk_branches,
>> +	rockchip_clk_register_branches(ctx, rk3036_clk_branches,
>>   				  ARRAY_SIZE(rk3036_clk_branches));
>>   	rockchip_clk_protect_critical(rk3036_critical_clocks,
>>   				      ARRAY_SIZE(rk3036_critical_clocks));
>>
>> -	rockchip_clk_register_armclk(ARMCLK, "armclk",
>> +	rockchip_clk_register_armclk(ctx, ARMCLK, "armclk",
>>   			mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
>>   			&rk3036_cpuclk_data, rk3036_cpuclk_rates,
>>   			ARRAY_SIZE(rk3036_cpuclk_rates));
>> @@ -489,6 +492,8 @@ static void __init rk3036_clk_init(struct device_node
>> *np) rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0),
>> ROCKCHIP_SOFTRST_HIWORD_MASK);
>>
>> -	rockchip_register_restart_notifier(RK2928_GLB_SRST_FST, NULL);
>> +	rockchip_register_restart_notifier(ctx, RK2928_GLB_SRST_FST, NULL);
>> +
>> +	rockchip_clk_of_add_provider(np, ctx);
>>   }
>>   CLK_OF_DECLARE(rk3036_cru, "rockchip,rk3036-cru", rk3036_clk_init);
>> diff --git a/drivers/clk/rockchip/clk-rk3188.c
>> b/drivers/clk/rockchip/clk-rk3188.c index 7f7444c..338a22e 100644
>> --- a/drivers/clk/rockchip/clk-rk3188.c
>> +++ b/drivers/clk/rockchip/clk-rk3188.c
>> @@ -750,18 +750,19 @@ static const char *const rk3188_critical_clocks[]
>> __initconst = { "pclk_peri",
>>   };
>>
>> -static void __init rk3188_common_clk_init(struct device_node *np)
>> +static struct rockchip_clk_provider *__init rk3188_common_clk_init(struct
>> device_node *np) {
>> +	struct rockchip_clk_provider *ctx;
>>   	void __iomem *reg_base;
>>   	struct clk *clk;
>>
>>   	reg_base = of_iomap(np, 0);
>> -	if (!reg_base) {
>> -		pr_err("%s: could not map cru region\n", __func__);
>> -		return;
>> -	}
>> +	if (!reg_base)
>> +		panic("%s: could not map cru region\n", __func__);
> I don't believe in doing panics everywhere :-), please do
> 	return ERR_PTR(-ENOMEM);
> here and in similar locations and let the top-most caller handle errors.
Done.
>> @@ -260,27 +261,49 @@ static struct clk
>> *rockchip_clk_register_frac_branch(const char *name, return clk;
>>   }
>>
>> -static DEFINE_SPINLOCK(clk_lock);
>> -static struct clk **clk_table;
>> -static void __iomem *reg_base;
>> -static struct clk_onecell_data clk_data;
>>   static struct device_node *cru_node;
> please also include the devicetree node into the ctx struct. That way we can
> keep track of all of them (when there are multiple).
In my opinion, I am a bit worried about the function 
rockchip_clk_get_grf, it just refer to
common CRU node to acquire global GRF regmap. And the function is 
non-parameter,
so, I think I will add ctx as parameter in the clk-pll.c.

And, there are the same feature between PLL lock status in the 
GRF_SOC_STATUSx and the
the lock bit of CRU_xPLL_CONx. They are coexsit on some SoCs like 
RK3036/RK3228/RK3288/RK3368,
there is only GRF_SOC_STATUSx on some SoCs like RK3066/RK3188.
AFAIK,  there are not the bits of PLL lock status in the GRF_SOC_STATUSx 
any more in future, like RK3399.
Therefore, we should add some patches to keep the checking PLL lock 
status compatibility.

BTW, it looks that there are too many place call the 
rockchip_clk_get_grf, and they looks
like unused? Should we remove them?
>>   static struct regmap *grf;
>>
>> -void __init rockchip_clk_init(struct device_node *np, void __iomem *base,
>> -			      unsigned long nr_clks)
>> +struct rockchip_clk_provider *__init rockchip_clk_init(
>> +			void __iomem *base, unsigned long nr_clks)
>>   {
>> -	reg_base = base;
>> -	cru_node = np;
>> -	grf = ERR_PTR(-EPROBE_DEFER);
>> +	struct rockchip_clk_provider *ctx;
>> +	struct clk **clk_table;
>> +	int i;
>> +
>> +	ctx = kzalloc(sizeof(struct rockchip_clk_provider), GFP_KERNEL);
>> +	if (!ctx)
>> +		panic("could not allocate clock provider context.\n");
> as I wrote above, please return a real error here. Especially when there are
> multiple clock controllers, it might actually get a bit farther in cases
> when only one of them fails, which might help in debugging underlying issues
> in such cases.
Done.
>>   	clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL);
>>   	if (!clk_table)
>> -		pr_err("%s: could not allocate clock lookup table\n", __func__);
>> +		panic("could not allocate clock lookup table\n");
> same here (and kfree the ctx from above before returning)
Done.
>> +
>> +	for (i = 0; i < nr_clks; ++i)
>> +		clk_table[i] = ERR_PTR(-ENOENT);
>> +
>> +	ctx->reg_base = base;
>> +	ctx->clk_data.clks = clk_table;
>> +	ctx->clk_data.clk_num = nr_clks;
>> +	spin_lock_init(&ctx->lock);
>> +
>> +	return ctx;
>> +}
>>
>> -	clk_data.clks = clk_table;
>> -	clk_data.clk_num = nr_clks;
>> -	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
>> +void __init rockchip_clk_common_cru_init(struct device_node *np)
>> +{
>> +	cru_node = np;
>> +	grf = ERR_PTR(-EPROBE_DEFER);
> I'm not sure why this is a separate function. Setting the node pointer (into
> the new struct as written above) can still happen in rockchip_clk_init, and
> initializing the global grf pointer as well.
Done.
>
> Heiko
Thanks.

- Xing Zheng




From zhengxing at rock-chips.com  Wed Jan 20 23:22:18 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Thu, 21 Jan 2016 15:22:18 +0800
Subject: [PATCH v3] clk: rockchip: Add support for multiple clock providers
Message-ID: <1453360938-10775-1-git-send-email-zhengxing@rock-chips.com>

There are need to support Multi-CRUs probability in future, but
it is not supported on the current Rockchip Clock Framework.

Therefore, this patch add support a provider as the parameter
handler when we call the clock register functions for per CRU.

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
---

Changes in v3:
- Remove panic, use the normal error codes for returning.
- The ctx struct include the cru_node and grf members for pll tracking.

Changes in v2:
- Fix missed to call rockchip_clk_common_cru_init when SoCs clock init.

 drivers/clk/rockchip/clk-pll.c    |   30 ++++----
 drivers/clk/rockchip/clk-rk3036.c |   17 +++--
 drivers/clk/rockchip/clk-rk3188.c |   48 +++++++++----
 drivers/clk/rockchip/clk-rk3228.c |   17 +++--
 drivers/clk/rockchip/clk-rk3288.c |   19 +++--
 drivers/clk/rockchip/clk-rk3368.c |   21 ++++--
 drivers/clk/rockchip/clk.c        |  144 +++++++++++++++++++++++--------------
 drivers/clk/rockchip/clk.h        |   49 +++++++++----
 8 files changed, 229 insertions(+), 116 deletions(-)

diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c
index b7e66c9..649db7d 100644
--- a/drivers/clk/rockchip/clk-pll.c
+++ b/drivers/clk/rockchip/clk-pll.c
@@ -46,6 +46,8 @@ struct rockchip_clk_pll {
 	const struct rockchip_pll_rate_table *rate_table;
 	unsigned int		rate_count;
 	spinlock_t		*lock;
+
+	struct rockchip_clk_provider *ctx;
 };
 
 #define to_rockchip_clk_pll(_hw) container_of(_hw, struct rockchip_clk_pll, hw)
@@ -90,7 +92,7 @@ static long rockchip_pll_round_rate(struct clk_hw *hw,
  */
 static int rockchip_pll_wait_lock(struct rockchip_clk_pll *pll)
 {
-	struct regmap *grf = rockchip_clk_get_grf();
+	struct regmap *grf = rockchip_clk_get_grf(pll->ctx);
 	unsigned int val;
 	int delay = 24000000, ret;
 
@@ -246,7 +248,7 @@ static int rockchip_rk3036_pll_set_rate(struct clk_hw *hw, unsigned long drate,
 	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
 	const struct rockchip_pll_rate_table *rate;
 	unsigned long old_rate = rockchip_rk3036_pll_recalc_rate(hw, prate);
-	struct regmap *grf = rockchip_clk_get_grf();
+	struct regmap *grf = rockchip_clk_get_grf(pll->ctx);
 
 	if (IS_ERR(grf)) {
 		pr_debug("%s: grf regmap not available, aborting rate change\n",
@@ -485,7 +487,7 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate,
 	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
 	const struct rockchip_pll_rate_table *rate;
 	unsigned long old_rate = rockchip_rk3066_pll_recalc_rate(hw, prate);
-	struct regmap *grf = rockchip_clk_get_grf();
+	struct regmap *grf = rockchip_clk_get_grf(pll->ctx);
 
 	if (IS_ERR(grf)) {
 		pr_debug("%s: grf regmap not available, aborting rate change\n",
@@ -558,7 +560,7 @@ static void rockchip_rk3066_pll_init(struct clk_hw *hw)
 		 rate->no, cur.no, rate->nf, cur.nf, rate->nb, cur.nb);
 	if (rate->nr != cur.nr || rate->no != cur.no || rate->nf != cur.nf
 						     || rate->nb != cur.nb) {
-		struct regmap *grf = rockchip_clk_get_grf();
+		struct regmap *grf = rockchip_clk_get_grf(pll->ctx);
 
 		if (IS_ERR(grf))
 			return;
@@ -590,12 +592,13 @@ static const struct clk_ops rockchip_rk3066_pll_clk_ops = {
  * Common registering of pll clocks
  */
 
-struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
+struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
+		enum rockchip_pll_type pll_type,
 		const char *name, const char *const *parent_names,
-		u8 num_parents, void __iomem *base, int con_offset,
-		int grf_lock_offset, int lock_shift, int mode_offset,
-		int mode_shift, struct rockchip_pll_rate_table *rate_table,
-		u8 clk_pll_flags, spinlock_t *lock)
+		u8 num_parents, int con_offset, int grf_lock_offset,
+		int lock_shift, int mode_offset, int mode_shift,
+		struct rockchip_pll_rate_table *rate_table,
+		u8 clk_pll_flags)
 {
 	const char *pll_parents[3];
 	struct clk_init_data init;
@@ -619,11 +622,11 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
 	/* create the mux on top of the real pll */
 	pll->pll_mux_ops = &clk_mux_ops;
 	pll_mux = &pll->pll_mux;
-	pll_mux->reg = base + mode_offset;
+	pll_mux->reg = ctx->reg_base + mode_offset;
 	pll_mux->shift = mode_shift;
 	pll_mux->mask = PLL_MODE_MASK;
 	pll_mux->flags = 0;
-	pll_mux->lock = lock;
+	pll_mux->lock = &ctx->lock;
 	pll_mux->hw.init = &init;
 
 	if (pll_type == pll_rk3036 || pll_type == pll_rk3066)
@@ -690,11 +693,12 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
 
 	pll->hw.init = &init;
 	pll->type = pll_type;
-	pll->reg_base = base + con_offset;
+	pll->reg_base = ctx->reg_base + con_offset;
 	pll->lock_offset = grf_lock_offset;
 	pll->lock_shift = lock_shift;
 	pll->flags = clk_pll_flags;
-	pll->lock = lock;
+	pll->lock = &ctx->lock;
+	pll->ctx = ctx;
 
 	pll_clk = clk_register(NULL, &pll->hw);
 	if (IS_ERR(pll_clk)) {
diff --git a/drivers/clk/rockchip/clk-rk3036.c b/drivers/clk/rockchip/clk-rk3036.c
index 483913b..3df5893 100644
--- a/drivers/clk/rockchip/clk-rk3036.c
+++ b/drivers/clk/rockchip/clk-rk3036.c
@@ -434,6 +434,7 @@ static const char *const rk3036_critical_clocks[] __initconst = {
 
 static void __init rk3036_clk_init(struct device_node *np)
 {
+	struct rockchip_clk_provider *ctx;
 	void __iomem *reg_base;
 	struct clk *clk;
 
@@ -443,7 +444,11 @@ static void __init rk3036_clk_init(struct device_node *np)
 		return;
 	}
 
-	rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
+	ctx = rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
+	if (IS_ERR(ctx)) {
+		pr_err("%s: rockchip clk init failed\n", __func__);
+		return;
+	}
 
 	/* xin12m is created by an cru-internal divider */
 	clk = clk_register_fixed_factor(NULL, "xin12m", "xin24m", 0, 1, 2);
@@ -473,15 +478,15 @@ static void __init rk3036_clk_init(struct device_node *np)
 		pr_warn("%s: could not register clock sclk_macref_out: %ld\n",
 			__func__, PTR_ERR(clk));
 
-	rockchip_clk_register_plls(rk3036_pll_clks,
+	rockchip_clk_register_plls(ctx, rk3036_pll_clks,
 				   ARRAY_SIZE(rk3036_pll_clks),
 				   RK3036_GRF_SOC_STATUS0);
-	rockchip_clk_register_branches(rk3036_clk_branches,
+	rockchip_clk_register_branches(ctx, rk3036_clk_branches,
 				  ARRAY_SIZE(rk3036_clk_branches));
 	rockchip_clk_protect_critical(rk3036_critical_clocks,
 				      ARRAY_SIZE(rk3036_critical_clocks));
 
-	rockchip_clk_register_armclk(ARMCLK, "armclk",
+	rockchip_clk_register_armclk(ctx, ARMCLK, "armclk",
 			mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
 			&rk3036_cpuclk_data, rk3036_cpuclk_rates,
 			ARRAY_SIZE(rk3036_cpuclk_rates));
@@ -489,6 +494,8 @@ static void __init rk3036_clk_init(struct device_node *np)
 	rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0),
 				  ROCKCHIP_SOFTRST_HIWORD_MASK);
 
-	rockchip_register_restart_notifier(RK2928_GLB_SRST_FST, NULL);
+	rockchip_register_restart_notifier(ctx, RK2928_GLB_SRST_FST, NULL);
+
+	rockchip_clk_of_add_provider(np, ctx);
 }
 CLK_OF_DECLARE(rk3036_cru, "rockchip,rk3036-cru", rk3036_clk_init);
diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c
index 7f7444c..adb08b2 100644
--- a/drivers/clk/rockchip/clk-rk3188.c
+++ b/drivers/clk/rockchip/clk-rk3188.c
@@ -750,18 +750,23 @@ static const char *const rk3188_critical_clocks[] __initconst = {
 	"pclk_peri",
 };
 
-static void __init rk3188_common_clk_init(struct device_node *np)
+static struct rockchip_clk_provider *__init rk3188_common_clk_init(struct device_node *np)
 {
+	struct rockchip_clk_provider *ctx;
 	void __iomem *reg_base;
 	struct clk *clk;
 
 	reg_base = of_iomap(np, 0);
 	if (!reg_base) {
 		pr_err("%s: could not map cru region\n", __func__);
-		return;
+		return ERR_PTR(-ENOMEM);
 	}
 
-	rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
+	ctx = rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
+	if (IS_ERR(ctx)) {
+		pr_err("%s: rockchip clk init failed\n", __func__);
+		return ERR_PTR(-ENOMEM);
+	}
 
 	/* xin12m is created by an cru-internal divider */
 	clk = clk_register_fixed_factor(NULL, "xin12m", "xin24m", 0, 1, 2);
@@ -774,45 +779,61 @@ static void __init rk3188_common_clk_init(struct device_node *np)
 		pr_warn("%s: could not register clock usb480m: %ld\n",
 			__func__, PTR_ERR(clk));
 
-	rockchip_clk_register_branches(common_clk_branches,
+	rockchip_clk_register_branches(ctx, common_clk_branches,
 				  ARRAY_SIZE(common_clk_branches));
 
 	rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0),
 				  ROCKCHIP_SOFTRST_HIWORD_MASK);
 
-	rockchip_register_restart_notifier(RK2928_GLB_SRST_FST, NULL);
+	rockchip_register_restart_notifier(ctx, RK2928_GLB_SRST_FST, NULL);
+
+	return ctx;
 }
 
 static void __init rk3066a_clk_init(struct device_node *np)
 {
-	rk3188_common_clk_init(np);
-	rockchip_clk_register_plls(rk3066_pll_clks,
+	struct rockchip_clk_provider *ctx;
+
+	ctx = rk3188_common_clk_init(np);
+	if (IS_ERR(ctx)) {
+		pr_err("%s: common clk init failed\n", __func__);
+		return;
+	}
+
+	rockchip_clk_register_plls(ctx, rk3066_pll_clks,
 				   ARRAY_SIZE(rk3066_pll_clks),
 				   RK3066_GRF_SOC_STATUS);
-	rockchip_clk_register_branches(rk3066a_clk_branches,
+	rockchip_clk_register_branches(ctx, rk3066a_clk_branches,
 				  ARRAY_SIZE(rk3066a_clk_branches));
-	rockchip_clk_register_armclk(ARMCLK, "armclk",
+	rockchip_clk_register_armclk(ctx, ARMCLK, "armclk",
 			mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
 			&rk3066_cpuclk_data, rk3066_cpuclk_rates,
 			ARRAY_SIZE(rk3066_cpuclk_rates));
 	rockchip_clk_protect_critical(rk3188_critical_clocks,
 				      ARRAY_SIZE(rk3188_critical_clocks));
+	rockchip_clk_of_add_provider(np, ctx);
 }
 CLK_OF_DECLARE(rk3066a_cru, "rockchip,rk3066a-cru", rk3066a_clk_init);
 
 static void __init rk3188a_clk_init(struct device_node *np)
 {
+	struct rockchip_clk_provider *ctx;
 	struct clk *clk1, *clk2;
 	unsigned long rate;
 	int ret;
 
-	rk3188_common_clk_init(np);
-	rockchip_clk_register_plls(rk3188_pll_clks,
+	ctx = rk3188_common_clk_init(np);
+	if (IS_ERR(ctx)) {
+		pr_err("%s: common clk init failed\n", __func__);
+		return;
+	}
+
+	rockchip_clk_register_plls(ctx, rk3188_pll_clks,
 				   ARRAY_SIZE(rk3188_pll_clks),
 				   RK3188_GRF_SOC_STATUS);
-	rockchip_clk_register_branches(rk3188_clk_branches,
+	rockchip_clk_register_branches(ctx, rk3188_clk_branches,
 				  ARRAY_SIZE(rk3188_clk_branches));
-	rockchip_clk_register_armclk(ARMCLK, "armclk",
+	rockchip_clk_register_armclk(ctx, ARMCLK, "armclk",
 				  mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
 				  &rk3188_cpuclk_data, rk3188_cpuclk_rates,
 				  ARRAY_SIZE(rk3188_cpuclk_rates));
@@ -836,6 +857,7 @@ static void __init rk3188a_clk_init(struct device_node *np)
 
 	rockchip_clk_protect_critical(rk3188_critical_clocks,
 				      ARRAY_SIZE(rk3188_critical_clocks));
+	rockchip_clk_of_add_provider(np, ctx);
 }
 CLK_OF_DECLARE(rk3188a_cru, "rockchip,rk3188a-cru", rk3188a_clk_init);
 
diff --git a/drivers/clk/rockchip/clk-rk3228.c b/drivers/clk/rockchip/clk-rk3228.c
index 981a502..3bf572e 100644
--- a/drivers/clk/rockchip/clk-rk3228.c
+++ b/drivers/clk/rockchip/clk-rk3228.c
@@ -623,6 +623,7 @@ static const char *const rk3228_critical_clocks[] __initconst = {
 
 static void __init rk3228_clk_init(struct device_node *np)
 {
+	struct rockchip_clk_provider *ctx;
 	void __iomem *reg_base;
 	struct clk *clk;
 
@@ -632,7 +633,11 @@ static void __init rk3228_clk_init(struct device_node *np)
 		return;
 	}
 
-	rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
+	ctx = rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
+	if (IS_ERR(ctx)) {
+		pr_err("%s: rockchip clk init failed\n", __func__);
+		return;
+	}
 
 	/* xin12m is created by an cru-internal divider */
 	clk = clk_register_fixed_factor(NULL, "xin12m", "xin24m", 0, 1, 2);
@@ -657,15 +662,15 @@ static void __init rk3228_clk_init(struct device_node *np)
 		pr_warn("%s: could not register clock hclk_rkvdec_pre: %ld\n",
 			__func__, PTR_ERR(clk));
 
-	rockchip_clk_register_plls(rk3228_pll_clks,
+	rockchip_clk_register_plls(ctx, rk3228_pll_clks,
 				   ARRAY_SIZE(rk3228_pll_clks),
 				   RK3228_GRF_SOC_STATUS0);
-	rockchip_clk_register_branches(rk3228_clk_branches,
+	rockchip_clk_register_branches(ctx, rk3228_clk_branches,
 				  ARRAY_SIZE(rk3228_clk_branches));
 	rockchip_clk_protect_critical(rk3228_critical_clocks,
 				      ARRAY_SIZE(rk3228_critical_clocks));
 
-	rockchip_clk_register_armclk(ARMCLK, "armclk",
+	rockchip_clk_register_armclk(ctx, ARMCLK, "armclk",
 			mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
 			&rk3228_cpuclk_data, rk3228_cpuclk_rates,
 			ARRAY_SIZE(rk3228_cpuclk_rates));
@@ -673,6 +678,8 @@ static void __init rk3228_clk_init(struct device_node *np)
 	rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0),
 				  ROCKCHIP_SOFTRST_HIWORD_MASK);
 
-	rockchip_register_restart_notifier(RK3228_GLB_SRST_FST, NULL);
+	rockchip_register_restart_notifier(ctx, RK3228_GLB_SRST_FST, NULL);
+
+	rockchip_clk_of_add_provider(np, ctx);
 }
 CLK_OF_DECLARE(rk3228_cru, "rockchip,rk3228-cru", rk3228_clk_init);
diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c
index 984fc18..bc87146 100644
--- a/drivers/clk/rockchip/clk-rk3288.c
+++ b/drivers/clk/rockchip/clk-rk3288.c
@@ -878,6 +878,7 @@ static struct syscore_ops rk3288_clk_syscore_ops = {
 
 static void __init rk3288_clk_init(struct device_node *np)
 {
+	struct rockchip_clk_provider *ctx;
 	struct clk *clk;
 
 	rk3288_cru_base = of_iomap(np, 0);
@@ -886,7 +887,11 @@ static void __init rk3288_clk_init(struct device_node *np)
 		return;
 	}
 
-	rockchip_clk_init(np, rk3288_cru_base, CLK_NR_CLKS);
+	ctx = rockchip_clk_init(np, rk3288_cru_base, CLK_NR_CLKS);
+	if (IS_ERR(ctx)) {
+		pr_err("%s: rockchip clk init failed\n", __func__);
+		return;
+	}
 
 	/* xin12m is created by an cru-internal divider */
 	clk = clk_register_fixed_factor(NULL, "xin12m", "xin24m", 0, 1, 2);
@@ -912,17 +917,17 @@ static void __init rk3288_clk_init(struct device_node *np)
 		pr_warn("%s: could not register clock pclk_wdt: %ld\n",
 			__func__, PTR_ERR(clk));
 	else
-		rockchip_clk_add_lookup(clk, PCLK_WDT);
+		rockchip_clk_add_lookup(ctx, clk, PCLK_WDT);
 
-	rockchip_clk_register_plls(rk3288_pll_clks,
+	rockchip_clk_register_plls(ctx, rk3288_pll_clks,
 				   ARRAY_SIZE(rk3288_pll_clks),
 				   RK3288_GRF_SOC_STATUS1);
-	rockchip_clk_register_branches(rk3288_clk_branches,
+	rockchip_clk_register_branches(ctx, rk3288_clk_branches,
 				  ARRAY_SIZE(rk3288_clk_branches));
 	rockchip_clk_protect_critical(rk3288_critical_clocks,
 				      ARRAY_SIZE(rk3288_critical_clocks));
 
-	rockchip_clk_register_armclk(ARMCLK, "armclk",
+	rockchip_clk_register_armclk(ctx, ARMCLK, "armclk",
 			mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
 			&rk3288_cpuclk_data, rk3288_cpuclk_rates,
 			ARRAY_SIZE(rk3288_cpuclk_rates));
@@ -931,8 +936,10 @@ static void __init rk3288_clk_init(struct device_node *np)
 				  rk3288_cru_base + RK3288_SOFTRST_CON(0),
 				  ROCKCHIP_SOFTRST_HIWORD_MASK);
 
-	rockchip_register_restart_notifier(RK3288_GLB_SRST_FST,
+	rockchip_register_restart_notifier(ctx, RK3288_GLB_SRST_FST,
 					   rk3288_clk_shutdown);
 	register_syscore_ops(&rk3288_clk_syscore_ops);
+
+	rockchip_clk_of_add_provider(np, ctx);
 }
 CLK_OF_DECLARE(rk3288_cru, "rockchip,rk3288-cru", rk3288_clk_init);
diff --git a/drivers/clk/rockchip/clk-rk3368.c b/drivers/clk/rockchip/clk-rk3368.c
index be0ede5..0b68338 100644
--- a/drivers/clk/rockchip/clk-rk3368.c
+++ b/drivers/clk/rockchip/clk-rk3368.c
@@ -831,6 +831,7 @@ static const char *const rk3368_critical_clocks[] __initconst = {
 
 static void __init rk3368_clk_init(struct device_node *np)
 {
+	struct rockchip_clk_provider *ctx;
 	void __iomem *reg_base;
 	struct clk *clk;
 
@@ -840,7 +841,11 @@ static void __init rk3368_clk_init(struct device_node *np)
 		return;
 	}
 
-	rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
+	ctx = rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
+	if (IS_ERR(ctx)) {
+		pr_err("%s: rockchip clk init failed\n", __func__);
+		return;
+	}
 
 	/* xin12m is created by a cru-internal divider */
 	clk = clk_register_fixed_factor(NULL, "xin12m", "xin24m", 0, 1, 2);
@@ -866,22 +871,22 @@ static void __init rk3368_clk_init(struct device_node *np)
 		pr_warn("%s: could not register clock pclk_wdt: %ld\n",
 			__func__, PTR_ERR(clk));
 	else
-		rockchip_clk_add_lookup(clk, PCLK_WDT);
+		rockchip_clk_add_lookup(ctx, clk, PCLK_WDT);
 
-	rockchip_clk_register_plls(rk3368_pll_clks,
+	rockchip_clk_register_plls(ctx, rk3368_pll_clks,
 				   ARRAY_SIZE(rk3368_pll_clks),
 				   RK3368_GRF_SOC_STATUS0);
-	rockchip_clk_register_branches(rk3368_clk_branches,
+	rockchip_clk_register_branches(ctx, rk3368_clk_branches,
 				  ARRAY_SIZE(rk3368_clk_branches));
 	rockchip_clk_protect_critical(rk3368_critical_clocks,
 				      ARRAY_SIZE(rk3368_critical_clocks));
 
-	rockchip_clk_register_armclk(ARMCLKB, "armclkb",
+	rockchip_clk_register_armclk(ctx, ARMCLKB, "armclkb",
 			mux_armclkb_p, ARRAY_SIZE(mux_armclkb_p),
 			&rk3368_cpuclkb_data, rk3368_cpuclkb_rates,
 			ARRAY_SIZE(rk3368_cpuclkb_rates));
 
-	rockchip_clk_register_armclk(ARMCLKL, "armclkl",
+	rockchip_clk_register_armclk(ctx, ARMCLKL, "armclkl",
 			mux_armclkl_p, ARRAY_SIZE(mux_armclkl_p),
 			&rk3368_cpuclkl_data, rk3368_cpuclkl_rates,
 			ARRAY_SIZE(rk3368_cpuclkl_rates));
@@ -889,6 +894,8 @@ static void __init rk3368_clk_init(struct device_node *np)
 	rockchip_register_softrst(np, 15, reg_base + RK3368_SOFTRST_CON(0),
 				  ROCKCHIP_SOFTRST_HIWORD_MASK);
 
-	rockchip_register_restart_notifier(RK3368_GLB_SRST_FST, NULL);
+	rockchip_register_restart_notifier(ctx, RK3368_GLB_SRST_FST, NULL);
+
+	rockchip_clk_of_add_provider(np, ctx);
 }
 CLK_OF_DECLARE(rk3368_cru, "rockchip,rk3368-cru", rk3368_clk_init);
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
index d9a0b5d..a1e9921 100644
--- a/drivers/clk/rockchip/clk.c
+++ b/drivers/clk/rockchip/clk.c
@@ -2,6 +2,9 @@
  * Copyright (c) 2014 MundoReader S.L.
  * Author: Heiko Stuebner <heiko at sntech.de>
  *
+ * Copyright (c) 2016 Rockchip Electronics Co. Ltd.
+ * Author: Xing Zheng <zhengxing at rock-chips.com>
+ *
  * based on
  *
  * samsung/clk.c
@@ -150,7 +153,8 @@ static int rockchip_clk_frac_notifier_cb(struct notifier_block *nb,
 	return notifier_from_errno(ret);
 }
 
-static struct clk *rockchip_clk_register_frac_branch(const char *name,
+static struct clk *rockchip_clk_register_frac_branch(
+		struct rockchip_clk_provider *ctx, const char *name,
 		const char *const *parent_names, u8 num_parents,
 		void __iomem *base, int muxdiv_offset, u8 div_flags,
 		int gate_offset, u8 gate_shift, u8 gate_flags,
@@ -243,7 +247,7 @@ static struct clk *rockchip_clk_register_frac_branch(const char *name,
 		if (IS_ERR(mux_clk))
 			return clk;
 
-		rockchip_clk_add_lookup(mux_clk, child->id);
+		rockchip_clk_add_lookup(ctx, mux_clk, child->id);
 
 		/* notifier on the fraction divider to catch rate changes */
 		if (frac->mux_frac_idx >= 0) {
@@ -260,66 +264,94 @@ static struct clk *rockchip_clk_register_frac_branch(const char *name,
 	return clk;
 }
 
-static DEFINE_SPINLOCK(clk_lock);
-static struct clk **clk_table;
-static void __iomem *reg_base;
-static struct clk_onecell_data clk_data;
-static struct device_node *cru_node;
-static struct regmap *grf;
-
-void __init rockchip_clk_init(struct device_node *np, void __iomem *base,
-			      unsigned long nr_clks)
+struct rockchip_clk_provider *__init rockchip_clk_init(struct device_node *np,
+			void __iomem *base, unsigned long nr_clks)
 {
-	reg_base = base;
-	cru_node = np;
-	grf = ERR_PTR(-EPROBE_DEFER);
+	struct rockchip_clk_provider *ctx;
+	struct clk **clk_table;
+	int i;
+
+	ctx = kzalloc(sizeof(struct rockchip_clk_provider), GFP_KERNEL);
+	if (!ctx) {
+		pr_err("%s: Could not allocate clock provider context\n",
+			__func__);
+		return ERR_PTR(-ENOMEM);
+	}
 
 	clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL);
-	if (!clk_table)
-		pr_err("%s: could not allocate clock lookup table\n", __func__);
+	if (!clk_table) {
+		pr_err("%s: Could not allocate clock lookup table\n",
+			__func__);
+		goto err_free;
+	}
 
-	clk_data.clks = clk_table;
-	clk_data.clk_num = nr_clks;
-	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+	for (i = 0; i < nr_clks; ++i)
+		clk_table[i] = ERR_PTR(-ENOENT);
+
+	ctx->reg_base = base;
+	ctx->clk_data.clks = clk_table;
+	ctx->clk_data.clk_num = nr_clks;
+	ctx->cru_node = np;
+	ctx->grf = ERR_PTR(-EPROBE_DEFER);
+	spin_lock_init(&ctx->lock);
+
+	return ctx;
+
+err_free:
+	kfree(ctx);
+	return ERR_PTR(-ENOMEM);
+}
+
+void __init rockchip_clk_of_add_provider(struct device_node *np,
+				struct rockchip_clk_provider *ctx)
+{
+	if (np) {
+		if (of_clk_add_provider(np, of_clk_src_onecell_get,
+					&ctx->clk_data))
+			panic("could not register clk provider\n");
+	}
 }
 
-struct regmap *rockchip_clk_get_grf(void)
+struct regmap *rockchip_clk_get_grf(struct rockchip_clk_provider *ctx)
 {
-	if (IS_ERR(grf))
-		grf = syscon_regmap_lookup_by_phandle(cru_node, "rockchip,grf");
-	return grf;
+	if (IS_ERR(ctx->grf))
+		ctx->grf = syscon_regmap_lookup_by_phandle(ctx->cru_node, "rockchip,grf");
+	return ctx->grf;
 }
 
-void rockchip_clk_add_lookup(struct clk *clk, unsigned int id)
+void rockchip_clk_add_lookup(struct rockchip_clk_provider *ctx,
+			     struct clk *clk, unsigned int id)
 {
-	if (clk_table && id)
-		clk_table[id] = clk;
+	if (ctx->clk_data.clks && id)
+		ctx->clk_data.clks[id] = clk;
 }
 
-void __init rockchip_clk_register_plls(struct rockchip_pll_clock *list,
+void __init rockchip_clk_register_plls(struct rockchip_clk_provider *ctx,
+				struct rockchip_pll_clock *list,
 				unsigned int nr_pll, int grf_lock_offset)
 {
 	struct clk *clk;
 	int idx;
 
 	for (idx = 0; idx < nr_pll; idx++, list++) {
-		clk = rockchip_clk_register_pll(list->type, list->name,
+		clk = rockchip_clk_register_pll(ctx, list->type, list->name,
 				list->parent_names, list->num_parents,
-				reg_base, list->con_offset, grf_lock_offset,
+				list->con_offset, grf_lock_offset,
 				list->lock_shift, list->mode_offset,
 				list->mode_shift, list->rate_table,
-				list->pll_flags, &clk_lock);
+				list->pll_flags);
 		if (IS_ERR(clk)) {
 			pr_err("%s: failed to register clock %s\n", __func__,
 				list->name);
 			continue;
 		}
 
-		rockchip_clk_add_lookup(clk, list->id);
+		rockchip_clk_add_lookup(ctx, clk, list->id);
 	}
 }
 
 void __init rockchip_clk_register_branches(
+				      struct rockchip_clk_provider *ctx,
 				      struct rockchip_clk_branch *list,
 				      unsigned int nr_clk)
 {
@@ -335,56 +367,56 @@ void __init rockchip_clk_register_branches(
 		case branch_mux:
 			clk = clk_register_mux(NULL, list->name,
 				list->parent_names, list->num_parents,
-				flags, reg_base + list->muxdiv_offset,
+				flags, ctx->reg_base + list->muxdiv_offset,
 				list->mux_shift, list->mux_width,
-				list->mux_flags, &clk_lock);
+				list->mux_flags, &ctx->lock);
 			break;
 		case branch_divider:
 			if (list->div_table)
 				clk = clk_register_divider_table(NULL,
 					list->name, list->parent_names[0],
-					flags, reg_base + list->muxdiv_offset,
+					flags, ctx->reg_base + list->muxdiv_offset,
 					list->div_shift, list->div_width,
 					list->div_flags, list->div_table,
-					&clk_lock);
+					&ctx->lock);
 			else
 				clk = clk_register_divider(NULL, list->name,
 					list->parent_names[0], flags,
-					reg_base + list->muxdiv_offset,
+					ctx->reg_base + list->muxdiv_offset,
 					list->div_shift, list->div_width,
-					list->div_flags, &clk_lock);
+					list->div_flags, &ctx->lock);
 			break;
 		case branch_fraction_divider:
-			clk = rockchip_clk_register_frac_branch(list->name,
+			clk = rockchip_clk_register_frac_branch(ctx, list->name,
 				list->parent_names, list->num_parents,
-				reg_base, list->muxdiv_offset, list->div_flags,
+				ctx->reg_base, list->muxdiv_offset, list->div_flags,
 				list->gate_offset, list->gate_shift,
 				list->gate_flags, flags, list->child,
-				&clk_lock);
+				&ctx->lock);
 			break;
 		case branch_gate:
 			flags |= CLK_SET_RATE_PARENT;
 
 			clk = clk_register_gate(NULL, list->name,
 				list->parent_names[0], flags,
-				reg_base + list->gate_offset,
-				list->gate_shift, list->gate_flags, &clk_lock);
+				ctx->reg_base + list->gate_offset,
+				list->gate_shift, list->gate_flags, &ctx->lock);
 			break;
 		case branch_composite:
 			clk = rockchip_clk_register_branch(list->name,
 				list->parent_names, list->num_parents,
-				reg_base, list->muxdiv_offset, list->mux_shift,
+				ctx->reg_base, list->muxdiv_offset, list->mux_shift,
 				list->mux_width, list->mux_flags,
 				list->div_shift, list->div_width,
 				list->div_flags, list->div_table,
 				list->gate_offset, list->gate_shift,
-				list->gate_flags, flags, &clk_lock);
+				list->gate_flags, flags, &ctx->lock);
 			break;
 		case branch_mmc:
 			clk = rockchip_clk_register_mmc(
 				list->name,
 				list->parent_names, list->num_parents,
-				reg_base + list->muxdiv_offset,
+				ctx->reg_base + list->muxdiv_offset,
 				list->div_shift
 			);
 			break;
@@ -392,8 +424,8 @@ void __init rockchip_clk_register_branches(
 			clk = rockchip_clk_register_inverter(
 				list->name, list->parent_names,
 				list->num_parents,
-				reg_base + list->muxdiv_offset,
-				list->div_shift, list->div_flags, &clk_lock);
+				ctx->reg_base + list->muxdiv_offset,
+				list->div_shift, list->div_flags, &ctx->lock);
 			break;
 		}
 
@@ -410,11 +442,12 @@ void __init rockchip_clk_register_branches(
 			continue;
 		}
 
-		rockchip_clk_add_lookup(clk, list->id);
+		rockchip_clk_add_lookup(ctx, clk, list->id);
 	}
 }
 
-void __init rockchip_clk_register_armclk(unsigned int lookup_id,
+void __init rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx,
+			unsigned int lookup_id,
 			const char *name, const char *const *parent_names,
 			u8 num_parents,
 			const struct rockchip_cpuclk_reg_data *reg_data,
@@ -424,15 +457,15 @@ void __init rockchip_clk_register_armclk(unsigned int lookup_id,
 	struct clk *clk;
 
 	clk = rockchip_clk_register_cpuclk(name, parent_names, num_parents,
-					   reg_data, rates, nrates, reg_base,
-					   &clk_lock);
+					   reg_data, rates, nrates, ctx->reg_base,
+					   &ctx->lock);
 	if (IS_ERR(clk)) {
 		pr_err("%s: failed to register clock %s: %ld\n",
 		       __func__, name, PTR_ERR(clk));
 		return;
 	}
 
-	rockchip_clk_add_lookup(clk, lookup_id);
+	rockchip_clk_add_lookup(ctx, clk, lookup_id);
 }
 
 void __init rockchip_clk_protect_critical(const char *const clocks[],
@@ -449,6 +482,7 @@ void __init rockchip_clk_protect_critical(const char *const clocks[],
 	}
 }
 
+static void __iomem *rst_base;
 static unsigned int reg_restart;
 static void (*cb_restart)(void);
 static int rockchip_restart_notify(struct notifier_block *this,
@@ -457,7 +491,7 @@ static int rockchip_restart_notify(struct notifier_block *this,
 	if (cb_restart)
 		cb_restart();
 
-	writel(0xfdb9, reg_base + reg_restart);
+	writel(0xfdb9, rst_base + reg_restart);
 	return NOTIFY_DONE;
 }
 
@@ -466,10 +500,12 @@ static struct notifier_block rockchip_restart_handler = {
 	.priority = 128,
 };
 
-void __init rockchip_register_restart_notifier(unsigned int reg, void (*cb)(void))
+void __init rockchip_register_restart_notifier(struct rockchip_clk_provider *ctx,
+					       unsigned int reg, void (*cb)(void))
 {
 	int ret;
 
+	rst_base = ctx->reg_base;
 	reg_restart = reg;
 	cb_restart = cb;
 	ret = register_restart_handler(&rockchip_restart_handler);
diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
index ff8bd23..658de63 100644
--- a/drivers/clk/rockchip/clk.h
+++ b/drivers/clk/rockchip/clk.h
@@ -27,6 +27,7 @@
 #define CLK_ROCKCHIP_CLK_H
 
 #include <linux/io.h>
+#include <linux/clk-provider.h>
 
 struct clk;
 
@@ -127,6 +128,20 @@ enum rockchip_pll_type {
 	.nb = _nb,						\
 }
 
+/**
+ * struct rockchip_clk_provider: information about clock provider
+ * @reg_base: virtual address for the register base.
+ * @clk_data: holds clock related data like clk* and number of clocks.
+ * @lock: maintains exclusion between callbacks for a given clock-provider.
+ */
+struct rockchip_clk_provider {
+	void __iomem *reg_base;
+	struct clk_onecell_data clk_data;
+	struct device_node *cru_node;
+	struct regmap *grf;
+	spinlock_t lock;
+};
+
 struct rockchip_pll_rate_table {
 	unsigned long rate;
 	unsigned int nr;
@@ -194,12 +209,13 @@ struct rockchip_pll_clock {
 		.rate_table	= _rtable,				\
 	}
 
-struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
+struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
+		enum rockchip_pll_type pll_type,
 		const char *name, const char *const *parent_names,
-		u8 num_parents, void __iomem *base, int con_offset,
-		int grf_lock_offset, int lock_shift, int reg_mode,
-		int mode_shift, struct rockchip_pll_rate_table *rate_table,
-		u8 clk_pll_flags, spinlock_t *lock);
+		u8 num_parents, int con_offset, int grf_lock_offset,
+		int lock_shift, int mode_offset, int mode_shift,
+		struct rockchip_pll_rate_table *rate_table,
+		u8 clk_pll_flags);
 
 struct rockchip_cpuclk_clksel {
 	int reg;
@@ -508,21 +524,28 @@ struct rockchip_clk_branch {
 		.div_flags	= if,				\
 	}
 
-void rockchip_clk_init(struct device_node *np, void __iomem *base,
-		       unsigned long nr_clks);
-struct regmap *rockchip_clk_get_grf(void);
-void rockchip_clk_add_lookup(struct clk *clk, unsigned int id);
-void rockchip_clk_register_branches(struct rockchip_clk_branch *clk_list,
+struct rockchip_clk_provider *rockchip_clk_init(struct device_node *np,
+			void __iomem *base, unsigned long nr_clks);
+void rockchip_clk_of_add_provider(struct device_node *np,
+				struct rockchip_clk_provider *ctx);
+struct regmap *rockchip_clk_get_grf(struct rockchip_clk_provider *ctx);
+void rockchip_clk_add_lookup(struct rockchip_clk_provider *ctx,
+			     struct clk *clk, unsigned int id);
+void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
+				    struct rockchip_clk_branch *list,
 				    unsigned int nr_clk);
-void rockchip_clk_register_plls(struct rockchip_pll_clock *pll_list,
+void rockchip_clk_register_plls(struct rockchip_clk_provider *ctx,
+				struct rockchip_pll_clock *pll_list,
 				unsigned int nr_pll, int grf_lock_offset);
-void rockchip_clk_register_armclk(unsigned int lookup_id, const char *name,
+void rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx,
+			unsigned int lookup_id, const char *name,
 			const char *const *parent_names, u8 num_parents,
 			const struct rockchip_cpuclk_reg_data *reg_data,
 			const struct rockchip_cpuclk_rate_table *rates,
 			int nrates);
 void rockchip_clk_protect_critical(const char *const clocks[], int nclocks);
-void rockchip_register_restart_notifier(unsigned int reg, void (*cb)(void));
+void rockchip_register_restart_notifier(struct rockchip_clk_provider *ctx,
+					unsigned int reg, void (*cb)(void));
 
 #define ROCKCHIP_SOFTRST_HIWORD_MASK	BIT(0)
 
-- 
1.7.9.5




From matthias.bgg at gmail.com  Thu Jan 21 00:37:36 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Thu, 21 Jan 2016 09:37:36 +0100
Subject: [PATCH v2 2/4] power: reset: add reboot mode driver
In-Reply-To: <1452598319-8324-1-git-send-email-andy.yan@rock-chips.com>
References: <1452598029-8222-1-git-send-email-andy.yan@rock-chips.com>
 <1452598319-8324-1-git-send-email-andy.yan@rock-chips.com>
Message-ID: <56A098D0.6040205@gmail.com>



On 12/01/16 12:31, Andy Yan wrote:
> This driver parse the reboot commands like "reboot loader"
> and "reboot recovery" to get a boot mode described in the
> device tree , then call the write interfae to store the boot
> mode in some persistent storage  like special register or ram,
> which can be read by the bootloader after system reboot, then
> the bootloader can take different action according to the mode
> stored.
>
> This is commonly used on Android based devices, which in order
> to reboot the device into fastboot or recovery mode.
>
> Signed-off-by: Andy Yan <andy.yan at rock-chips.com>
>
> ---

Reviewed-by: Matthias Brugger <matthias.bgg at gmail.com>

>
> Changes in v2:
> - move to dir drivers/power/reset/
> - make syscon-reboot-mode a generic driver
>
> Changes in v1:
> - fix the embarrassed compile warning
> - correct the maskrom magic number
> - check for the normal reboot
>
>   drivers/power/reset/Kconfig              |  16 +++++
>   drivers/power/reset/Makefile             |   2 +
>   drivers/power/reset/reboot-mode.c        | 100 +++++++++++++++++++++++++++++++
>   drivers/power/reset/reboot-mode.h        |   6 ++
>   drivers/power/reset/syscon-reboot-mode.c |  62 +++++++++++++++++++
>   5 files changed, 186 insertions(+)
>   create mode 100644 drivers/power/reset/reboot-mode.c
>   create mode 100644 drivers/power/reset/reboot-mode.h
>   create mode 100644 drivers/power/reset/syscon-reboot-mode.c
>
> diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
> index 1131cf7..2e6d873 100644
> --- a/drivers/power/reset/Kconfig
> +++ b/drivers/power/reset/Kconfig
> @@ -173,5 +173,21 @@ config POWER_RESET_ZX
>   	help
>   	  Reboot support for ZTE SoCs.
>
> +config REBOOT_MODE
> +	tristate
> +	depends on OF
> +	help
> +	 This driver will help to pass the system reboot mode
> +	 to bootloader
> +
> +config SYSCON_REBOOT_MODE
> +	bool "Generic SYSCON regmap reboot mode driver"
> +	select REBOOT_MODE
> +	help
> +	 Say y here will enable reboot mode driver. This will
> +	 get reboot mode arguments and store it in SYSCON mapped
> +	 register, then the bootloader can read it to take different
> +	 action according to the mode.
> +
>   endif
>
> diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile
> index 096fa67..a63865b 100644
> --- a/drivers/power/reset/Makefile
> +++ b/drivers/power/reset/Makefile
> @@ -20,3 +20,5 @@ obj-$(CONFIG_POWER_RESET_SYSCON) += syscon-reboot.o
>   obj-$(CONFIG_POWER_RESET_SYSCON_POWEROFF) += syscon-poweroff.o
>   obj-$(CONFIG_POWER_RESET_RMOBILE) += rmobile-reset.o
>   obj-$(CONFIG_POWER_RESET_ZX) += zx-reboot.o
> +obj-$(CONFIG_REBOOT_MODE) += reboot-mode.o
> +obj-$(CONFIG_SYSCON_REBOOT_MODE) += syscon-reboot-mode.o
> diff --git a/drivers/power/reset/reboot-mode.c b/drivers/power/reset/reboot-mode.c
> new file mode 100644
> index 0000000..92b7b4d
> --- /dev/null
> +++ b/drivers/power/reset/reboot-mode.c
> @@ -0,0 +1,100 @@
> +/*
> + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +#include <linux/device.h>
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/reboot.h>
> +#include "reboot-mode.h"
> +
> +struct mode_info {
> +	const char *mode;
> +	int magic;
> +	struct list_head list;
> +};
> +
> +struct reboot_mode_driver {
> +	struct list_head head;
> +	int (*write)(int magic);
> +	struct notifier_block reboot_notifier;
> +};
> +
> +static int get_reboot_mode_magic(struct reboot_mode_driver *reboot,
> +				 const char *cmd)
> +{
> +	const char *normal = "normal";
> +	int magic = 0;
> +	struct mode_info *info;
> +
> +	if (!cmd)
> +		cmd = normal;
> +
> +	list_for_each_entry(info, &reboot->head, list) {
> +		if (!strcmp(info->mode, cmd)) {
> +			magic = info->magic;
> +			break;
> +		}
> +	}
> +
> +	return magic;
> +}
> +
> +static int reboot_mode_notify(struct notifier_block *this,
> +			      unsigned long mode, void *cmd)
> +{
> +	struct reboot_mode_driver *reboot;
> +	int magic;
> +
> +	reboot = container_of(this, struct reboot_mode_driver, reboot_notifier);
> +	magic = get_reboot_mode_magic(reboot, cmd);
> +	if (magic)
> +		reboot->write(magic);
> +
> +	return NOTIFY_DONE;
> +}
> +
> +int reboot_mode_register(struct device *dev, int (*write)(int))
> +{
> +	struct reboot_mode_driver *reboot;
> +	struct mode_info *info;
> +	struct device_node *child;
> +	int ret;
> +
> +	reboot = devm_kzalloc(dev, sizeof(*reboot), GFP_KERNEL);
> +	if (!reboot)
> +		return -ENOMEM;
> +
> +	reboot->write = write;
> +	INIT_LIST_HEAD(&reboot->head);
> +	for_each_child_of_node(dev->of_node, child) {
> +		info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
> +		if (!info)
> +			return -ENOMEM;
> +		info->mode = of_get_property(child, "linux,mode", NULL);
> +		if (of_property_read_u32(child, "loader,magic", &info->magic)) {
> +			dev_err(dev, "reboot mode %s without magic number\n",
> +				info->mode);
> +			devm_kfree(dev, info);
> +			continue;
> +		}
> +		list_add_tail(&info->list, &reboot->head);
> +	}
> +	reboot->reboot_notifier.notifier_call = reboot_mode_notify;
> +	ret = register_reboot_notifier(&reboot->reboot_notifier);
> +	if (ret)
> +		dev_err(dev, "can't register reboot notifier\n");
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(reboot_mode_register);
> +
> +MODULE_AUTHOR("Andy Yan <andy.yan at rock-chips.com");
> +MODULE_DESCRIPTION("System reboot mode driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/power/reset/reboot-mode.h b/drivers/power/reset/reboot-mode.h
> new file mode 100644
> index 0000000..76646e7
> --- /dev/null
> +++ b/drivers/power/reset/reboot-mode.h
> @@ -0,0 +1,6 @@
> +#ifndef __REBOOT_MODE_H__
> +#define __REBOOT_MODE_H__
> +
> +extern int reboot_mode_register(struct device *dev, int (*write)(int));
> +
> +#endif
> diff --git a/drivers/power/reset/syscon-reboot-mode.c b/drivers/power/reset/syscon-reboot-mode.c
> new file mode 100644
> index 0000000..b9370f3
> --- /dev/null
> +++ b/drivers/power/reset/syscon-reboot-mode.c
> @@ -0,0 +1,62 @@
> +/*
> + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/platform_device.h>
> +#include <linux/reboot.h>
> +#include <linux/regmap.h>
> +#include <linux/mfd/syscon.h>
> +#include "reboot-mode.h"
> +
> +static struct regmap *map;
> +static u32 offset;
> +
> +static int syscon_reboot_mode_write(int magic)
> +{
> +	regmap_write(map, offset, magic);
> +
> +	return 0;
> +}
> +
> +static int syscon_reboot_mode_probe(struct platform_device *pdev)
> +{
> +	int ret;
> +
> +	map = syscon_node_to_regmap(pdev->dev.parent->of_node);
> +	if (IS_ERR(map))
> +		return PTR_ERR(map);
> +	if (of_property_read_u32(pdev->dev.of_node, "offset", &offset))
> +		return -EINVAL;
> +	ret = reboot_mode_register(&pdev->dev, syscon_reboot_mode_write);
> +	if (ret)
> +		dev_err(&pdev->dev, "can't register reboot mode\n");
> +
> +	return ret;
> +}
> +
> +static const struct of_device_id syscon_reboot_mode_of_match[] = {
> +	{ .compatible = "syscon-reboot-mode" },
> +	{}
> +};
> +
> +static struct platform_driver syscon_reboot_mode_driver = {
> +	.probe = syscon_reboot_mode_probe,
> +	.driver = {
> +		.name = "syscon-reboot-mode",
> +		.of_match_table = syscon_reboot_mode_of_match,
> +	},
> +};
> +module_platform_driver(syscon_reboot_mode_driver);
> +
> +MODULE_AUTHOR("Andy Yan <andy.yan at rock-chips.com");
> +MODULE_DESCRIPTION("SYSCON reboot mode driver");
> +MODULE_LICENSE("GPL v2");
>


From matthias.bgg at gmail.com  Thu Jan 21 00:38:17 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Thu, 21 Jan 2016 09:38:17 +0100
Subject: [PATCH v2 3/4] ARM: dts: rockchip: add syscon-reboot-mode node
In-Reply-To: <1452598378-8371-1-git-send-email-andy.yan@rock-chips.com>
References: <1452598029-8222-1-git-send-email-andy.yan@rock-chips.com>
 <1452598378-8371-1-git-send-email-andy.yan@rock-chips.com>
Message-ID: <56A098F9.5020100@gmail.com>



On 12/01/16 12:32, Andy Yan wrote:
> Rockchip platform use a SYSCON mapped register store
> the reboot mode magic value for bootloader to use when
> system reboot. So add syscon-reboot-mode driver DT node
> for rk3xxx,rk3288 platform
>
> Signed-off-by: Andy Yan <andy.yan at rock-chips.com>
>
> ---

Reviewed-by: Matthias Brugger <matthias.bgg at gmail.com>

>
> Changes in v2:
> - make this node as a subnode of PMU
>
> Changes in v1:
> - correct the maskrom magic number
> - use macro defined in rockchip_boot-mode.h for reboot-mode DT node
>
>   arch/arm/boot/dts/rk3288.dtsi                | 31 ++++++++++++++++++++++++++
>   arch/arm/boot/dts/rk3xxx.dtsi                | 33 +++++++++++++++++++++++++++-
>   include/dt-bindings/soc/rockchip_boot-mode.h | 30 +++++++++++++++++++++++++
>   3 files changed, 93 insertions(+), 1 deletion(-)
>   create mode 100644 include/dt-bindings/soc/rockchip_boot-mode.h
>
> diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
> index 04ea209..4e49fb7 100644
> --- a/arch/arm/boot/dts/rk3288.dtsi
> +++ b/arch/arm/boot/dts/rk3288.dtsi
> @@ -45,6 +45,7 @@
>   #include <dt-bindings/clock/rk3288-cru.h>
>   #include <dt-bindings/thermal/thermal.h>
>   #include <dt-bindings/power/rk3288-power.h>
> +#include <dt-bindings/soc/rockchip_boot-mode.h>
>   #include "skeleton.dtsi"
>
>   / {
> @@ -712,6 +713,36 @@
>   				clocks = <&cru ACLK_GPU>;
>   			};
>   		};
> +
> +		reboot-mode {
> +			compatible = "syscon-reboot-mode";
> +			offset = <0x94>;
> +
> +			normal {
> +				linux,mode = "normal";
> +				loader,magic = <BOOT_NORMAL>;
> +			};
> +
> +			loader {
> +				linux,mode = "loader";
> +				loader,magic = <BOOT_LOADER>;
> +			};
> +
> +			maskrom {
> +				linux,mode = "maskrom";
> +				loader,magic = <BOOT_MASKROM>;
> +			};
> +
> +			recovery {
> +				linux,mode = "recovery";
> +				loader,magic = <BOOT_RECOVERY>;
> +			};
> +
> +			fastboot {
> +				linux,mode = "fastboot";
> +				loader,magic = <BOOT_FASTBOOT>;
> +			};
> +		};
>   	};
>
>   	sgrf: syscon at ff740000 {
> diff --git a/arch/arm/boot/dts/rk3xxx.dtsi b/arch/arm/boot/dts/rk3xxx.dtsi
> index 4497d28..58af546 100644
> --- a/arch/arm/boot/dts/rk3xxx.dtsi
> +++ b/arch/arm/boot/dts/rk3xxx.dtsi
> @@ -43,6 +43,7 @@
>
>   #include <dt-bindings/interrupt-controller/irq.h>
>   #include <dt-bindings/interrupt-controller/arm-gic.h>
> +#include <dt-bindings/soc/rockchip_boot-mode.h>
>   #include "skeleton.dtsi"
>
>   / {
> @@ -242,8 +243,38 @@
>   	};
>
>   	pmu: pmu at 20004000 {
> -		compatible = "rockchip,rk3066-pmu", "syscon";
> +		compatible = "rockchip,rk3066-pmu", "syscon", "simple-mfd";
>   		reg = <0x20004000 0x100>;
> +
> +		reboot-mode {
> +			compatible = "syscon-reboot-mode";
> +			offset = <0x40>;
> +
> +			normal {
> +				linux,mode = "normal";
> +				loader,magic = <BOOT_NORMAL>;
> +			};
> +
> +			loader {
> +				linux,mode = "loader";
> +				loader,magic = <BOOT_LOADER>;
> +			};
> +
> +			maskrom {
> +				linux,mode = "maskrom";
> +				loader,magic = <BOOT_MASKROM>;
> +			};
> +
> +			recovery {
> +				linux,mode = "recovery";
> +				loader,magic = <BOOT_RECOVERY>;
> +			};
> +
> +			fastboot {
> +				linux,mode = "fastboot";
> +				loader,magic = <BOOT_FASTBOOT>;
> +			};
> +		};
>   	};
>
>   	grf: grf at 20008000 {
> diff --git a/include/dt-bindings/soc/rockchip_boot-mode.h b/include/dt-bindings/soc/rockchip_boot-mode.h
> new file mode 100644
> index 0000000..eedf113
> --- /dev/null
> +++ b/include/dt-bindings/soc/rockchip_boot-mode.h
> @@ -0,0 +1,30 @@
> +#ifndef __ROCKCHIP_BOOT_MODE_H
> +#define __ROCKCHIP_BOOT_MODE_H
> +
> +/*high 24 bits is tag, low 8 bits is type*/
> +#define	REBOOT_FLAG		0x5242C300
> +/* normal boot */
> +#define	BOOT_NORMAL		(REBOOT_FLAG + 0)
> +/* enter loader rockusb mode */
> +#define	BOOT_LOADER		(REBOOT_FLAG + 1)
> +/* enter maskrom rockusb mode */
> +#define	BOOT_MASKROM		(0xEF08A53C)
> +/* enter recovery */
> +#define	BOOT_RECOVERY		(REBOOT_FLAG + 3)
> +/* do not enter recover */
> +#define	BOOT_NORECOVER		(REBOOT_FLAG + 4)
> +/* boot second OS*/
> +#define	BOOT_SECONDOS		(REBOOT_FLAG + 5)
> +/* enter recover and wipe data. */
> +#define	BOOT_WIPEDATA		(REBOOT_FLAG + 6)
> +/* enter recover and wipe all data. */
> +#define	BOOT_WIPEALL		(REBOOT_FLAG + 7)
> +/* check firmware img with backup part*/
> +#define	BOOT_CHECKIMG		(REBOOT_FLAG + 8)
> + /* enter fast boot mode */
> +#define	BOOT_FASTBOOT		(REBOOT_FLAG + 9)
> +#define	BOOT_SECUREBOOT_DISABLE	(REBOOT_FLAG + 10)
> +/* enter charge mode */
> +#define	BOOT_CHARGING		(REBOOT_FLAG + 11)
> +
> +#endif
>


From tomeu at tomeuvizoso.net  Thu Jan 21 01:03:21 2016
From: tomeu at tomeuvizoso.net (Tomeu Vizoso)
Date: Thu, 21 Jan 2016 10:03:21 +0100
Subject: [PATCH 0/5] clk: rockchip: add full support for HDMI clock on
 rk3288
In-Reply-To: <CAD=FV=Wgu=xQ9PcVqfzcedXVU3Rh-HKbBVxHuqgV9OZJEYhYvg@mail.gmail.com>
References: <1415087559-19444-1-git-send-email-kever.yang@rock-chips.com>
 <48806799.thBSifdUp8@diego> <54647142.4080800@rock-chips.com>
 <CAD=FV=Vzec3Qm-kc6zZ4VxSREoZbzVkWbzp-noHYdcEVrLYYSw@mail.gmail.com>
 <CAAObsKChYN5nmcm5OWnAzNRGDi_QGSoKvN+0FizhjeT-3mmeXQ@mail.gmail.com>
 <CAD=FV=Wgu=xQ9PcVqfzcedXVU3Rh-HKbBVxHuqgV9OZJEYhYvg@mail.gmail.com>
Message-ID: <CAAObsKC1v--mZwkiG-c7P2Bwi2-_txbeqaW4Vd5h-noAVNbhjA@mail.gmail.com>

On 20 January 2016 at 17:50, Doug Anderson <dianders at chromium.org> wrote:
> Tomeu,
>
> On Tue, Jan 19, 2016 at 4:02 AM, Tomeu Vizoso <tomeu at tomeuvizoso.net> wrote:
>> On 13 November 2014 at 23:59, Doug Anderson <dianders at chromium.org> wrote:
>>> Hi,
>>>
>>> On Thu, Nov 13, 2014 at 12:52 AM, Kever Yang <kever.yang at rock-chips.com> wrote:
>>>> Hi Heiko,
>>>>
>>>> On 11/07/2014 05:06 AM, Heiko St?bner wrote:
>>>>>
>>>>> Hi Kever,
>>>>>
>>>>> Am Dienstag, 4. November 2014, 15:52:34 schrieb Kever Yang:
>>>>>>
>>>>>> we are going to make a clock usage solution for rk3288:
>>>>>> 1. CPLL and GPLL always not change after assign init;
>>>>>> 2. NPLL default as 500MHz, may used for most scene;
>>>>>> 3. NPLL may be changed by VOP(HDMI) clock for some special
>>>>>>     frequency requirement.
>>>>>>
>>>>>>      I test it with rk3288 evb on top of Heiko's clk-for-next
>>>>>
>>>>> In general I'm not really sure if allowing one component to arbitarily
>>>>> change
>>>>> a shared clock wouldn't result in trouble.
>>>>>
>>>>> At the moment only dclk_vop0 is included in your series, while the hdmi
>>>>> controller can connect to both vop0 and vop1.
>>>>> And as Doug mentioned the gpu also has the npll as one possible source.
>>>>
>>>> I think the problem GPU HANGs with 480MHz clock from usbphy has
>>>> been fixed with my patch to gerrit:
>>>> https://chromium-review.googlesource.com/#/c/229554/
>>>>>
>>>>>
>>>>> Looking through the clock-tree there are a lot more components possibly
>>>>> using
>>>>> (or wanting to use) the npll: of course the VOPs, the edp, hdmi, isp,
>>>>> hevc,
>>>>> gpu, tsp uart0 and gmac. So I'm slightly uncomfortable with somehow
>>>>> reserving
>>>>> the npll for VOP0 alone.
>>>>
>>>> It's true that I customized the usage of npll for VOP0 when we need some
>>>> very special frequency, but it doesn't means other modules can't use the
>>>> npll, it will always decided by clock core for module clocks that which
>>>> parent
>>>> is the best.
>>>
>>> We will just need to be very careful.  As I've mentioned in the past
>>> we'll need to think about what happens to other clocks that happen to
>>> be parented by NPLL whenever we change it.
>>>
>>> So if we do this:
>>>
>>> 1. NPLL happens to be 500MHz.
>>> 2. We set GPU to be 500MHz and it picks NPLL.
>>> 3. We change NPLL to a different speed (like 600MHz).
>>>
>>> ...I believe in this scenario the GPU would start running at 600MHz
>>> immediately.  We'd need to add special code to watch out for this and
>>> pick an alternate clock for the GPU (like the USB 480) before the NPLL
>>> change.  If NPLL later changes back to 500MHz and the GPU still wanted
>>> 500MHz, we'd have to decide what to do.
>>>
>>> The summary is: it's pretty complicated
>>
>> Hello,
>>
>> I have heard that this is still an issue in RK3288 support in mainline
>> so have given a look and wonder if you have considered having the
>> consumers of a shared clock (which could be a child clock) use
>> PRE_RATE_CHANGE notifications to adapt to changes in a parent's rate,
>> or to abort them if that's not possible.
>>
>> Alternatively, a consumer can set min and max constraints if it
>> doesn't want for others to mess with its provider, and let the other
>> consumer deal with failed rate changes (probably by choosing another
>> provider).
>>
>> Have I missed any problems with that?
>
> I don't think it's really that simple, but I certainly could be wrong.
>
> Summary of the system:
>
> * There are two VOPs (video processors) on rk3288: the big one and the
> little one.
>
> * The big VOP can handle higher resolutions than the little one but
> uses more power.
>
> * Both VOPs two can be sourced from CPLL, GPLL, or NPLL.
>
> * Both VOPs can be setup to process video for either HDMI, eDP, LVDS, ...
>
> * NPLL is the "new" PLL and is kinda like an extra PLL that's not used
> for any of the core peripherals.  It's intended to be one that's
> changed dynamically based on system needs.
>
> * GPLL appears intended (in current designs) to run at 594 MHz on
> rk3288 using a special "low jitter" setting.  This is intended to be
> the source clock for HDMI when running the very common 148.5 and 74.25
> rates.  It also it intended to be the source clock for various other
> peripherals like i2c, eMMC, SD, etc.  This also runs the GPU when the
> GPU is running at ~600MHz or ~300MHz.
>
> * CPLL appears intended (in current designs) to run at 400MHz.  There
> are some peripherals that run off this plus the GPU at 400MHz, 200MHz,
> or 100MHz.
>
>
> Let's imagine that we're booting up on a laptop where the builtin
> panel is hooked up via eDP.  Let's say that the panel really wants
> 76.42 MHz but that will work just fine with 74.25 MHz too.  If this is
> a system that has no other graphics needs, we'd want eDP to take NPLL
> and grab 76.42 MHz.  Make the best rate, right?
>
> ...but what about if it's a system with an HDMI port?  The eDP panel
> is built in and we know it works with 74.25 MHz, but the user could
> plug into all sorts of HDMI devices and they may require some very
> special PLL rates to get there.  So if we happen to take NPLL for eDP
> we'll need to know to switch away from it once something is plugged
> into HDMI.  We don't want to block the HDMI request since we know we
> can run eDP at 74.25 off GPLL.  Presumably we could see the HDMI
> request and then try to remux the eDP on the fly.  ...but what decides
> the HDMI is more important to eDP?  You might want different rules if
> the eDP port is somehow exposed and the user might plug different
> panels into it...

So we have a mechanism for detecting a conflict in the clock
hierarchy, and a mechanism to solve it, but we are missing a way for
userspace to communicate policy regarding which clocks should be given
priority when solving such a conflict?

Regards,

Tomeu


From heiko at sntech.de  Thu Jan 21 01:21:50 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Thu, 21 Jan 2016 10:21:50 +0100
Subject: [PATCH] clk: rockchip: rk3036: Add apll as the critical clock
In-Reply-To: <1453279037-23971-1-git-send-email-zhengxing@rock-chips.com>
References: <1453279037-23971-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <7538210.fX6mmzZ75v@phil>

Hi Xing,

Am Mittwoch, 20. Januar 2016, 16:37:17 schrieb Xing Zheng:
> The apll may be closed if there are some child clock nodes below
> it when the device startup. Therefore, the apll should be keep
> critical.
> 
> The apll tree like this:
>     pll_apll
>        apll
>           armclk
>              pclk_dbg
>              aclk_core_pre
>           aclk_hvec
>           uart_pll_clk
>              uart2_src
>                 uart2_frac
>              uart1_src
>                 uart1_frac
>              uart0_src
>                 uart0_frac

can you find out which of those clocks does cause your hang?
Because things like the uart-clocks for example should be handled by their 
driver already, at the time the clk_disable_unused runs(). So I'd really 
like the critical clock to be the actually needed clock.

Thanks
Heiko


> Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
> ---
> 
>  drivers/clk/rockchip/clk-rk3036.c |    1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/drivers/clk/rockchip/clk-rk3036.c
> b/drivers/clk/rockchip/clk-rk3036.c index ebce980..483913b 100644
> --- a/drivers/clk/rockchip/clk-rk3036.c
> +++ b/drivers/clk/rockchip/clk-rk3036.c
> @@ -425,6 +425,7 @@ static struct rockchip_clk_branch
> rk3036_clk_branches[] __initdata = { };
> 
>  static const char *const rk3036_critical_clocks[] __initconst = {
> +	"apll",
>  	"aclk_cpu",
>  	"aclk_peri",
>  	"hclk_peri",



From heiko at sntech.de  Thu Jan 21 01:55:07 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Thu, 21 Jan 2016 10:55:07 +0100
Subject: [PATCH] ARM64: dts: rockchip: Add GeekBox config
In-Reply-To: <56A041FE.7000104@suse.de>
References: <1453307913-8907-1-git-send-email-afaerber@suse.de>
 <6705185.M2rneQv8FF@phil> <56A041FE.7000104@suse.de>
Message-ID: <1969169.l2ZMdDHEV7@phil>

Hi Andreas,

Am Donnerstag, 21. Januar 2016, 03:27:10 schrieb Andreas F?rber:
> Am 21.01.2016 um 00:08 schrieb Heiko Stuebner:
> > Am Mittwoch, 20. Januar 2016, 17:38:33 schrieb Andreas F?rber:
> >> The GeekBox contains an MXM3 module with a Rockchip RK3368 SoC.
> >> Some connectors are available directly on the module.
> >> 
> >> This adds initial support, namely serial, PMIC and USB.
> >> 
> >> Signed-off-by: Andreas F?rber <afaerber at suse.de>
> >> ---
> >> 
> >>  Hi Heiko,
> >>  
> >>  Here's an initial shot at a .dts for GeekBox.
> >>  GMAC was not yet working for me.
> >>  
> >>  Regards,
> >>  Andreas
> >>  
> >>  arch/arm64/boot/dts/rockchip/Makefile            |   1 +
> >>  arch/arm64/boot/dts/rockchip/rk3368-geekbox.dts  |  48 +++++
> >>  arch/arm64/boot/dts/rockchip/rk3368-geekbox.dtsi | 242
> >> 
> >> +++++++++++++++++++++++ 3 files changed, 291 insertions(+)
> >> 
> >>  create mode 100644 arch/arm64/boot/dts/rockchip/rk3368-geekbox.dts
> >>  create mode 100644 arch/arm64/boot/dts/rockchip/rk3368-geekbox.dtsi
> > 
> > You might want to explain your reasoning for having a dtsi and an empty
> > standalone dts. I guess you want to add a second one for
> > module+baseboard?
> More generally, as mentioned in the commit message, it's a module, so
> automatically I did a .dtsi to allow for easy reuse.
> 
> At LinuxCon JP I had discussed with Grant Likely about whether it's okay
> to #include a .dts rather than .dtsi and don't remember getting a clear
> answer as we ended up drawing a line between hardware and FPGA.
> 
> And yes, I may later add a .dts for the Landingship - it would mainly
> enable more nodes, I envision. But I assume that we're not bound to just
> the Landingship with such a standard connector.
> 
> For now I'm more interested in incrementally adding further nodes to the
> module's own file.
> 
> > The interesting artifact here is, that the baseboard can actually be
> > used
> > standalone and the baseboard just extends it where in contrast a rk3288
> > rock2 for example isn't actually usable without a baseboard.
> > 
> > So I'm wondering if this really should be encoded in the dts or should
> > uses something like dts overlays instead to enable one geekbox module
> > to be used alone and in the "landingship".
> 
> Well, with the Parallella I made the experience that I used a single
> .dts initially, and it turned out to be a bad choice when we tried
> adding stuff to the DT while new hardware models surfaced - we still
> haven't managed to split it up and it's stuck in limbo without USB.
> I'd really like to avoid that going forward, so I'm trying to look
> forward here in keeping our options open with a clean design.
> 
> How would your overlay get loaded? Wouldn't that need some special
> kernel driver like for the BeagleBone? Not so attractive for a single
> optional baseboard I care about.

It wasn't meant as a requirement, more like a thought I had and wanted to 
discuss ;-) . So far I also only saw talks about devicetree overlays but 
never in action yet.

I would have hoped there to be some gpio indicating if it's connected to the 
"LandingShip" but that doesn't seem to be the case after looking through the 
schematics again.

In any case, going the regular way should be ok as well.


> >> diff --git a/arch/arm64/boot/dts/rockchip/Makefile
> >> b/arch/arm64/boot/dts/rockchip/Makefile index e3f0b5f..df37865 100644
> >> --- a/arch/arm64/boot/dts/rockchip/Makefile
> >> +++ b/arch/arm64/boot/dts/rockchip/Makefile
> >> @@ -1,4 +1,5 @@
> >> 
> >>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-evb-act8846.dtb
> >> 
> >> +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-geekbox.dtb
> >> 
> >>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-r88.dtb
> >>  
> >>  always		:= $(dtb-y)
> >> 
> >> diff --git a/arch/arm64/boot/dts/rockchip/rk3368-geekbox.dts
> >> b/arch/arm64/boot/dts/rockchip/rk3368-geekbox.dts new file mode 100644
> >> index 0000000..eb24ecd
> >> --- /dev/null
> >> +++ b/arch/arm64/boot/dts/rockchip/rk3368-geekbox.dts
> >> @@ -0,0 +1,48 @@
> >> +/*
> >> + * Copyright (c) 2016 Andreas F?rber
> >> + *
> >> + * This file is dual-licensed: you can use it either under the terms
> >> + * of the GPL or the X11 license, at your option. Note that this dual
> >> + * licensing only applies to this file, and not this project as a
> >> + * whole.
> >> + *
> >> + *  a) This file is free software; you can redistribute it and/or
> >> + *     modify it under the terms of the GNU General Public License as
> >> + *     published by the Free Software Foundation; either version 2 of
> >> the + *     License, or (at your option) any later version.
> >> + *
> >> + *     This file is distributed in the hope that it will be useful,
> >> + *     but WITHOUT ANY WARRANTY; without even the implied warranty of
> >> + *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> >> + *     GNU General Public License for more details.
> >> + *
> >> + * Or, alternatively,
> >> + *
> >> + *  b) Permission is hereby granted, free of charge, to any person
> >> + *     obtaining a copy of this software and associated documentation
> >> + *     files (the "Software"), to deal in the Software without
> >> + *     restriction, including without limitation the rights to use,
> >> + *     copy, modify, merge, publish, distribute, sublicense, and/or
> >> + *     sell copies of the Software, and to permit persons to whom the
> >> + *     Software is furnished to do so, subject to the following
> >> + *     conditions:
> >> + *
> >> + *     The above copyright notice and this permission notice shall be
> >> + *     included in all copies or substantial portions of the Software.
> >> + *
> >> + *     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.
> >> + */
> >> +
> >> +/dts-v1/;
> >> +#include "rk3368-geekbox.dtsi"
> >> +
> >> +/ {
> >> +	model = "GeekBox";

Compatible should probably move here.


> >> +};
> >> diff --git a/arch/arm64/boot/dts/rockchip/rk3368-geekbox.dtsi
> >> b/arch/arm64/boot/dts/rockchip/rk3368-geekbox.dtsi new file mode 100644
> >> index 0000000..f083cdb
> >> --- /dev/null
> >> +++ b/arch/arm64/boot/dts/rockchip/rk3368-geekbox.dtsi

maybe also rk3368-geekbox-som.dtsi like the rk3288-rock2-som.dtsi. That way 
it would show clearly its status as system on module which then gets used 
somewhere.


> >> @@ -0,0 +1,242 @@
> >> +/*
> >> + * Copyright (c) 2016 Andreas F?rber
> >> + *
> >> + * This file is dual-licensed: you can use it either under the terms
> >> + * of the GPL or the X11 license, at your option. Note that this dual
> >> + * licensing only applies to this file, and not this project as a
> >> + * whole.
> >> + *
> >> + *  a) This file is free software; you can redistribute it and/or
> >> + *     modify it under the terms of the GNU General Public License as
> >> + *     published by the Free Software Foundation; either version 2 of
> >> the + *     License, or (at your option) any later version.
> >> + *
> >> + *     This file is distributed in the hope that it will be useful,
> >> + *     but WITHOUT ANY WARRANTY; without even the implied warranty of
> >> + *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> >> + *     GNU General Public License for more details.
> >> + *
> >> + * Or, alternatively,
> >> + *
> >> + *  b) Permission is hereby granted, free of charge, to any person
> >> + *     obtaining a copy of this software and associated documentation
> >> + *     files (the "Software"), to deal in the Software without
> >> + *     restriction, including without limitation the rights to use,
> >> + *     copy, modify, merge, publish, distribute, sublicense, and/or
> >> + *     sell copies of the Software, and to permit persons to whom the
> >> + *     Software is furnished to do so, subject to the following
> >> + *     conditions:
> >> + *
> >> + *     The above copyright notice and this permission notice shall be
> >> + *     included in all copies or substantial portions of the Software.
> >> + *
> >> + *     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.
> >> + */
> >> +
> >> +#include "rk3368.dtsi"
> >> +
> >> +/ {
> >> +	compatible = "geekbuying,geekbox", "rockchip,rk3368";
> > 
> > you're introducing
> > - a new vendor name (geekbuying), so should update
> > Documentation/devicetree/bindings/vendor-prefixes.txt in a separate
> > patch
> 
> True. I did not check whether they're NASDAQ-listed either, but I assume
> no; haven't compared the downstream DT for what they might've been using
> themselves.
> 
> > - a new board, so should update
> > Documentation/devicetree/bindings/arm/rockchip.txt
> 
> Will do.

And as said above, the compatible should probably live in the dts, not the 
dtsi (see rock2).

> 
> >> +
> >> +	chosen {
> >> +		stdout-path = "serial2:115200n8";
> >> +	};
> >> +
> >> +	memory {
> >> +		device_type = "memory";
> >> +		reg = <0x0 0x0 0x0 0x80000000>;
> >> +	};
> >> +
> >> +	leds: gpio-leds {
> >> +		compatible = "gpio-leds";
> >> +
> >> +		blue {
> >> +			gpios = <&gpio2 2 GPIO_ACTIVE_HIGH>;
> >> +			label = "geekbox:blue:led";
> >> +			default-state = "on";
> >> +		};
> >> +
> >> +		red {
> >> +			gpios = <&gpio2 3 GPIO_ACTIVE_HIGH>;
> >> +			label = "geekbox:red:led";
> >> +			default-state = "off";
> >> +		};
> >> +	};
> >> +
> >> +	vcc_sys: vcc-sys-regulator {
> >> +		compatible = "regulator-fixed";
> >> +		regulator-name = "vcc_sys";
> >> +		regulator-min-microvolt = <5000000>;
> >> +		regulator-max-microvolt = <5000000>;
> >> +		regulator-always-on;
> >> +		regulator-boot-on;
> >> +	};
> >> +};
> >> +
> >> +&i2c0 {
> >> +	status = "okay";
> >> +
> >> +	rk808: pmic at 1b {
> >> +		compatible = "rockchip,rk808";
> >> +		reg = <0x1b>;
> >> +		pinctrl-names = "default";
> >> +		pinctrl-0 = <&pmic_int>, <&pmic_sleep>;
> >> +		interrupt-parent = <&gpio0>;
> >> +		interrupts = <5 IRQ_TYPE_LEVEL_LOW>;
> >> +		rockchip,system-power-controller;
> >> +		vcc1-supply = <&vcc_sys>;
> >> +		vcc2-supply = <&vcc_sys>;
> >> +		vcc3-supply = <&vcc_sys>;
> >> +		vcc4-supply = <&vcc_sys>;
> >> +		vcc6-supply = <&vcc_sys>;
> >> +		vcc7-supply = <&vcc_sys>;
> >> +		vcc8-supply = <&vcc_io>;
> >> +		vcc9-supply = <&vcc_sys>;
> >> +		vcc10-supply = <&vcc_sys>;
> >> +		vcc11-supply = <&vcc_sys>;
> >> +		vcc12-supply = <&vcc_io>;
> >> +		clock-output-names = "xin32k", "rk808-clkout2";
> >> +		#clock-cells = <1>;
> >> +
> >> +		regulators {
> >> +			vdd_cpu: DCDC_REG1 {
> >> +				regulator-always-on;
> >> +				regulator-boot-on;
> >> +				regulator-min-microvolt = <700000>;
> >> +				regulator-max-microvolt = <1500000>;
> >> +				regulator-name = "vdd_cpu";
> >> +			};
> >> +
> >> +			vdd_log: DCDC_REG2 {
> >> +				regulator-always-on;
> >> +				regulator-boot-on;
> >> +				regulator-min-microvolt = <700000>;
> >> +				regulator-max-microvolt = <1500000>;
> >> +				regulator-name = "vdd_log";
> >> +			};
> >> +
> >> +			vcc_ddr: DCDC_REG3 {
> >> +				regulator-always-on;
> >> +				regulator-boot-on;
> >> +				regulator-name = "vcc_ddr";
> >> +			};
> >> +
> >> +			vcc_io: DCDC_REG4 {
> >> +				regulator-always-on;
> >> +				regulator-boot-on;
> >> +				regulator-min-microvolt = <3300000>;
> >> +				regulator-max-microvolt = <3300000>;
> >> +				regulator-name = "vcc_io";
> >> +			};
> >> +
> >> +			vcc18_flash: LDO_REG1 {
> >> +				regulator-always-on;
> >> +				regulator-boot-on;
> >> +				regulator-min-microvolt = <1800000>;
> >> +				regulator-max-microvolt = <1800000>;
> >> +				regulator-name = "vcc18_flash";
> >> +			};
> >> +
> >> +			vcc33_lcd: LDO_REG2 {
> >> +				regulator-always-on;
> >> +				regulator-boot-on;
> >> +				regulator-min-microvolt = <3300000>;
> >> +				regulator-max-microvolt = <3300000>;
> >> +				regulator-name = "vcc33_lcd";
> >> +			};
> >> +
> >> +			vdd_10: LDO_REG3 {
> >> +				regulator-always-on;
> > 
> > why all others also "boot-on" and this one not?
> 
> Good question. I had to fiddle with voltages and always-on / boot-on to
> get things to work so far, it kept probing with -EINVAL earlier.
> 
> According to the schematics, the PLL is behind vdd_10, so it probably
> should be boot-on, too. I'm now less certain whether some of the others
> should be boot-on though, in particular in light of RK3368 mainline
> U-Boot not existing yet and choices possibly being different there.

I'm fine with going the always-on/boot-on way at first. Sorting which 
regulators can actually be disabled can also be done later - especially as 
some components still don't do regulator-handling.


> Counter-question: Why does the -r88.dts have them all as fixed
> regulators?

Because the r88 board doesn't actually have a pmic ;-) . Just a bunch of 
individual (and mostly always-on) regulators on the board - thankfully I 
have schematics for it.


> I simply assumed here that the RK808-B is sufficiently
> compatible with rockchip,rk808?

I'd hope so.


Heiko


From shawn.lin at rock-chips.com  Thu Jan 21 04:32:09 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Thu, 21 Jan 2016 20:32:09 +0800
Subject: [PATCH] Documentation: rockchip-dw-mshc: add RK3368 dw-mshc
 description
Message-ID: <1453379529-5148-1-git-send-email-shawn.lin@rock-chips.com>

rk3368 dtsi file add dw-mshc compatible "rockchip,rk3368-dw-mshc"
but didn't add it into rockchip-dw-mshc.txt.

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
---

 Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt
index 3dc13b6..634e249 100644
--- a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt
+++ b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt
@@ -13,6 +13,7 @@ Required Properties:
 	- "rockchip,rk2928-dw-mshc": for Rockchip RK2928 and following,
 							before RK3288
 	- "rockchip,rk3288-dw-mshc": for Rockchip RK3288
+	- "rockchip,rk3368-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK3368
 
 Optional Properties:
 * clocks: from common clock binding: if ciu_drive and ciu_sample are
-- 
2.3.7




From w.egorov at phytec.de  Thu Jan 21 04:57:13 2016
From: w.egorov at phytec.de (Wadim Egorov)
Date: Thu, 21 Jan 2016 13:57:13 +0100
Subject: [PATCH] ARM: dts: rockchip: Bump sd card pin drive strength up on
 firefly boards
Message-ID: <1453381033-3479-1-git-send-email-w.egorov@phytec.de>

It seems some firefly boards need 12mA drive strength for sdmmc.
Using 4mA/8mA drive strength will cause the kernel to fail to recognize
the sd card correctly.
Increased the sdmmc lines drive strength from 4mA to 12mA.

Signed-off-by: Wadim Egorov <w.egorov at phytec.de>
---
 arch/arm/boot/dts/rk3288-firefly.dtsi | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/arch/arm/boot/dts/rk3288-firefly.dtsi b/arch/arm/boot/dts/rk3288-firefly.dtsi
index 4e3fd9a..49ec20d 100644
--- a/arch/arm/boot/dts/rk3288-firefly.dtsi
+++ b/arch/arm/boot/dts/rk3288-firefly.dtsi
@@ -408,6 +408,11 @@
 		output-low;
 	};
 
+	pcfg_pull_up_drv_12ma: pcfg-pull-up-drv-12ma {
+		bias-pull-up;
+		drive-strength = <12>;
+	};
+
 	act8846 {
 		pwr_hold: pwr-hold {
 			rockchip,pins = <0 1 RK_FUNC_GPIO &pcfg_output_high>;
@@ -457,6 +462,25 @@
 	};
 
 	sdmmc {
+		/*
+		 * Default drive strength isn't enough to achieve even
+		 * high-speed mode on firefly board so bump up to 12ma.
+		 */
+		sdmmc_bus4: sdmmc-bus4 {
+			rockchip,pins = <6 16 RK_FUNC_1 &pcfg_pull_up_drv_12ma>,
+					<6 17 RK_FUNC_1 &pcfg_pull_up_drv_12ma>,
+					<6 18 RK_FUNC_1 &pcfg_pull_up_drv_12ma>,
+					<6 19 RK_FUNC_1 &pcfg_pull_up_drv_12ma>;
+		};
+
+		sdmmc_clk: sdmmc-clk {
+			rockchip,pins = <6 20 RK_FUNC_1 &pcfg_pull_none_12ma>;
+		};
+
+		sdmmc_cmd: sdmmc-cmd {
+			rockchip,pins = <6 21 RK_FUNC_1 &pcfg_pull_up_drv_12ma>;
+		};
+
 		sdmmc_pwr: sdmmc-pwr {
 			rockchip,pins = <7 11 RK_FUNC_GPIO &pcfg_pull_none>;
 		};
-- 
1.9.1



From john at metanate.com  Thu Jan 21 05:53:46 2016
From: john at metanate.com (John Keeping)
Date: Thu, 21 Jan 2016 13:53:46 +0000
Subject: [PATCH] drm/rockchip: respect CONFIG_DRM_FBDEV_EMULATION
Message-ID: <1453384426-32617-1-git-send-email-john@metanate.com>

If DRM_FBDEV_EMULATION is not selected in the config then we should not
setup a framebuffer console.

Signed-off-by: John Keeping <john at metanate.com>
---
 drivers/gpu/drm/rockchip/Makefile             |  3 ++-
 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h | 11 +++++++++++
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index a4e03bc..f6a809a 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -2,8 +2,9 @@
 # Makefile for the drm device driver.  This driver provides support for the
 # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
 
-rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
+rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
 		rockchip_drm_gem.o rockchip_drm_vop.o
+rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
 
 obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
 obj-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
index 50432e9..73718c5 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
@@ -15,7 +15,18 @@
 #ifndef _ROCKCHIP_DRM_FBDEV_H
 #define _ROCKCHIP_DRM_FBDEV_H
 
+#ifdef CONFIG_DRM_FBDEV_EMULATION
 int rockchip_drm_fbdev_init(struct drm_device *dev);
 void rockchip_drm_fbdev_fini(struct drm_device *dev);
+#else
+static inline int rockchip_drm_fbdev_init(struct drm_device *dev)
+{
+	return 0;
+}
+
+static inline void rockchip_drm_fbdev_fini(struct drm_device *dev)
+{
+}
+#endif
 
 #endif /* _ROCKCHIP_DRM_FBDEV_H */
-- 
2.7.0.226.gfe986fe



From treding at nvidia.com  Thu Jan 21 08:20:37 2016
From: treding at nvidia.com (Thierry Reding)
Date: Thu, 21 Jan 2016 17:20:37 +0100
Subject: [PATCH v3 3/5] soc: rockchip: add reboot notifier driver
In-Reply-To: <2250962.OgUpqMgY4k@wuerfel>
References: <1447840044-19689-1-git-send-email-andy.yan@rock-chips.com>
 <15127971.1juVbocOjP@wuerfel>
 <20151228092054.GA29195@ulmo.nvidia.com>
 <2250962.OgUpqMgY4k@wuerfel>
Message-ID: <20160121162035.GG647@ulmo.nvidia.com>

On Mon, Dec 28, 2015 at 04:35:46PM +0100, Arnd Bergmann wrote:
> On Monday 28 December 2015 10:20:56 Thierry Reding wrote:
> > > > > HTC apparently uses a separate RAM area to pass the reboot reason,
> > > > > and they have a driver to store that, which is separate from the
> > > > > driver that they use for actually rebooting the machine.
> > > > 
> > > > I wasn't very clear, but the PMC_SCRATCH0 register is used to store the
> > > > reset reason. It supports the recovery mode, which I think is really an
> > > > Android thing, "bootloader" will typically cause the bootloader not to
> > > > boot anything, and "forced-recovery" will go into a recovery mode that
> > > > is used to bootstrap the device (usually by uploading a "miniloader"
> > > > that initializes RAM, downloads a bootloader for booting or flashing an
> > > > operating system, ...).
> > > > 
> > > > The write that resets the SoC is to a different register.
> > > 
> > > So is this scratch register interpreted by some maskrom code, or by code that
> > > can be provided by the OEM?
> > 
> > My understanding is that its interpreted both by what's called BootROM
> > on Tegra (I guess that's what you call "maskrom code") and the system's
> > bootloader. The BootROM cannot typically be replaced by the OEM, but it
> > is quite typical for the bootloader to differ between devices.
> 
> Ok, so not maskrom (which would not be OEM specific, but hardcoded for
> the chip) but rather some form of PROM. This means we can only guess
> that all OEMs use the same protocol but in theory someone could have
> implemented an incompatible BootROM, but it's also possible that HTC
> just ignore the register entirely and implement the same thing separately.

I wasn't being clear, the BootROM is hardcoded for the chip, I'm not
aware of a way to replace it once the chip's taped out.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160121/8a6f284b/attachment.sig>

From daniel at ffwll.ch  Thu Jan 21 08:52:51 2016
From: daniel at ffwll.ch (Daniel Vetter)
Date: Thu, 21 Jan 2016 17:52:51 +0100
Subject: [PATCH] drm/rockchip: respect CONFIG_DRM_FBDEV_EMULATION
In-Reply-To: <1453384426-32617-1-git-send-email-john@metanate.com>
References: <1453384426-32617-1-git-send-email-john@metanate.com>
Message-ID: <20160121165251.GO19130@phenom.ffwll.local>

On Thu, Jan 21, 2016 at 01:53:46PM +0000, John Keeping wrote:
> If DRM_FBDEV_EMULATION is not selected in the config then we should not
> setup a framebuffer console.

It should just magically work, and this patch here just removes a bit more
dead code in the rockchip driver itself that's not needed in case fbdev
emulation is disabled.

Can you please double-check this is the case and then resend with a
clarified commit message?

Thanks, Daniel

> 
> Signed-off-by: John Keeping <john at metanate.com>
> ---
>  drivers/gpu/drm/rockchip/Makefile             |  3 ++-
>  drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h | 11 +++++++++++
>  2 files changed, 13 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> index a4e03bc..f6a809a 100644
> --- a/drivers/gpu/drm/rockchip/Makefile
> +++ b/drivers/gpu/drm/rockchip/Makefile
> @@ -2,8 +2,9 @@
>  # Makefile for the drm device driver.  This driver provides support for the
>  # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
>  
> -rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
> +rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
>  		rockchip_drm_gem.o rockchip_drm_vop.o
> +rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
>  
>  obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
>  obj-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
> index 50432e9..73718c5 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
> @@ -15,7 +15,18 @@
>  #ifndef _ROCKCHIP_DRM_FBDEV_H
>  #define _ROCKCHIP_DRM_FBDEV_H
>  
> +#ifdef CONFIG_DRM_FBDEV_EMULATION
>  int rockchip_drm_fbdev_init(struct drm_device *dev);
>  void rockchip_drm_fbdev_fini(struct drm_device *dev);
> +#else
> +static inline int rockchip_drm_fbdev_init(struct drm_device *dev)
> +{
> +	return 0;
> +}
> +
> +static inline void rockchip_drm_fbdev_fini(struct drm_device *dev)
> +{
> +}
> +#endif
>  
>  #endif /* _ROCKCHIP_DRM_FBDEV_H */
> -- 
> 2.7.0.226.gfe986fe
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


From john at metanate.com  Thu Jan 21 10:19:34 2016
From: john at metanate.com (John Keeping)
Date: Thu, 21 Jan 2016 18:19:34 +0000
Subject: [PATCH v2] drm/rockchip: respect CONFIG_DRM_FBDEV_EMULATION
In-Reply-To: <20160121165251.GO19130@phenom.ffwll.local>
References: <1453384426-32617-1-git-send-email-john@metanate.com>
 <20160121165251.GO19130@phenom.ffwll.local>
Message-ID: <20160121181934.76cb2c03.john@metanate.com>

If DRM_FBDEV_EMULATION is not selected in the config then we can save a
bit of space by not including the framebuffer code.

Signed-off-by: John Keeping <john at metanate.com>
---
On Thu, 21 Jan 2016 17:52:51 +0100, Daniel Vetter wrote:

> On Thu, Jan 21, 2016 at 01:53:46PM +0000, John Keeping wrote:
> > If DRM_FBDEV_EMULATION is not selected in the config then we should not
> > setup a framebuffer console.  
> 
> It should just magically work, and this patch here just removes a bit more
> dead code in the rockchip driver itself that's not needed in case fbdev
> emulation is disabled.
> 
> Can you please double-check this is the case and then resend with a
> clarified commit message?

You're right, it does work without this change, so this is just to save
a bit of space when we don't need this feature.

v2:
- change commit message to clarify that this is only to remove some dead
  code

 drivers/gpu/drm/rockchip/Makefile             |  3 ++-
 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h | 11 +++++++++++
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index a4e03bc..f6a809a 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -2,8 +2,9 @@
 # Makefile for the drm device driver.  This driver provides support for the
 # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
 
-rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
+rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
 		rockchip_drm_gem.o rockchip_drm_vop.o
+rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
 
 obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
 obj-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
index 50432e9..73718c5 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
@@ -15,7 +15,18 @@
 #ifndef _ROCKCHIP_DRM_FBDEV_H
 #define _ROCKCHIP_DRM_FBDEV_H
 
+#ifdef CONFIG_DRM_FBDEV_EMULATION
 int rockchip_drm_fbdev_init(struct drm_device *dev);
 void rockchip_drm_fbdev_fini(struct drm_device *dev);
+#else
+static inline int rockchip_drm_fbdev_init(struct drm_device *dev)
+{
+	return 0;
+}
+
+static inline void rockchip_drm_fbdev_fini(struct drm_device *dev)
+{
+}
+#endif
 
 #endif /* _ROCKCHIP_DRM_FBDEV_H */
-- 
2.7.0.226.gfe986fe



From heiko at sntech.de  Thu Jan 21 11:11:45 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Thu, 21 Jan 2016 20:11:45 +0100
Subject: [PATCH v12.1 07/17] drm: rockchip: dp: add rockchip platform dp
 driver
In-Reply-To: <1453197893-13413-1-git-send-email-ykk@rock-chips.com>
References: <1450873538-18304-1-git-send-email-ykk@rock-chips.com>
 <1453197893-13413-1-git-send-email-ykk@rock-chips.com>
Message-ID: <7132332.jmLRiUjx3U@phil>

Hi Yakir,

Am Dienstag, 19. Januar 2016, 18:04:53 schrieb Yakir Yang:
> Rockchip have three clocks for dp controller, we leave pclk_edp
> to analogix_dp driver control, and keep the sclk_edp_24m and
> sclk_edp in platform driver.
> 
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
> Tested-by: Javier Martinez Canillas <javier at osg.samsung.com>
> ---

[...]

> +static int rockchip_dp_drm_create_encoder(struct rockchip_dp_device *dp)
> +{
> +	struct drm_encoder *encoder = &dp->encoder;
> +	struct drm_device *drm_dev = dp->drm_dev;
> +	struct device *dev = dp->dev;
> +	int ret;
> +
> +	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
> +							     dev->of_node);
> +	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
> +
> +	ret = drm_encoder_init(drm_dev, encoder, &rockchip_dp_encoder_funcs,
> +			       DRM_MODE_ENCODER_TMDS);

should be
+			       DRM_MODE_ENCODER_TMDS, NULL);

Exynos-side seems to be ok though.

With the adapted v12.1 patches it applies cleanly and display is working as 
well. So with the thing above fixed, you could resubmit a full v13, 
especially as you want to drop patch 16 as discussed there.


Heiko


From dianders at chromium.org  Thu Jan 21 12:11:00 2016
From: dianders at chromium.org (Doug Anderson)
Date: Thu, 21 Jan 2016 12:11:00 -0800
Subject: [PATCH 0/5] clk: rockchip: add full support for HDMI clock on
 rk3288
In-Reply-To: <CAAObsKC1v--mZwkiG-c7P2Bwi2-_txbeqaW4Vd5h-noAVNbhjA@mail.gmail.com>
References: <1415087559-19444-1-git-send-email-kever.yang@rock-chips.com>
 <48806799.thBSifdUp8@diego> <54647142.4080800@rock-chips.com>
 <CAD=FV=Vzec3Qm-kc6zZ4VxSREoZbzVkWbzp-noHYdcEVrLYYSw@mail.gmail.com>
 <CAAObsKChYN5nmcm5OWnAzNRGDi_QGSoKvN+0FizhjeT-3mmeXQ@mail.gmail.com>
 <CAD=FV=Wgu=xQ9PcVqfzcedXVU3Rh-HKbBVxHuqgV9OZJEYhYvg@mail.gmail.com>
 <CAAObsKC1v--mZwkiG-c7P2Bwi2-_txbeqaW4Vd5h-noAVNbhjA@mail.gmail.com>
Message-ID: <CAD=FV=VB93wYigRJ8wa8YL6P+Q+-fRokPiCB-Fo1pC_zAdh2oA@mail.gmail.com>

Hi,

On Thu, Jan 21, 2016 at 1:03 AM, Tomeu Vizoso <tomeu at tomeuvizoso.net> wrote:
> So we have a mechanism for detecting a conflict in the clock
> hierarchy, and a mechanism to solve it, but we are missing a way for
> userspace to communicate policy regarding which clocks should be given
> priority when solving such a conflict?

Hrmmm, I guess it could be userspace that makes the decision.  It does
seem a little odd to force it to userspace in all cases, though.  For
a particular laptop that is designed with a specific panel connected
up eDP it seems less than ideal to push this into userspace.  If the
kernel could just work in the expected sane way (or at least work that
way by default) it would be ideal.

If the kernel doesn't try to do anything sane by default then you're
creating a requirement for everyone's userspace to somehow figure this
out.  Do you expect there to be UI here, or that this would be
something that would be figured out by the Linux distribution?
Certainly exposing UI on something like a laptop with a builtin panel
wouldn't make any sense to me, but it might make sense if you had an
eval board with different display connectors on it.  If there's no UI,
would the Linux distribution need to somehow identify which board we
were on and then have a big lookup table about how to configure
things?


From heiko at sntech.de  Thu Jan 21 12:53:09 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Thu, 21 Jan 2016 21:53:09 +0100
Subject: [PATCH v3] clk-divider: make sure read-only dividers do not write to
 their register
Message-ID: <4666170.lmsMJxAogk@phil>

Commit e6d5e7d90be9 ("clk-divider: Fix READ_ONLY when divider > 1") removed
the special ops struct for read-only clocks and instead opted to handle
them inside the regular ops.

On the rk3368 this results in breakage as aclkm now gets set a value.
While it is the same divider value, the A53 core still doesn't like it,
which can result in the cpu ending up in a hang.
The reason being that "ACLKENMasserts one clock cycle before the rising
edge of ACLKM" and the clock should only be touched when STANDBYWFIL2
is asserted.

To fix this, reintroduce the read-only ops but do include the round_rate
callback. That way no writes that may be unsafe are done to the divider
register in any case.

The Rockchip use of the clk_divider_ops is adapted to this split again,
as is the nxp, lpc18xx-ccu driver that was included since the original
commit. On lpc18xx-ccu the divider seems to always be read-only
so only uses the new ops now.

Fixes: e6d5e7d90be9 ("clk-divider: Fix READ_ONLY when divider > 1")
Reported-by: Zhang Qing <zhangqing at rock-chips.com>
Signed-off-by: Heiko Stuebner <heiko at sntech.de>
---
changes in v3:
- instead of bailing out of set_rate, reintroduce the _ro_ops
  with added round_rate callback, as requested by Stephen
changes in v2:
- emphasize that even setting the same divider may be unsafe

 drivers/clk/clk-divider.c         | 11 ++++++++++-
 drivers/clk/nxp/clk-lpc18xx-ccu.c |  2 +-
 drivers/clk/rockchip/clk.c        |  4 +++-
 include/linux/clk-provider.h      |  1 +
 4 files changed, 15 insertions(+), 3 deletions(-)

diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index ded3ff4..aa1dacd 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -423,6 +423,12 @@ const struct clk_ops clk_divider_ops = {
 };
 EXPORT_SYMBOL_GPL(clk_divider_ops);
 
+const struct clk_ops clk_divider_ro_ops = {
+	.recalc_rate = clk_divider_recalc_rate,
+	.round_rate = clk_divider_round_rate,
+};
+EXPORT_SYMBOL_GPL(clk_divider_ro_ops);
+
 static struct clk *_register_divider(struct device *dev, const char *name,
 		const char *parent_name, unsigned long flags,
 		void __iomem *reg, u8 shift, u8 width,
@@ -446,7 +452,10 @@ static struct clk *_register_divider(struct device *dev, const char *name,
 		return ERR_PTR(-ENOMEM);
 
 	init.name = name;
-	init.ops = &clk_divider_ops;
+	if (clk_divider_flags & CLK_DIVIDER_READ_ONLY)
+		init.ops = &clk_divider_ro_ops;
+	else
+		init.ops = &clk_divider_ops;
 	init.flags = flags | CLK_IS_BASIC;
 	init.parent_names = (parent_name ? &parent_name: NULL);
 	init.num_parents = (parent_name ? 1 : 0);
diff --git a/drivers/clk/nxp/clk-lpc18xx-ccu.c b/drivers/clk/nxp/clk-lpc18xx-ccu.c
index 13aabbb..558da89 100644
--- a/drivers/clk/nxp/clk-lpc18xx-ccu.c
+++ b/drivers/clk/nxp/clk-lpc18xx-ccu.c
@@ -222,7 +222,7 @@ static void lpc18xx_ccu_register_branch_gate_div(struct lpc18xx_clk_branch *bran
 		div->width = 1;
 
 		div_hw = &div->hw;
-		div_ops = &clk_divider_ops;
+		div_ops = &clk_divider_ro_ops;
 	}
 
 	branch->gate.reg = branch->offset + reg_base;
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
index d9a0b5d..f7e8693 100644
--- a/drivers/clk/rockchip/clk.c
+++ b/drivers/clk/rockchip/clk.c
@@ -90,7 +90,9 @@ static struct clk *rockchip_clk_register_branch(const char *name,
 		div->width = div_width;
 		div->lock = lock;
 		div->table = div_table;
-		div_ops = &clk_divider_ops;
+		div_ops = (div_flags & CLK_DIVIDER_READ_ONLY)
+						? &clk_divider_ro_ops
+						: &clk_divider_ops;
 	}
 
 	clk = clk_register_composite(NULL, name, parent_names, num_parents,
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 1143e38..408a60d 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -385,6 +385,7 @@ struct clk_divider {
 #define CLK_DIVIDER_MAX_AT_ZERO		BIT(6)
 
 extern const struct clk_ops clk_divider_ops;
+extern const struct clk_ops clk_divider_ro_ops;
 
 unsigned long divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate,
 		unsigned int val, const struct clk_div_table *table,
-- 
2.6.4




From robh at kernel.org  Thu Jan 21 15:09:40 2016
From: robh at kernel.org (Rob Herring)
Date: Thu, 21 Jan 2016 17:09:40 -0600
Subject: [PATCH] Documentation: rockchip-dw-mshc: add RK3368 dw-mshc
 description
In-Reply-To: <1453379529-5148-1-git-send-email-shawn.lin@rock-chips.com>
References: <1453379529-5148-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <20160121230940.GA1609@rob-hp-laptop>

On Thu, Jan 21, 2016 at 08:32:09PM +0800, Shawn Lin wrote:
> rk3368 dtsi file add dw-mshc compatible "rockchip,rk3368-dw-mshc"
> but didn't add it into rockchip-dw-mshc.txt.
> 
> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
> ---
> 
>  Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt | 1 +
>  1 file changed, 1 insertion(+)

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

I'm assuming Heiko will take this.

Rob


From dianders at chromium.org  Thu Jan 21 16:24:10 2016
From: dianders at chromium.org (Doug Anderson)
Date: Thu, 21 Jan 2016 16:24:10 -0800
Subject: [PATCH v4 07/21] usb: dwc2: hcd: fix split transfer schedule
 sequence
In-Reply-To: <201601211352.VXgeFvup%fengguang.wu@intel.com>
References: <1453352672-27890-8-git-send-email-dianders@chromium.org>
 <201601211352.VXgeFvup%fengguang.wu@intel.com>
Message-ID: <CAD=FV=W8+A0MduBs+_he5EKXsLbDxqVu419+Lco+SiUNUyw6Cw@mail.gmail.com>

Hi,

On Wed, Jan 20, 2016 at 9:24 PM, kbuild test robot <lkp at intel.com> wrote:
> Hi Douglas,
>
> [auto build test ERROR on next-20160120]
> [cannot apply to v4.4-rc8 v4.4-rc7 v4.4-rc6 v4.4]
> [if your patch is applied to the wrong git tree, please drop us a note to help improving the system]
>
> url:    https://github.com/0day-ci/linux/commits/Douglas-Anderson/usb-dwc2-host-Fix-and-speed-up-all-the-stuff-especially-with-splits/20160121-131414
> config: x86_64-randconfig-x019-01201142 (attached as .config)
> reproduce:
>         # save the attached .config to linux build tree
>         make ARCH=x86_64
>
> All errors (new ones prefixed by >>):
>
>    drivers/usb/dwc2/core.c: In function 'dwc2_hc_start_transfer':
>>> drivers/usb/dwc2/core.c:1876:17: error: 'struct dwc2_hsotg' has no member named 'split_order'
>               &hsotg->split_order);
>                     ^
> --
>>> /bin/bash: line 0: [: -ge: unary operator expected
>
> vim +1876 drivers/usb/dwc2/core.c
>
>   1870                          ec_mc = 3;
>   1871                  else
>   1872                          ec_mc = 1;
>   1873
>   1874                  /* Put ourselves on the list to keep order straight */
>   1875                  list_move_tail(&chan->split_order_list_entry,
>> 1876                                 &hsotg->split_order);
>   1877          } else {
>   1878                  if (dbg_hc(chan))
>   1879                          dev_vdbg(hsotg->dev, "no split\n");
>
> ---
> 0-DAY kernel test infrastructure                Open Source Technology Center
> https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

I've got a fix this and the other error reported by the bot.  I'll
plan to fix both tomorrow and send up a v5 unless someone gives me
some other feedback on the series that takes me longer than a day to
deal with.

-Doug


From mark.yao at rock-chips.com  Thu Jan 21 17:07:44 2016
From: mark.yao at rock-chips.com (Mark yao)
Date: Fri, 22 Jan 2016 09:07:44 +0800
Subject: [PATCH v2] drm/rockchip: respect CONFIG_DRM_FBDEV_EMULATION
In-Reply-To: <20160121181934.76cb2c03.john@metanate.com>
References: <1453384426-32617-1-git-send-email-john@metanate.com>	<20160121165251.GO19130@phenom.ffwll.local>
 <20160121181934.76cb2c03.john@metanate.com>
Message-ID: <56A180E0.6090601@rock-chips.com>

On 2016?01?22? 02:19, John Keeping wrote:
> If DRM_FBDEV_EMULATION is not selected in the config then we can save a
> bit of space by not including the framebuffer code.
>
> Signed-off-by: John Keeping <john at metanate.com>
> ---
> On Thu, 21 Jan 2016 17:52:51 +0100, Daniel Vetter wrote:
>
>> On Thu, Jan 21, 2016 at 01:53:46PM +0000, John Keeping wrote:
>>> If DRM_FBDEV_EMULATION is not selected in the config then we should not
>>> setup a framebuffer console.
>> It should just magically work, and this patch here just removes a bit more
>> dead code in the rockchip driver itself that's not needed in case fbdev
>> emulation is disabled.
>>
>> Can you please double-check this is the case and then resend with a
>> clarified commit message?
> You're right, it does work without this change, so this is just to save
> a bit of space when we don't need this feature.
>
> v2:
> - change commit message to clarify that this is only to remove some dead
>    code
>
>   drivers/gpu/drm/rockchip/Makefile             |  3 ++-
>   drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h | 11 +++++++++++
>   2 files changed, 13 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> index a4e03bc..f6a809a 100644
> --- a/drivers/gpu/drm/rockchip/Makefile
> +++ b/drivers/gpu/drm/rockchip/Makefile
> @@ -2,8 +2,9 @@
>   # Makefile for the drm device driver.  This driver provides support for the
>   # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
>   
> -rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
> +rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
>   		rockchip_drm_gem.o rockchip_drm_vop.o
> +rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
>   
>   obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
>   obj-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
> index 50432e9..73718c5 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
> @@ -15,7 +15,18 @@
>   #ifndef _ROCKCHIP_DRM_FBDEV_H
>   #define _ROCKCHIP_DRM_FBDEV_H
>   
> +#ifdef CONFIG_DRM_FBDEV_EMULATION
>   int rockchip_drm_fbdev_init(struct drm_device *dev);
>   void rockchip_drm_fbdev_fini(struct drm_device *dev);
> +#else
> +static inline int rockchip_drm_fbdev_init(struct drm_device *dev)
> +{
> +	return 0;
> +}
> +
> +static inline void rockchip_drm_fbdev_fini(struct drm_device *dev)
> +{
> +}
> +#endif
>   
>   #endif /* _ROCKCHIP_DRM_FBDEV_H */

Looks good and it works on my popmetal board.

Applied to my branch:-) .

-- 
?ark Yao




From ykk at rock-chips.com  Thu Jan 21 17:37:08 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 22 Jan 2016 09:37:08 +0800
Subject: [PATCH v12.1 07/17] drm: rockchip: dp: add rockchip platform dp
 driver
In-Reply-To: <7132332.jmLRiUjx3U@phil>
References: <1450873538-18304-1-git-send-email-ykk@rock-chips.com>
 <1453197893-13413-1-git-send-email-ykk@rock-chips.com>
 <7132332.jmLRiUjx3U@phil>
Message-ID: <56A187C4.2030100@rock-chips.com>

Hi Heiko,

On 01/22/2016 03:11 AM, Heiko Stuebner wrote:
> Hi Yakir,
>
> Am Dienstag, 19. Januar 2016, 18:04:53 schrieb Yakir Yang:
>> Rockchip have three clocks for dp controller, we leave pclk_edp
>> to analogix_dp driver control, and keep the sclk_edp_24m and
>> sclk_edp in platform driver.
>>
>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
>> Tested-by: Javier Martinez Canillas <javier at osg.samsung.com>
>> ---
> [...]
>
>> +static int rockchip_dp_drm_create_encoder(struct rockchip_dp_device *dp)
>> +{
>> +	struct drm_encoder *encoder = &dp->encoder;
>> +	struct drm_device *drm_dev = dp->drm_dev;
>> +	struct device *dev = dp->dev;
>> +	int ret;
>> +
>> +	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
>> +							     dev->of_node);
>> +	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
>> +
>> +	ret = drm_encoder_init(drm_dev, encoder, &rockchip_dp_encoder_funcs,
>> +			       DRM_MODE_ENCODER_TMDS);
> should be
> +			       DRM_MODE_ENCODER_TMDS, NULL);

Ops,  :-|

> Exynos-side seems to be ok though.
>
> With the adapted v12.1 patches it applies cleanly and display is working as
> well. So with the thing above fixed, you could resubmit a full v13,
> especially as you want to drop patch 16 as discussed there.

Got it :)

Thanks,
Yakir
>
> Heiko
>
>
>




From ykk at rock-chips.com  Thu Jan 21 17:53:34 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 22 Jan 2016 09:53:34 +0800
Subject: [PATCH v13 0/17] Add Analogix Core Display Port Driver
Message-ID: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>


Hi all,

   The Samsung Exynos eDP controller and Rockchip RK3288 eDP controller
share the same IP, so a lot of parts can be re-used. I split the common
code into bridge directory, then rk3288 and exynos only need to keep
some platform code. Cause I can't find the exact IP name of exynos dp
controller, so I decide to name dp core driver with "analogix" which I
find in rk3288 eDP TRM

But there are still three light registers setting different between
exynos and rk3288.
1. RK3288 have five special pll registers which not indicate in exynos
   dp controller.
2. The address of DP_PHY_PD(dp phy power manager register) are different
   between rk3288 and exynos.
3. Rk3288 and exynos have different setting with AUX_HW_RETRY_CTL(dp debug
   register).

Due to Mark Yao have introduced the ATOMIC support to Rockchip drm, so it's
okay to use the ATOMIC helpers functions in connector_funcs. No need to
splict the connector init to platform driver anymore, and this is the biggest
change since version 11.

Previous version have been well tested on Rockchip platform and Samsung platform,
but for this version Heiko and me have verified on Rockchip Veyron Chromebook.

Thanks,
- Yakir


Changes in v13:
- Use .enable instead of preprare/commit in encoder_helper_funcs (Heiko)
- Fix the missing parameters with drm_encoder_init() helper function. (Heiko)

Changes in v12:
- Move the connector init to analogix_dp driver, and using ATOMIC helper(Heiko)
- Add the ack from Jingoo
- Remove the enum link_rate_type struct, using the marcos in drm_dp_helper.h (Jingoo)
- Re-order the include headers file alphabetically in phy-rockchip-dp.c (Jingoo)

Changes in v11:
- Uses tabs to fix the indentation issues in analogix_dp_core.h (Heiko)
- Correct the title of this rockchip dp phy document(Rob)
- Add the ack from Rob Herring
- Rename the "analogix,need-force-hpd" to common 'force-hpd' (Rob)
- Add the ack from Rob Herring
- Revert parts of Gustavo Padovan's changes in commit:
	drm/exynos: do not start enabling DP at bind() phase
  Add dp phy poweron function in bind time.
- Move the panel prepare from get_modes time to bind time, and move
  the panel unprepare from bridge->disable to unbind time. (Heiko)

Changes in v10:
- Add the ack from Rob Herring
- Correct the ROCKCHIP_ANALOGIX_DP indentation in Kconfig to tabs here (Heiko)
- Add the ack from Rob Herring
- Fix the wrong macro value of GRF_EDP_REF_CLK_SEL_INTER_HIWORD_MASK
    BIT(4) -> BIT(20)
- Remove the surplus "plat_data" check. (Heiko)
-       switch (dp->plat_data && dp->plat_data->dev_type) {
+       switch (dp->plat_data->dev_type) {

Changes in v9:
- Document more details for 'ports' property.
- Removed the unused the variable "res" in probe function. (Heiko)
- Removed the unused head file.

Changes in v8:
- Correct the right document path of display-timing.txt (Heiko)
- Correct the misspell of 'from' to 'frm'. (Heiko)
- Modify the commit subject name. (Heiko)
- Fix the mixed spacers on macro definitions. (Heiko)
- Remove the unnecessary empty line after clk_prepare_enable. (Heiko)
- Remove the specific address in the example node name. (Heiko)

Changes in v7:
- Back to use the of_property_read_bool() interfacs to provoid backward
  compatibility of "hsync-active-high" "vsync-active-high" "interlaced"
  to avoid -EOVERFLOW error (Krzysztof)
- Simply the commit message. (Kishon)
- Symmetrical enable/disbale the phy clock and power. (Kishon)
- Simplify the commit message. (Kishon)

Changes in v6:
- Fix the Kconfig recursive dependency (Javier)
- Fix Peach Pit hpd property name error:
-       hpd-gpio = <&gpx2 6 0>;
+       hpd-gpios = <&gpx2 6 0>;

Changes in v5:
- Correct the check condition of gpio_is_valid when driver try to get
  the "hpd-gpios" DT propery. (Heiko)
- Move the platform attach callback in the front of core driver bridge
  attch function. Cause once platform failed at attach, core driver should
  still failed, so no need to init connector before platform attached (Krzysztof)
- Keep code style no changes with the previous exynos_dp_code.c in this
  patch, and update commit message about the new export symbol (Krzysztof)
- Gather the device type patch (v4 11/16) into this one. (Krzysztof)
- leave out the connector registration to analogix platform driver. (Thierry)
- Resequence this patch after analogix_dp driver have been split
  from exynos_dp code, and rephrase reasonable commit message, and
  remove some controversial style (Krzysztof)
    -		analogix_dp_write_byte_to_dpcd(
    - 				dp, DP_TEST_RESPONSE,
    +		analogix_dp_write_byte_to_dpcd(dp,
    +				DP_TEST_RESPONSE,
				DP_TEST_EDID_CHECKSUM_WRITE);
- Switch video timing type to "u32", so driver could use "of_property_read_u32"
  to get the backword timing values. Krzysztof suggest me that driver could use
  the "of_property_read_bool" to get backword timing values, but that interfacs
  would modify the original drm_display_mode timing directly (whether those
  properties exists or not).
- Correct the misspell in commit message. (Krzysztof)
- Remove the empty line at the end of document, and correct the endpoint
  numbers in the example DT node, and remove the regulator iomux setting
  in driver code while using the pinctl in devicetree instead. (Heiko)
- Add device type declared, cause the previous "platform device type
  support (v4 11/16)" already merge into (v5 02/14).
- Implement connector registration code. (Thierry)
- Split binding doc's from driver changes. (Rob)
- Add eDP hotplug pinctrl property. (Heiko)
- Remove "reg" DT property, cause driver could poweron/poweroff phy via
  the exist "grf" syscon already. And rename the example DT node from
  "edp_phy: phy at ff770274" to "edp_phy: edp-phy" directly. (Heiko)
- Add deivce_node at the front of driver, update phy_ops type from "static
  struct" to "static const struct". And correct the input paramters of
  devm_phy_create() interfaces. (Heiko)
- Split binding doc's from driver changes. (Rob)
- Update the rockchip,grf explain in document, and correct the clock required
  elemets in document. (Rob & Heiko)

Changes in v4:
- Update "analogix,hpd-gpios" to "hpd-gpios" DT propery. (Rob)
- Rename "analogix_dp-exynos.c" file name to "exynos_dp.c" (Jingoo)
- Create a separate folder for analogix code in bridge/ (Archit)
- Update commit message more readable. (Jingoo)
- Adjust the order from 05 to 04
- Provide backword compatibility with samsung. (Krzysztof)
- Split all DTS changes, and provide backward compatibility. Mark old
  properties as deprecated but still support them. (Krzysztof)
- Update "analogix,hpd-gpio" to "hpd-gpios" prop name. (Rob)
- Deprecated some properties which could parsed from Edid/Mode/DPCD. (Thierry)
    "analogix,color-space" & "analogix,color-depth"   &
    "analogix,link-rate"   & "analogix,lane-count"    &
    "analogix,ycbcr-coeff" & "analogix,dynamic-range" &
    "vsync-active-high"    & "hsync-active-high"      & "interlaces"
- Separate all DTS changes to a separate patch. (Krzysztof)
- Remove some deprecated DT properties in rockchip dp document.
- Add commit message, and remove the redundant rockchip_dp_phy_init()
  function, move those code to probe() method. And remove driver .owner
  number. (Kishon)
- Seprate the link-rate and lane-count limit out with the device_type
  flag. (Thierry)
- Take Jingoo suggest, add commit messages.
- Call drm_panel_prepare() in .get_modes function, ensure panel should
  power on before driver try to read edid message.

Changes in v3:
- Move exynos's video_timing code to analogix_dp-exynos platform driver,
  add get_modes method to struct analogix_dp_plat_data. (Thierry)
- Rename some "samsung*" dts propery to "analogix*". (Heiko)
- The link_rate and lane_count shouldn't config to the DT property value
  directly, but we can take those as hardware limite. For example, RK3288
  only support 4 physical lanes of 2.7/1.62 Gbps/lane, so DT property would
  like "link-rate = 0x0a" "lane-count = 4". (Thierry)
- Dynamic parse video timing info from struct drm_display_mode and
  struct drm_display_info. (Thierry)
- Add devicetree binding documents. (Heiko)
- Remove sync pol & colorimetry properies from the new analogix dp driver
  devicetree binding. (Thierry)
- Update the exist exynos dtsi file with the latest DP DT properies.
- Leave "sclk_edp_24m" to rockchip dp phy driver which name to "24m",
  and leave "sclk_edp" to analogix dp core driver which name to "dp",
  and leave "pclk_edp" to rockchip dp platform driver which name to
  "pclk". (Thierry & Heiko)
- Add devicetree binding document. (Heiko)
- Remove "rockchip,panel" DT property, take use of remote point to get panel
  node. (Heiko)
- Add the new function point dp_platdata->get_modes() init.
- Suggest, add rockchip dp phy driver, collect the phy clocks and
  power control. (Heiko)
- Add "analogix,need-force-hpd" to indicate whether driver need foce
  hpd when hpd detect failed.
- move dp hpd detect to connector detect function.
- Add edid modes parse support

Changes in v2:
- Remove new copyright (Jingoo)
- Fix compiled failed due to analogix_dp_device misspell
- Improved commit message more readable, and avoid using some
  uncommon style like bellow: (Joe Preches)
    -  retval = exynos_dp_read_bytes_from_i2c(...
  				  ...);
    +  retval =
    +  exynos_dp_read_bytes_from_i2c(......);
- Get panel node with remote-endpoint method, and create devicetree binding
  for driver. (Heiko)
- Remove the clock enable/disbale with "sclk_edp" & "sclk_edp_24m",
  leave those clock to rockchip dp phy driver.
- Fix compile failed dut to phy_pd_addr variable misspell error

Yakir Yang (17):
  drm: bridge: analogix/dp: split exynos dp driver to bridge directory
  drm: bridge: analogix/dp: fix some obvious code style
  drm: bridge: analogix/dp: remove duplicate configuration of link rate
    and link count
  drm: bridge: analogix/dp: dynamic parse sync_pol & interlace &
    dynamic_range
  dt-bindings: add document for analogix display port driver
  ARM: dts: exynos/dp: remove some properties that deprecated by
    analogix_dp driver
  drm: rockchip: dp: add rockchip platform dp driver
  dt-bindings: add document for rockchip variant of analogix_dp
  phy: Add driver for rockchip Display Port PHY
  dt-bindings: add document for rockchip dp phy
  drm: bridge: analogix/dp: add some rk3288 special registers setting
  drm: bridge: analogix/dp: add max link rate and lane count limit for
    RK3288
  drm: bridge: analogix/dp: try force hpd after plug in lookup failed
  drm: bridge: analogix/dp: move hpd detect to connector detect function
  drm: bridge: analogix/dp: add edid modes parse in get_modes method
  drm: bridge: analogix/dp: add panel prepare/unprepare in
    suspend/resume time
  drm: bridge: analogix/dp: Fix the possible dead lock in bridge disable
    time

 .../bindings/display/bridge/analogix_dp.txt        |   52 +
 .../bindings/display/exynos/exynos_dp.txt          |   93 +-
 .../display/rockchip/analogix_dp-rockchip.txt      |   92 ++
 .../devicetree/bindings/phy/rockchip-dp-phy.txt    |   22 +
 arch/arm/boot/dts/exynos5250-arndale.dts           |    2 -
 arch/arm/boot/dts/exynos5250-smdk5250.dts          |    2 -
 arch/arm/boot/dts/exynos5250-snow-common.dtsi      |    4 +-
 arch/arm/boot/dts/exynos5250-spring.dts            |    4 +-
 arch/arm/boot/dts/exynos5420-peach-pit.dts         |    4 +-
 arch/arm/boot/dts/exynos5420-smdk5420.dts          |    2 -
 arch/arm/boot/dts/exynos5800-peach-pi.dts          |    2 -
 drivers/gpu/drm/bridge/Kconfig                     |    2 +
 drivers/gpu/drm/bridge/Makefile                    |    1 +
 drivers/gpu/drm/bridge/analogix/Kconfig            |    3 +
 drivers/gpu/drm/bridge/analogix/Makefile           |    1 +
 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 1430 ++++++++++++++++++
 drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  281 ++++
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  | 1320 +++++++++++++++++
 .../analogix/analogix_dp_reg.h}                    |  270 ++--
 drivers/gpu/drm/exynos/Kconfig                     |    3 +-
 drivers/gpu/drm/exynos/Makefile                    |    2 +-
 drivers/gpu/drm/exynos/exynos_dp.c                 |  328 +++++
 drivers/gpu/drm/exynos/exynos_dp_core.c            | 1515 --------------------
 drivers/gpu/drm/exynos/exynos_dp_core.h            |  282 ----
 drivers/gpu/drm/exynos/exynos_dp_reg.c             | 1263 ----------------
 drivers/gpu/drm/rockchip/Kconfig                   |    9 +
 drivers/gpu/drm/rockchip/Makefile                  |    1 +
 drivers/gpu/drm/rockchip/analogix_dp-rockchip.c    |  384 +++++
 drivers/phy/Kconfig                                |    7 +
 drivers/phy/Makefile                               |    1 +
 drivers/phy/phy-rockchip-dp.c                      |  151 ++
 include/drm/bridge/analogix_dp.h                   |   41 +
 32 files changed, 4300 insertions(+), 3274 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/display/bridge/analogix_dp.txt
 create mode 100644 Documentation/devicetree/bindings/display/rockchip/analogix_dp-rockchip.txt
 create mode 100644 Documentation/devicetree/bindings/phy/rockchip-dp-phy.txt
 create mode 100644 drivers/gpu/drm/bridge/analogix/Kconfig
 create mode 100644 drivers/gpu/drm/bridge/analogix/Makefile
 create mode 100644 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
 create mode 100644 drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
 create mode 100644 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
 rename drivers/gpu/drm/{exynos/exynos_dp_reg.h => bridge/analogix/analogix_dp_reg.h} (62%)
 create mode 100644 drivers/gpu/drm/exynos/exynos_dp.c
 delete mode 100644 drivers/gpu/drm/exynos/exynos_dp_core.c
 delete mode 100644 drivers/gpu/drm/exynos/exynos_dp_core.h
 delete mode 100644 drivers/gpu/drm/exynos/exynos_dp_reg.c
 create mode 100644 drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
 create mode 100644 drivers/phy/phy-rockchip-dp.c
 create mode 100644 include/drm/bridge/analogix_dp.h

-- 
1.9.1




From ykk at rock-chips.com  Thu Jan 21 17:54:39 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 22 Jan 2016 09:54:39 +0800
Subject: [PATCH v13 02/17] drm: bridge: analogix/dp: fix some obvious code
 style
In-Reply-To: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
References: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1453427679-23434-1-git-send-email-ykk@rock-chips.com>

Fix some obvious alignment problems, like alignment and line
over 80 characters problems, make this easy to be maintained
later.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Acked-by: Jingoo Han <jingoohan1 at gmail.com>
Reviewed-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
Tested-by: Javier Martinez Canillas <javier at osg.samsung.com>
---
Changes in v13: None
Changes in v12:
- Add the ack from Jingoo

Changes in v11: None
Changes in v10: None
Changes in v9: None
Changes in v8: None
Changes in v7: None
Changes in v6: None
Changes in v5:
- Resequence this patch after analogix_dp driver have been split
  from exynos_dp code, and rephrase reasonable commit message, and
  remove some controversial style (Krzysztof)
    -		analogix_dp_write_byte_to_dpcd(
    - 				dp, DP_TEST_RESPONSE,
    +		analogix_dp_write_byte_to_dpcd(dp,
    +				DP_TEST_RESPONSE,
				DP_TEST_EDID_CHECKSUM_WRITE);

Changes in v4: None
Changes in v3: None
Changes in v2:
- Improved commit message more readable, and avoid using some
  uncommon style like bellow: (Joe Preches)
    -  retval = exynos_dp_read_bytes_from_i2c(...
  				  ...);
    +  retval =
    +  exynos_dp_read_bytes_from_i2c(......);

 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 129 ++++++++++-----------
 drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  72 ++++++------
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  | 124 ++++++++++----------
 3 files changed, 163 insertions(+), 162 deletions(-)

diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index 392c296..6901a6f 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -64,7 +64,7 @@ static int analogix_dp_detect_hpd(struct analogix_dp_device *dp)
 
 	while (analogix_dp_get_plug_in_status(dp) != 0) {
 		timeout_loop++;
-		if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) {
+		if (timeout_loop > DP_TIMEOUT_LOOP_COUNT) {
 			dev_err(dp->dev, "failed to get hpd plug status\n");
 			return -ETIMEDOUT;
 		}
@@ -101,8 +101,8 @@ static int analogix_dp_read_edid(struct analogix_dp_device *dp)
 
 	/* Read Extension Flag, Number of 128-byte EDID extension blocks */
 	retval = analogix_dp_read_byte_from_i2c(dp, I2C_EDID_DEVICE_ADDR,
-				EDID_EXTENSION_FLAG,
-				&extend_block);
+						EDID_EXTENSION_FLAG,
+						&extend_block);
 	if (retval)
 		return retval;
 
@@ -110,7 +110,8 @@ static int analogix_dp_read_edid(struct analogix_dp_device *dp)
 		dev_dbg(dp->dev, "EDID data includes a single extension!\n");
 
 		/* Read EDID data */
-		retval = analogix_dp_read_bytes_from_i2c(dp, I2C_EDID_DEVICE_ADDR,
+		retval = analogix_dp_read_bytes_from_i2c(dp,
+						I2C_EDID_DEVICE_ADDR,
 						EDID_HEADER_PATTERN,
 						EDID_BLOCK_LENGTH,
 						&edid[EDID_HEADER_PATTERN]);
@@ -141,7 +142,7 @@ static int analogix_dp_read_edid(struct analogix_dp_device *dp)
 		}
 
 		analogix_dp_read_byte_from_dpcd(dp, DP_TEST_REQUEST,
-					&test_vector);
+						&test_vector);
 		if (test_vector & DP_TEST_LINK_EDID_READ) {
 			analogix_dp_write_byte_to_dpcd(dp,
 				DP_TEST_EDID_CHECKSUM,
@@ -155,10 +156,8 @@ static int analogix_dp_read_edid(struct analogix_dp_device *dp)
 
 		/* Read EDID data */
 		retval = analogix_dp_read_bytes_from_i2c(dp,
-				I2C_EDID_DEVICE_ADDR,
-				EDID_HEADER_PATTERN,
-				EDID_BLOCK_LENGTH,
-				&edid[EDID_HEADER_PATTERN]);
+				I2C_EDID_DEVICE_ADDR, EDID_HEADER_PATTERN,
+				EDID_BLOCK_LENGTH, &edid[EDID_HEADER_PATTERN]);
 		if (retval != 0) {
 			dev_err(dp->dev, "EDID Read failed!\n");
 			return -EIO;
@@ -169,16 +168,13 @@ static int analogix_dp_read_edid(struct analogix_dp_device *dp)
 			return -EIO;
 		}
 
-		analogix_dp_read_byte_from_dpcd(dp,
-			DP_TEST_REQUEST,
-			&test_vector);
+		analogix_dp_read_byte_from_dpcd(dp, DP_TEST_REQUEST,
+						&test_vector);
 		if (test_vector & DP_TEST_LINK_EDID_READ) {
 			analogix_dp_write_byte_to_dpcd(dp,
-				DP_TEST_EDID_CHECKSUM,
-				edid[EDID_CHECKSUM]);
+				DP_TEST_EDID_CHECKSUM, edid[EDID_CHECKSUM]);
 			analogix_dp_write_byte_to_dpcd(dp,
-				DP_TEST_RESPONSE,
-				DP_TEST_EDID_CHECKSUM_WRITE);
+				DP_TEST_RESPONSE, DP_TEST_EDID_CHECKSUM_WRITE);
 		}
 	}
 
@@ -193,8 +189,7 @@ static int analogix_dp_handle_edid(struct analogix_dp_device *dp)
 	int retval;
 
 	/* Read DPCD DP_DPCD_REV~RECEIVE_PORT1_CAP_1 */
-	retval = analogix_dp_read_bytes_from_dpcd(dp, DP_DPCD_REV,
-				12, buf);
+	retval = analogix_dp_read_bytes_from_dpcd(dp, DP_DPCD_REV, 12, buf);
 	if (retval)
 		return retval;
 
@@ -208,8 +203,9 @@ static int analogix_dp_handle_edid(struct analogix_dp_device *dp)
 	return retval;
 }
 
-static void analogix_dp_enable_rx_to_enhanced_mode(struct analogix_dp_device *dp,
-						bool enable)
+static void
+analogix_dp_enable_rx_to_enhanced_mode(struct analogix_dp_device *dp,
+				       bool enable)
 {
 	u8 data;
 
@@ -217,11 +213,11 @@ static void analogix_dp_enable_rx_to_enhanced_mode(struct analogix_dp_device *dp
 
 	if (enable)
 		analogix_dp_write_byte_to_dpcd(dp, DP_LANE_COUNT_SET,
-			DP_LANE_COUNT_ENHANCED_FRAME_EN |
-			DPCD_LANE_COUNT_SET(data));
+					       DP_LANE_COUNT_ENHANCED_FRAME_EN |
+					       DPCD_LANE_COUNT_SET(data));
 	else
 		analogix_dp_write_byte_to_dpcd(dp, DP_LANE_COUNT_SET,
-			DPCD_LANE_COUNT_SET(data));
+					       DPCD_LANE_COUNT_SET(data));
 }
 
 static int analogix_dp_is_enhanced_mode_available(struct analogix_dp_device *dp)
@@ -248,13 +244,13 @@ static void analogix_dp_training_pattern_dis(struct analogix_dp_device *dp)
 {
 	analogix_dp_set_training_pattern(dp, DP_NONE);
 
-	analogix_dp_write_byte_to_dpcd(dp,
-		DP_TRAINING_PATTERN_SET,
-		DP_TRAINING_PATTERN_DISABLE);
+	analogix_dp_write_byte_to_dpcd(dp, DP_TRAINING_PATTERN_SET,
+				       DP_TRAINING_PATTERN_DISABLE);
 }
 
-static void analogix_dp_set_lane_lane_pre_emphasis(struct analogix_dp_device *dp,
-					int pre_emphasis, int lane)
+static void
+analogix_dp_set_lane_lane_pre_emphasis(struct analogix_dp_device *dp,
+				       int pre_emphasis, int lane)
 {
 	switch (lane) {
 	case 0:
@@ -294,8 +290,7 @@ static int analogix_dp_link_start(struct analogix_dp_device *dp)
 	/* Setup RX configuration */
 	buf[0] = dp->link_train.link_rate;
 	buf[1] = dp->link_train.lane_count;
-	retval = analogix_dp_write_bytes_to_dpcd(dp, DP_LINK_BW_SET,
-				2, buf);
+	retval = analogix_dp_write_bytes_to_dpcd(dp, DP_LINK_BW_SET, 2, buf);
 	if (retval)
 		return retval;
 
@@ -331,7 +326,7 @@ static int analogix_dp_link_start(struct analogix_dp_device *dp)
 			    DP_TRAIN_VOLTAGE_SWING_LEVEL_0;
 
 	retval = analogix_dp_write_bytes_to_dpcd(dp, DP_TRAINING_LANE0_SET,
-			lane_count, buf);
+						 lane_count, buf);
 
 	return retval;
 }
@@ -339,7 +334,7 @@ static int analogix_dp_link_start(struct analogix_dp_device *dp)
 static unsigned char analogix_dp_get_lane_status(u8 link_status[2], int lane)
 {
 	int shift = (lane & 1) * 4;
-	u8 link_value = link_status[lane>>1];
+	u8 link_value = link_status[lane >> 1];
 
 	return (link_value >> shift) & 0xf;
 }
@@ -358,7 +353,7 @@ static int analogix_dp_clock_recovery_ok(u8 link_status[2], int lane_count)
 }
 
 static int analogix_dp_channel_eq_ok(u8 link_status[2], u8 link_align,
-				int lane_count)
+				     int lane_count)
 {
 	int lane;
 	u8 lane_status;
@@ -376,11 +371,11 @@ static int analogix_dp_channel_eq_ok(u8 link_status[2], u8 link_align,
 	return 0;
 }
 
-static unsigned char analogix_dp_get_adjust_request_voltage(u8 adjust_request[2],
-							int lane)
+static unsigned char
+analogix_dp_get_adjust_request_voltage(u8 adjust_request[2], int lane)
 {
 	int shift = (lane & 1) * 4;
-	u8 link_value = adjust_request[lane>>1];
+	u8 link_value = adjust_request[lane >> 1];
 
 	return (link_value >> shift) & 0x3;
 }
@@ -390,13 +385,13 @@ static unsigned char analogix_dp_get_adjust_request_pre_emphasis(
 					int lane)
 {
 	int shift = (lane & 1) * 4;
-	u8 link_value = adjust_request[lane>>1];
+	u8 link_value = adjust_request[lane >> 1];
 
 	return ((link_value >> shift) & 0xc) >> 2;
 }
 
 static void analogix_dp_set_lane_link_training(struct analogix_dp_device *dp,
-					u8 training_lane_set, int lane)
+					       u8 training_lane_set, int lane)
 {
 	switch (lane) {
 	case 0:
@@ -416,9 +411,9 @@ static void analogix_dp_set_lane_link_training(struct analogix_dp_device *dp,
 	}
 }
 
-static unsigned int analogix_dp_get_lane_link_training(
-				struct analogix_dp_device *dp,
-				int lane)
+static unsigned int
+analogix_dp_get_lane_link_training(struct analogix_dp_device *dp,
+				   int lane)
 {
 	u32 reg;
 
@@ -452,7 +447,7 @@ static void analogix_dp_reduce_link_rate(struct analogix_dp_device *dp)
 }
 
 static void analogix_dp_get_adjust_training_lane(struct analogix_dp_device *dp,
-					u8 adjust_request[2])
+						 u8 adjust_request[2])
 {
 	int lane, lane_count;
 	u8 voltage_swing, pre_emphasis, training_lane;
@@ -625,7 +620,7 @@ static int analogix_dp_process_equalizer_training(struct analogix_dp_device *dp)
 }
 
 static void analogix_dp_get_max_rx_bandwidth(struct analogix_dp_device *dp,
-					u8 *bandwidth)
+					     u8 *bandwidth)
 {
 	u8 data;
 
@@ -638,7 +633,7 @@ static void analogix_dp_get_max_rx_bandwidth(struct analogix_dp_device *dp,
 }
 
 static void analogix_dp_get_max_rx_lane_count(struct analogix_dp_device *dp,
-					u8 *lane_count)
+					      u8 *lane_count)
 {
 	u8 data;
 
@@ -651,8 +646,8 @@ static void analogix_dp_get_max_rx_lane_count(struct analogix_dp_device *dp,
 }
 
 static void analogix_dp_init_training(struct analogix_dp_device *dp,
-			enum link_lane_count_type max_lane,
-			enum link_rate_type max_rate)
+				      enum link_lane_count_type max_lane,
+				      enum link_rate_type max_rate)
 {
 	/*
 	 * MACRO_RST must be applied after the PLL_LOCK to avoid
@@ -665,7 +660,7 @@ static void analogix_dp_init_training(struct analogix_dp_device *dp,
 	analogix_dp_get_max_rx_lane_count(dp, &dp->link_train.lane_count);
 
 	if ((dp->link_train.link_rate != LINK_RATE_1_62GBPS) &&
-	   (dp->link_train.link_rate != LINK_RATE_2_70GBPS)) {
+	    (dp->link_train.link_rate != LINK_RATE_2_70GBPS)) {
 		dev_err(dp->dev, "Rx Max Link Rate is abnormal :%x !\n",
 			dp->link_train.link_rate);
 		dp->link_train.link_rate = LINK_RATE_1_62GBPS;
@@ -725,8 +720,7 @@ static int analogix_dp_sw_link_training(struct analogix_dp_device *dp)
 }
 
 static int analogix_dp_set_link_train(struct analogix_dp_device *dp,
-				u32 count,
-				u32 bwtype)
+				      u32 count, u32 bwtype)
 {
 	int i;
 	int retval;
@@ -762,7 +756,7 @@ static int analogix_dp_config_video(struct analogix_dp_device *dp)
 		timeout_loop++;
 		if (analogix_dp_is_slave_video_stream_clock_on(dp) == 0)
 			break;
-		if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) {
+		if (timeout_loop > DP_TIMEOUT_LOOP_COUNT) {
 			dev_err(dp->dev, "Timeout of video streamclk ok\n");
 			return -ETIMEDOUT;
 		}
@@ -793,7 +787,7 @@ static int analogix_dp_config_video(struct analogix_dp_device *dp)
 		} else if (done_count) {
 			done_count = 0;
 		}
-		if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) {
+		if (timeout_loop > DP_TIMEOUT_LOOP_COUNT) {
 			dev_err(dp->dev, "Timeout of video streamclk ok\n");
 			return -ETIMEDOUT;
 		}
@@ -807,25 +801,24 @@ static int analogix_dp_config_video(struct analogix_dp_device *dp)
 	return retval;
 }
 
-static void analogix_dp_enable_scramble(struct analogix_dp_device *dp, bool enable)
+static void analogix_dp_enable_scramble(struct analogix_dp_device *dp,
+					bool enable)
 {
 	u8 data;
 
 	if (enable) {
 		analogix_dp_enable_scrambling(dp);
 
-		analogix_dp_read_byte_from_dpcd(dp,
-			DP_TRAINING_PATTERN_SET,
-			&data);
+		analogix_dp_read_byte_from_dpcd(dp, DP_TRAINING_PATTERN_SET,
+						&data);
 		analogix_dp_write_byte_to_dpcd(dp,
 			DP_TRAINING_PATTERN_SET,
 			(u8)(data & ~DP_LINK_SCRAMBLING_DISABLE));
 	} else {
 		analogix_dp_disable_scrambling(dp);
 
-		analogix_dp_read_byte_from_dpcd(dp,
-			DP_TRAINING_PATTERN_SET,
-			&data);
+		analogix_dp_read_byte_from_dpcd(dp, DP_TRAINING_PATTERN_SET,
+						&data);
 		analogix_dp_write_byte_to_dpcd(dp,
 			DP_TRAINING_PATTERN_SET,
 			(u8)(data | DP_LINK_SCRAMBLING_DISABLE));
@@ -898,7 +891,7 @@ static void analogix_dp_commit(struct analogix_dp_device *dp)
 	}
 
 	ret = analogix_dp_set_link_train(dp, dp->video_info->lane_count,
-					dp->video_info->link_rate);
+					 dp->video_info->link_rate);
 	if (ret) {
 		dev_err(dp->dev, "unable to do link train\n");
 		return;
@@ -1130,8 +1123,8 @@ static struct video_info *analogix_dp_dt_parse_pdata(struct device *dev)
 	struct device_node *dp_node = dev->of_node;
 	struct video_info *dp_video_config;
 
-	dp_video_config = devm_kzalloc(dev,
-				sizeof(*dp_video_config), GFP_KERNEL);
+	dp_video_config = devm_kzalloc(dev, sizeof(*dp_video_config),
+				       GFP_KERNEL);
 	if (!dp_video_config)
 		return ERR_PTR(-ENOMEM);
 
@@ -1145,37 +1138,37 @@ static struct video_info *analogix_dp_dt_parse_pdata(struct device *dev)
 		of_property_read_bool(dp_node, "interlaced");
 
 	if (of_property_read_u32(dp_node, "samsung,color-space",
-				&dp_video_config->color_space)) {
+				 &dp_video_config->color_space)) {
 		dev_err(dev, "failed to get color-space\n");
 		return ERR_PTR(-EINVAL);
 	}
 
 	if (of_property_read_u32(dp_node, "samsung,dynamic-range",
-				&dp_video_config->dynamic_range)) {
+				 &dp_video_config->dynamic_range)) {
 		dev_err(dev, "failed to get dynamic-range\n");
 		return ERR_PTR(-EINVAL);
 	}
 
 	if (of_property_read_u32(dp_node, "samsung,ycbcr-coeff",
-				&dp_video_config->ycbcr_coeff)) {
+				 &dp_video_config->ycbcr_coeff)) {
 		dev_err(dev, "failed to get ycbcr-coeff\n");
 		return ERR_PTR(-EINVAL);
 	}
 
 	if (of_property_read_u32(dp_node, "samsung,color-depth",
-				&dp_video_config->color_depth)) {
+				 &dp_video_config->color_depth)) {
 		dev_err(dev, "failed to get color-depth\n");
 		return ERR_PTR(-EINVAL);
 	}
 
 	if (of_property_read_u32(dp_node, "samsung,link-rate",
-				&dp_video_config->link_rate)) {
+				 &dp_video_config->link_rate)) {
 		dev_err(dev, "failed to get link-rate\n");
 		return ERR_PTR(-EINVAL);
 	}
 
 	if (of_property_read_u32(dp_node, "samsung,lane-count",
-				&dp_video_config->lane_count)) {
+				 &dp_video_config->lane_count)) {
 		dev_err(dev, "failed to get lane-count\n");
 		return ERR_PTR(-EINVAL);
 	}
@@ -1284,7 +1277,7 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev,
 	pm_runtime_enable(dev);
 
 	ret = devm_request_irq(&pdev->dev, dp->irq, analogix_dp_irq_handler,
-			irq_flags, "analogix-dp", dp);
+			       irq_flags, "analogix-dp", dp);
 	if (ret) {
 		dev_err(&pdev->dev, "failed to request irq\n");
 		goto err_disable_pm_runtime;
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
index 70724ec..f7dfc8e 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
@@ -187,50 +187,55 @@ int analogix_dp_get_plug_in_status(struct analogix_dp_device *dp);
 void analogix_dp_enable_sw_function(struct analogix_dp_device *dp);
 int analogix_dp_start_aux_transaction(struct analogix_dp_device *dp);
 int analogix_dp_write_byte_to_dpcd(struct analogix_dp_device *dp,
-				unsigned int reg_addr,
-				unsigned char data);
+				   unsigned int reg_addr,
+				   unsigned char data);
 int analogix_dp_read_byte_from_dpcd(struct analogix_dp_device *dp,
-				unsigned int reg_addr,
-				unsigned char *data);
+				    unsigned int reg_addr,
+				    unsigned char *data);
 int analogix_dp_write_bytes_to_dpcd(struct analogix_dp_device *dp,
-				unsigned int reg_addr,
-				unsigned int count,
-				unsigned char data[]);
+				    unsigned int reg_addr,
+				    unsigned int count,
+				    unsigned char data[]);
 int analogix_dp_read_bytes_from_dpcd(struct analogix_dp_device *dp,
-				unsigned int reg_addr,
-				unsigned int count,
-				unsigned char data[]);
+				     unsigned int reg_addr,
+				     unsigned int count,
+				     unsigned char data[]);
 int analogix_dp_select_i2c_device(struct analogix_dp_device *dp,
-				unsigned int device_addr,
-				unsigned int reg_addr);
+				  unsigned int device_addr,
+				  unsigned int reg_addr);
 int analogix_dp_read_byte_from_i2c(struct analogix_dp_device *dp,
-				unsigned int device_addr,
-				unsigned int reg_addr,
-				unsigned int *data);
+				   unsigned int device_addr,
+				   unsigned int reg_addr,
+				   unsigned int *data);
 int analogix_dp_read_bytes_from_i2c(struct analogix_dp_device *dp,
-				unsigned int device_addr,
-				unsigned int reg_addr,
-				unsigned int count,
-				unsigned char edid[]);
+				    unsigned int device_addr,
+				    unsigned int reg_addr,
+				    unsigned int count,
+				    unsigned char edid[]);
 void analogix_dp_set_link_bandwidth(struct analogix_dp_device *dp, u32 bwtype);
 void analogix_dp_get_link_bandwidth(struct analogix_dp_device *dp, u32 *bwtype);
 void analogix_dp_set_lane_count(struct analogix_dp_device *dp, u32 count);
 void analogix_dp_get_lane_count(struct analogix_dp_device *dp, u32 *count);
-void analogix_dp_enable_enhanced_mode(struct analogix_dp_device *dp, bool enable);
+void analogix_dp_enable_enhanced_mode(struct analogix_dp_device *dp,
+				      bool enable);
 void analogix_dp_set_training_pattern(struct analogix_dp_device *dp,
-				enum pattern_set pattern);
-void analogix_dp_set_lane0_pre_emphasis(struct analogix_dp_device *dp, u32 level);
-void analogix_dp_set_lane1_pre_emphasis(struct analogix_dp_device *dp, u32 level);
-void analogix_dp_set_lane2_pre_emphasis(struct analogix_dp_device *dp, u32 level);
-void analogix_dp_set_lane3_pre_emphasis(struct analogix_dp_device *dp, u32 level);
+				      enum pattern_set pattern);
+void analogix_dp_set_lane0_pre_emphasis(struct analogix_dp_device *dp,
+					u32 level);
+void analogix_dp_set_lane1_pre_emphasis(struct analogix_dp_device *dp,
+					u32 level);
+void analogix_dp_set_lane2_pre_emphasis(struct analogix_dp_device *dp,
+					u32 level);
+void analogix_dp_set_lane3_pre_emphasis(struct analogix_dp_device *dp,
+					u32 level);
 void analogix_dp_set_lane0_link_training(struct analogix_dp_device *dp,
-				u32 training_lane);
+					 u32 training_lane);
 void analogix_dp_set_lane1_link_training(struct analogix_dp_device *dp,
-				u32 training_lane);
+					 u32 training_lane);
 void analogix_dp_set_lane2_link_training(struct analogix_dp_device *dp,
-				u32 training_lane);
+					 u32 training_lane);
 void analogix_dp_set_lane3_link_training(struct analogix_dp_device *dp,
-				u32 training_lane);
+					 u32 training_lane);
 u32 analogix_dp_get_lane0_link_training(struct analogix_dp_device *dp);
 u32 analogix_dp_get_lane1_link_training(struct analogix_dp_device *dp);
 u32 analogix_dp_get_lane2_link_training(struct analogix_dp_device *dp);
@@ -241,11 +246,12 @@ void analogix_dp_init_video(struct analogix_dp_device *dp);
 void analogix_dp_set_video_color_format(struct analogix_dp_device *dp);
 int analogix_dp_is_slave_video_stream_clock_on(struct analogix_dp_device *dp);
 void analogix_dp_set_video_cr_mn(struct analogix_dp_device *dp,
-			enum clock_recovery_m_value_type type,
-			u32 m_value,
-			u32 n_value);
+				 enum clock_recovery_m_value_type type,
+				 u32 m_value,
+				 u32 n_value);
 void analogix_dp_set_video_timing_mode(struct analogix_dp_device *dp, u32 type);
-void analogix_dp_enable_video_master(struct analogix_dp_device *dp, bool enable);
+void analogix_dp_enable_video_master(struct analogix_dp_device *dp,
+				     bool enable);
 void analogix_dp_start_video(struct analogix_dp_device *dp);
 int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp);
 void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp);
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
index 442cc66..a388c0a 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
@@ -54,10 +54,10 @@ void analogix_dp_lane_swap(struct analogix_dp_device *dp, bool enable)
 
 	if (enable)
 		reg = LANE3_MAP_LOGIC_LANE_0 | LANE2_MAP_LOGIC_LANE_1 |
-			LANE1_MAP_LOGIC_LANE_2 | LANE0_MAP_LOGIC_LANE_3;
+		      LANE1_MAP_LOGIC_LANE_2 | LANE0_MAP_LOGIC_LANE_3;
 	else
 		reg = LANE3_MAP_LOGIC_LANE_3 | LANE2_MAP_LOGIC_LANE_2 |
-			LANE1_MAP_LOGIC_LANE_1 | LANE0_MAP_LOGIC_LANE_0;
+		      LANE1_MAP_LOGIC_LANE_1 | LANE0_MAP_LOGIC_LANE_0;
 
 	writel(reg, dp->reg_base + ANALOGIX_DP_LANE_MAP);
 }
@@ -202,8 +202,8 @@ void analogix_dp_set_pll_power_down(struct analogix_dp_device *dp, bool enable)
 }
 
 void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp,
-				enum analog_power_block block,
-				bool enable)
+				       enum analog_power_block block,
+				       bool enable)
 {
 	u32 reg;
 
@@ -399,8 +399,8 @@ void analogix_dp_init_aux(struct analogix_dp_device *dp)
 	analogix_dp_reset_aux(dp);
 
 	/* Disable AUX transaction H/W retry */
-	reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3) | AUX_HW_RETRY_COUNT_SEL(0)|
-		AUX_HW_RETRY_INTERVAL_600_MICROSECONDS;
+	reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3) | AUX_HW_RETRY_COUNT_SEL(0) |
+	      AUX_HW_RETRY_INTERVAL_600_MICROSECONDS;
 	writel(reg, dp->reg_base + ANALOGIX_DP_AUX_HW_RETRY_CTL);
 
 	/* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */
@@ -483,8 +483,8 @@ int analogix_dp_start_aux_transaction(struct analogix_dp_device *dp)
 }
 
 int analogix_dp_write_byte_to_dpcd(struct analogix_dp_device *dp,
-				unsigned int reg_addr,
-				unsigned char data)
+				   unsigned int reg_addr,
+				   unsigned char data)
 {
 	u32 reg;
 	int i;
@@ -519,17 +519,16 @@ int analogix_dp_write_byte_to_dpcd(struct analogix_dp_device *dp,
 		retval = analogix_dp_start_aux_transaction(dp);
 		if (retval == 0)
 			break;
-		else
-			dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
-				__func__);
+
+		dev_dbg(dp->dev, "%s: Aux Transaction fail!\n", __func__);
 	}
 
 	return retval;
 }
 
 int analogix_dp_read_byte_from_dpcd(struct analogix_dp_device *dp,
-				unsigned int reg_addr,
-				unsigned char *data)
+				    unsigned int reg_addr,
+				    unsigned char *data)
 {
 	u32 reg;
 	int i;
@@ -560,9 +559,8 @@ int analogix_dp_read_byte_from_dpcd(struct analogix_dp_device *dp,
 		retval = analogix_dp_start_aux_transaction(dp);
 		if (retval == 0)
 			break;
-		else
-			dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
-				__func__);
+
+		dev_dbg(dp->dev, "%s: Aux Transaction fail!\n", __func__);
 	}
 
 	/* Read data buffer */
@@ -573,9 +571,9 @@ int analogix_dp_read_byte_from_dpcd(struct analogix_dp_device *dp,
 }
 
 int analogix_dp_write_bytes_to_dpcd(struct analogix_dp_device *dp,
-				unsigned int reg_addr,
-				unsigned int count,
-				unsigned char data[])
+				    unsigned int reg_addr,
+				    unsigned int count,
+				    unsigned char data[])
 {
 	u32 reg;
 	unsigned int start_offset;
@@ -608,8 +606,9 @@ int analogix_dp_write_bytes_to_dpcd(struct analogix_dp_device *dp,
 			for (cur_data_idx = 0; cur_data_idx < cur_data_count;
 			     cur_data_idx++) {
 				reg = data[start_offset + cur_data_idx];
-				writel(reg, dp->reg_base + ANALOGIX_DP_BUF_DATA_0
-							  + 4 * cur_data_idx);
+				writel(reg, dp->reg_base +
+				       ANALOGIX_DP_BUF_DATA_0 +
+				       4 * cur_data_idx);
 			}
 
 			/*
@@ -625,9 +624,9 @@ int analogix_dp_write_bytes_to_dpcd(struct analogix_dp_device *dp,
 			retval = analogix_dp_start_aux_transaction(dp);
 			if (retval == 0)
 				break;
-			else
-				dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
-					__func__);
+
+			dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
+				__func__);
 		}
 
 		start_offset += cur_data_count;
@@ -637,9 +636,9 @@ int analogix_dp_write_bytes_to_dpcd(struct analogix_dp_device *dp,
 }
 
 int analogix_dp_read_bytes_from_dpcd(struct analogix_dp_device *dp,
-				unsigned int reg_addr,
-				unsigned int count,
-				unsigned char data[])
+				     unsigned int reg_addr,
+				     unsigned int count,
+				     unsigned char data[])
 {
 	u32 reg;
 	unsigned int start_offset;
@@ -683,9 +682,9 @@ int analogix_dp_read_bytes_from_dpcd(struct analogix_dp_device *dp,
 			retval = analogix_dp_start_aux_transaction(dp);
 			if (retval == 0)
 				break;
-			else
-				dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
-					__func__);
+
+			dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
+				__func__);
 		}
 
 		for (cur_data_idx = 0; cur_data_idx < cur_data_count;
@@ -703,8 +702,8 @@ int analogix_dp_read_bytes_from_dpcd(struct analogix_dp_device *dp,
 }
 
 int analogix_dp_select_i2c_device(struct analogix_dp_device *dp,
-				unsigned int device_addr,
-				unsigned int reg_addr)
+				  unsigned int device_addr,
+				  unsigned int reg_addr)
 {
 	u32 reg;
 	int retval;
@@ -736,9 +735,9 @@ int analogix_dp_select_i2c_device(struct analogix_dp_device *dp,
 }
 
 int analogix_dp_read_byte_from_i2c(struct analogix_dp_device *dp,
-				unsigned int device_addr,
-				unsigned int reg_addr,
-				unsigned int *data)
+				   unsigned int device_addr,
+				   unsigned int reg_addr,
+				   unsigned int *data)
 {
 	u32 reg;
 	int i;
@@ -750,7 +749,8 @@ int analogix_dp_read_byte_from_i2c(struct analogix_dp_device *dp,
 		writel(reg, dp->reg_base + ANALOGIX_DP_BUFFER_DATA_CTL);
 
 		/* Select EDID device */
-		retval = analogix_dp_select_i2c_device(dp, device_addr, reg_addr);
+		retval = analogix_dp_select_i2c_device(dp, device_addr,
+						       reg_addr);
 		if (retval != 0)
 			continue;
 
@@ -767,9 +767,8 @@ int analogix_dp_read_byte_from_i2c(struct analogix_dp_device *dp,
 		retval = analogix_dp_start_aux_transaction(dp);
 		if (retval == 0)
 			break;
-		else
-			dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
-				__func__);
+
+		dev_dbg(dp->dev, "%s: Aux Transaction fail!\n", __func__);
 	}
 
 	/* Read data */
@@ -780,10 +779,10 @@ int analogix_dp_read_byte_from_i2c(struct analogix_dp_device *dp,
 }
 
 int analogix_dp_read_bytes_from_i2c(struct analogix_dp_device *dp,
-				unsigned int device_addr,
-				unsigned int reg_addr,
-				unsigned int count,
-				unsigned char edid[])
+				    unsigned int device_addr,
+				    unsigned int reg_addr,
+				    unsigned int count,
+				    unsigned char edid[])
 {
 	u32 reg;
 	unsigned int i, j;
@@ -828,15 +827,14 @@ int analogix_dp_read_bytes_from_i2c(struct analogix_dp_device *dp,
 				retval = analogix_dp_start_aux_transaction(dp);
 				if (retval == 0)
 					break;
-				else
-					dev_dbg(dp->dev,
-						"%s: Aux Transaction fail!\n",
-						__func__);
+
+				dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
+					__func__);
 			}
 			/* Check if Rx sends defer */
 			reg = readl(dp->reg_base + ANALOGIX_DP_AUX_RX_COMM);
 			if (reg == AUX_RX_COMM_AUX_DEFER ||
-				reg == AUX_RX_COMM_I2C_DEFER) {
+			    reg == AUX_RX_COMM_I2C_DEFER) {
 				dev_err(dp->dev, "Defer: %d\n\n", reg);
 				defer = 1;
 			}
@@ -885,7 +883,8 @@ void analogix_dp_get_lane_count(struct analogix_dp_device *dp, u32 *count)
 	*count = reg;
 }
 
-void analogix_dp_enable_enhanced_mode(struct analogix_dp_device *dp, bool enable)
+void analogix_dp_enable_enhanced_mode(struct analogix_dp_device *dp,
+				      bool enable)
 {
 	u32 reg;
 
@@ -901,7 +900,7 @@ void analogix_dp_enable_enhanced_mode(struct analogix_dp_device *dp, bool enable
 }
 
 void analogix_dp_set_training_pattern(struct analogix_dp_device *dp,
-				enum pattern_set pattern)
+				      enum pattern_set pattern)
 {
 	u32 reg;
 
@@ -933,7 +932,8 @@ void analogix_dp_set_training_pattern(struct analogix_dp_device *dp,
 	}
 }
 
-void analogix_dp_set_lane0_pre_emphasis(struct analogix_dp_device *dp, u32 level)
+void analogix_dp_set_lane0_pre_emphasis(struct analogix_dp_device *dp,
+					u32 level)
 {
 	u32 reg;
 
@@ -943,7 +943,8 @@ void analogix_dp_set_lane0_pre_emphasis(struct analogix_dp_device *dp, u32 level
 	writel(reg, dp->reg_base + ANALOGIX_DP_LN0_LINK_TRAINING_CTL);
 }
 
-void analogix_dp_set_lane1_pre_emphasis(struct analogix_dp_device *dp, u32 level)
+void analogix_dp_set_lane1_pre_emphasis(struct analogix_dp_device *dp,
+					u32 level)
 {
 	u32 reg;
 
@@ -953,7 +954,8 @@ void analogix_dp_set_lane1_pre_emphasis(struct analogix_dp_device *dp, u32 level
 	writel(reg, dp->reg_base + ANALOGIX_DP_LN1_LINK_TRAINING_CTL);
 }
 
-void analogix_dp_set_lane2_pre_emphasis(struct analogix_dp_device *dp, u32 level)
+void analogix_dp_set_lane2_pre_emphasis(struct analogix_dp_device *dp,
+					u32 level)
 {
 	u32 reg;
 
@@ -963,7 +965,8 @@ void analogix_dp_set_lane2_pre_emphasis(struct analogix_dp_device *dp, u32 level
 	writel(reg, dp->reg_base + ANALOGIX_DP_LN2_LINK_TRAINING_CTL);
 }
 
-void analogix_dp_set_lane3_pre_emphasis(struct analogix_dp_device *dp, u32 level)
+void analogix_dp_set_lane3_pre_emphasis(struct analogix_dp_device *dp,
+					u32 level)
 {
 	u32 reg;
 
@@ -974,7 +977,7 @@ void analogix_dp_set_lane3_pre_emphasis(struct analogix_dp_device *dp, u32 level
 }
 
 void analogix_dp_set_lane0_link_training(struct analogix_dp_device *dp,
-					u32 training_lane)
+					 u32 training_lane)
 {
 	u32 reg;
 
@@ -983,7 +986,7 @@ void analogix_dp_set_lane0_link_training(struct analogix_dp_device *dp,
 }
 
 void analogix_dp_set_lane1_link_training(struct analogix_dp_device *dp,
-					u32 training_lane)
+					 u32 training_lane)
 {
 	u32 reg;
 
@@ -1001,7 +1004,7 @@ void analogix_dp_set_lane2_link_training(struct analogix_dp_device *dp,
 }
 
 void analogix_dp_set_lane3_link_training(struct analogix_dp_device *dp,
-					u32 training_lane)
+					 u32 training_lane)
 {
 	u32 reg;
 
@@ -1125,9 +1128,8 @@ int analogix_dp_is_slave_video_stream_clock_on(struct analogix_dp_device *dp)
 }
 
 void analogix_dp_set_video_cr_mn(struct analogix_dp_device *dp,
-		enum clock_recovery_m_value_type type,
-		u32 m_value,
-		u32 n_value)
+				 enum clock_recovery_m_value_type type,
+				 u32 m_value, u32 n_value)
 {
 	u32 reg;
 
@@ -1221,7 +1223,7 @@ void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp)
 	u32 reg;
 
 	reg = readl(dp->reg_base + ANALOGIX_DP_FUNC_EN_1);
-	reg &= ~(MASTER_VID_FUNC_EN_N|SLAVE_VID_FUNC_EN_N);
+	reg &= ~(MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N);
 	reg |= MASTER_VID_FUNC_EN_N;
 	writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_1);
 
-- 
1.9.1




From ykk at rock-chips.com  Thu Jan 21 17:54:49 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 22 Jan 2016 09:54:49 +0800
Subject: [PATCH v13 03/17] drm: bridge: analogix/dp: remove duplicate
 configuration of link rate and link count
In-Reply-To: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
References: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1453427689-23785-1-git-send-email-ykk@rock-chips.com>

link_rate and lane_count already configured in analogix_dp_set_link_train(),
so we don't need to config those repeatly after training finished, just
remove them out.

Beside Display Port 1.2 already support 5.4Gbps link rate, the maximum sets
would change from {1.62Gbps, 2.7Gbps} to {1.62Gbps, 2.7Gbps, 5.4Gbps}.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Tested-by: Javier Martinez Canillas <javier at osg.samsung.com>
---
Changes in v13: None
Changes in v12:
- Remove the enum link_rate_type struct, using the marcos in drm_dp_helper.h (Jingoo)

Changes in v11: None
Changes in v10: None
Changes in v9: None
Changes in v8: None
Changes in v7: None
Changes in v6: None
Changes in v5: None
Changes in v4:
- Update commit message more readable. (Jingoo)
- Adjust the order from 05 to 04

Changes in v3:
- The link_rate and lane_count shouldn't config to the DT property value
  directly, but we can take those as hardware limite. For example, RK3288
  only support 4 physical lanes of 2.7/1.62 Gbps/lane, so DT property would
  like "link-rate = 0x0a" "lane-count = 4". (Thierry)

Changes in v2: None

 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 14 +++++++-------
 drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  7 +------
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  |  2 +-
 3 files changed, 9 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index 6901a6f..b948636 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -627,6 +627,8 @@ static void analogix_dp_get_max_rx_bandwidth(struct analogix_dp_device *dp,
 	/*
 	 * For DP rev.1.1, Maximum link rate of Main Link lanes
 	 * 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps
+	 * For DP rev.1.2, Maximum link rate of Main Link lanes
+	 * 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps, 0x14 = 5.4Gbps
 	 */
 	analogix_dp_read_byte_from_dpcd(dp, DP_MAX_LINK_RATE, &data);
 	*bandwidth = data;
@@ -647,7 +649,7 @@ static void analogix_dp_get_max_rx_lane_count(struct analogix_dp_device *dp,
 
 static void analogix_dp_init_training(struct analogix_dp_device *dp,
 				      enum link_lane_count_type max_lane,
-				      enum link_rate_type max_rate)
+				      int max_rate)
 {
 	/*
 	 * MACRO_RST must be applied after the PLL_LOCK to avoid
@@ -659,11 +661,12 @@ static void analogix_dp_init_training(struct analogix_dp_device *dp,
 	analogix_dp_get_max_rx_bandwidth(dp, &dp->link_train.link_rate);
 	analogix_dp_get_max_rx_lane_count(dp, &dp->link_train.lane_count);
 
-	if ((dp->link_train.link_rate != LINK_RATE_1_62GBPS) &&
-	    (dp->link_train.link_rate != LINK_RATE_2_70GBPS)) {
+	if ((dp->link_train.link_rate != DP_LINK_BW_1_62) &&
+	    (dp->link_train.link_rate != DP_LINK_BW_2_7) &&
+	    (dp->link_train.link_rate != DP_LINK_BW_5_4)) {
 		dev_err(dp->dev, "Rx Max Link Rate is abnormal :%x !\n",
 			dp->link_train.link_rate);
-		dp->link_train.link_rate = LINK_RATE_1_62GBPS;
+		dp->link_train.link_rate = DP_LINK_BW_1_62;
 	}
 
 	if (dp->link_train.lane_count == 0) {
@@ -901,9 +904,6 @@ static void analogix_dp_commit(struct analogix_dp_device *dp)
 	analogix_dp_enable_rx_to_enhanced_mode(dp, 1);
 	analogix_dp_enable_enhanced_mode(dp, 1);
 
-	analogix_dp_set_lane_count(dp, dp->video_info->lane_count);
-	analogix_dp_set_link_bandwidth(dp, dp->video_info->link_rate);
-
 	analogix_dp_init_video(dp);
 	ret = analogix_dp_config_video(dp);
 	if (ret)
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
index f7dfc8e..911642e 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
@@ -20,11 +20,6 @@
 #define MAX_CR_LOOP			5
 #define MAX_EQ_LOOP			5
 
-enum link_rate_type {
-	LINK_RATE_1_62GBPS = 0x06,
-	LINK_RATE_2_70GBPS = 0x0a
-};
-
 enum link_lane_count_type {
 	LANE_COUNT1 = 1,
 	LANE_COUNT2 = 2,
@@ -128,7 +123,7 @@ struct video_info {
 	enum color_coefficient ycbcr_coeff;
 	enum color_depth color_depth;
 
-	enum link_rate_type link_rate;
+	int link_rate;
 	enum link_lane_count_type lane_count;
 };
 
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
index a388c0a..eb0b63c 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
@@ -855,7 +855,7 @@ void analogix_dp_set_link_bandwidth(struct analogix_dp_device *dp, u32 bwtype)
 	u32 reg;
 
 	reg = bwtype;
-	if ((bwtype == LINK_RATE_2_70GBPS) || (bwtype == LINK_RATE_1_62GBPS))
+	if ((bwtype == DP_LINK_BW_2_7) || (bwtype == DP_LINK_BW_1_62))
 		writel(reg, dp->reg_base + ANALOGIX_DP_LINK_BW_SET);
 }
 
-- 
1.9.1




From ykk at rock-chips.com  Thu Jan 21 17:54:21 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 22 Jan 2016 09:54:21 +0800
Subject: [PATCH v13 01/17] drm: bridge: analogix/dp: split exynos dp driver to
 bridge directory
In-Reply-To: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
References: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1453427661-23365-1-git-send-email-ykk@rock-chips.com>

Split the dp core driver from exynos directory to bridge directory,
and rename the core driver to analogix_dp_*, rename the platform
code to exynos_dp.

Beside the new analogix_dp driver would export six hooks.
"analogix_dp_bind()" and "analogix_dp_unbind()"
"analogix_dp_suspned()" and "analogix_dp_resume()"
"analogix_dp_detect()" and "analogix_dp_get_modes()"

The bind/unbind symbols is used for analogix platform driver to connect
with analogix_dp core driver. And the detect/get_modes is used for analogix
platform driver to init the connector.

They reason why connector need register in helper driver is rockchip drm
haven't implement the atomic API, but Exynos drm have implement it, so
there would need two different connector helper functions, that's why we
leave the connector register in helper driver.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v13: None
Changes in v12:
- Move the connector init to analogix_dp driver, and using ATOMIC helper(Heiko)

Changes in v11:
- Uses tabs to fix the indentation issues in analogix_dp_core.h (Heiko)

Changes in v10: None
Changes in v9: None
Changes in v8: None
Changes in v7: None
Changes in v6:
- Fix the Kconfig recursive dependency (Javier)

Changes in v5:
- Correct the check condition of gpio_is_valid when driver try to get
  the "hpd-gpios" DT propery. (Heiko)
- Move the platform attach callback in the front of core driver bridge
  attch function. Cause once platform failed at attach, core driver should
  still failed, so no need to init connector before platform attached (Krzysztof)
- Keep code style no changes with the previous exynos_dp_code.c in this
  patch, and update commit message about the new export symbol (Krzysztof)
- Gather the device type patch (v4 11/16) into this one. (Krzysztof)
- leave out the connector registration to analogix platform driver. (Thierry)

Changes in v4:
- Update "analogix,hpd-gpios" to "hpd-gpios" DT propery. (Rob)
- Rename "analogix_dp-exynos.c" file name to "exynos_dp.c" (Jingoo)
- Create a separate folder for analogix code in bridge/ (Archit)

Changes in v3:
- Move exynos's video_timing code to analogix_dp-exynos platform driver,
  add get_modes method to struct analogix_dp_plat_data. (Thierry)
- Rename some "samsung*" dts propery to "analogix*". (Heiko)

Changes in v2:
- Remove new copyright (Jingoo)
- Fix compiled failed due to analogix_dp_device misspell

 drivers/gpu/drm/bridge/Kconfig                     |    2 +
 drivers/gpu/drm/bridge/Makefile                    |    1 +
 drivers/gpu/drm/bridge/analogix/Kconfig            |    3 +
 drivers/gpu/drm/bridge/analogix/Makefile           |    1 +
 .../analogix/analogix_dp_core.c}                   |  816 +++++--------
 drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  277 +++++
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  | 1263 ++++++++++++++++++++
 .../analogix/analogix_dp_reg.h}                    |  258 ++--
 drivers/gpu/drm/exynos/Kconfig                     |    3 +-
 drivers/gpu/drm/exynos/Makefile                    |    2 +-
 drivers/gpu/drm/exynos/exynos_dp.c                 |  328 +++++
 drivers/gpu/drm/exynos/exynos_dp_core.h            |  282 -----
 drivers/gpu/drm/exynos/exynos_dp_reg.c             | 1263 --------------------
 include/drm/bridge/analogix_dp.h                   |   40 +
 14 files changed, 2373 insertions(+), 2166 deletions(-)
 create mode 100644 drivers/gpu/drm/bridge/analogix/Kconfig
 create mode 100644 drivers/gpu/drm/bridge/analogix/Makefile
 rename drivers/gpu/drm/{exynos/exynos_dp_core.c => bridge/analogix/analogix_dp_core.c} (52%)
 create mode 100644 drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
 create mode 100644 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
 rename drivers/gpu/drm/{exynos/exynos_dp_reg.h => bridge/analogix/analogix_dp_reg.h} (64%)
 create mode 100644 drivers/gpu/drm/exynos/exynos_dp.c
 delete mode 100644 drivers/gpu/drm/exynos/exynos_dp_core.h
 delete mode 100644 drivers/gpu/drm/exynos/exynos_dp_reg.c
 create mode 100644 include/drm/bridge/analogix_dp.h

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 27e2022..efd94e0 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -40,4 +40,6 @@ config DRM_PARADE_PS8622
 	---help---
 	  Parade eDP-LVDS bridge chip driver.
 
+source "drivers/gpu/drm/bridge/analogix/Kconfig"
+
 endmenu
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index f13c33d..ff821f4 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o
 obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o
 obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
 obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
+obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/
diff --git a/drivers/gpu/drm/bridge/analogix/Kconfig b/drivers/gpu/drm/bridge/analogix/Kconfig
new file mode 100644
index 0000000..80f286f
--- /dev/null
+++ b/drivers/gpu/drm/bridge/analogix/Kconfig
@@ -0,0 +1,3 @@
+config DRM_ANALOGIX_DP
+	tristate
+	depends on DRM
diff --git a/drivers/gpu/drm/bridge/analogix/Makefile b/drivers/gpu/drm/bridge/analogix/Makefile
new file mode 100644
index 0000000..9107b86
--- /dev/null
+++ b/drivers/gpu/drm/bridge/analogix/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix_dp_core.o analogix_dp_reg.o
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
similarity index 52%
rename from drivers/gpu/drm/exynos/exynos_dp_core.c
rename to drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index b79c316..392c296 100644
--- a/drivers/gpu/drm/exynos/exynos_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -1,14 +1,14 @@
 /*
- * Samsung SoC DP (Display Port) interface driver.
- *
- * Copyright (C) 2012 Samsung Electronics Co., Ltd.
- * Author: Jingoo Han <jg1.han at samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
+* Analogix DP (Display Port) core interface driver.
+*
+* Copyright (C) 2012 Samsung Electronics Co., Ltd.
+* Author: Jingoo Han <jg1.han at samsung.com>
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*/
 
 #include <linux/module.h>
 #include <linux/platform_device.h>
@@ -18,65 +18,51 @@
 #include <linux/interrupt.h>
 #include <linux/of.h>
 #include <linux/of_gpio.h>
-#include <linux/of_graph.h>
 #include <linux/gpio.h>
 #include <linux/component.h>
 #include <linux/phy/phy.h>
-#include <video/of_display_timing.h>
-#include <video/of_videomode.h>
 
 #include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
-#include <drm/drm_atomic_helper.h>
 #include <drm/drm_panel.h>
 
-#include "exynos_dp_core.h"
-#include "exynos_drm_crtc.h"
+#include <drm/bridge/analogix_dp.h>
 
-#define ctx_from_connector(c)	container_of(c, struct exynos_dp_device, \
-					connector)
+#include "analogix_dp_core.h"
 
-static inline struct exynos_drm_crtc *dp_to_crtc(struct exynos_dp_device *dp)
-{
-	return to_exynos_crtc(dp->encoder.crtc);
-}
-
-static inline struct exynos_dp_device *encoder_to_dp(
-						struct drm_encoder *e)
-{
-	return container_of(e, struct exynos_dp_device, encoder);
-}
+#define to_dp(nm)	container_of(nm, struct analogix_dp_device, nm)
 
 struct bridge_init {
 	struct i2c_client *client;
 	struct device_node *node;
 };
 
-static void exynos_dp_init_dp(struct exynos_dp_device *dp)
+static void analogix_dp_init_dp(struct analogix_dp_device *dp)
 {
-	exynos_dp_reset(dp);
+	analogix_dp_reset(dp);
 
-	exynos_dp_swreset(dp);
+	analogix_dp_swreset(dp);
 
-	exynos_dp_init_analog_param(dp);
-	exynos_dp_init_interrupt(dp);
+	analogix_dp_init_analog_param(dp);
+	analogix_dp_init_interrupt(dp);
 
 	/* SW defined function Normal operation */
-	exynos_dp_enable_sw_function(dp);
+	analogix_dp_enable_sw_function(dp);
 
-	exynos_dp_config_interrupt(dp);
-	exynos_dp_init_analog_func(dp);
+	analogix_dp_config_interrupt(dp);
+	analogix_dp_init_analog_func(dp);
 
-	exynos_dp_init_hpd(dp);
-	exynos_dp_init_aux(dp);
+	analogix_dp_init_hpd(dp);
+	analogix_dp_init_aux(dp);
 }
 
-static int exynos_dp_detect_hpd(struct exynos_dp_device *dp)
+static int analogix_dp_detect_hpd(struct analogix_dp_device *dp)
 {
 	int timeout_loop = 0;
 
-	while (exynos_dp_get_plug_in_status(dp) != 0) {
+	while (analogix_dp_get_plug_in_status(dp) != 0) {
 		timeout_loop++;
 		if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) {
 			dev_err(dp->dev, "failed to get hpd plug status\n");
@@ -88,7 +74,7 @@ static int exynos_dp_detect_hpd(struct exynos_dp_device *dp)
 	return 0;
 }
 
-static unsigned char exynos_dp_calc_edid_check_sum(unsigned char *edid_data)
+static unsigned char analogix_dp_calc_edid_check_sum(unsigned char *edid_data)
 {
 	int i;
 	unsigned char sum = 0;
@@ -99,7 +85,7 @@ static unsigned char exynos_dp_calc_edid_check_sum(unsigned char *edid_data)
 	return sum;
 }
 
-static int exynos_dp_read_edid(struct exynos_dp_device *dp)
+static int analogix_dp_read_edid(struct analogix_dp_device *dp)
 {
 	unsigned char edid[EDID_BLOCK_LENGTH * 2];
 	unsigned int extend_block = 0;
@@ -114,7 +100,7 @@ static int exynos_dp_read_edid(struct exynos_dp_device *dp)
 	 */
 
 	/* Read Extension Flag, Number of 128-byte EDID extension blocks */
-	retval = exynos_dp_read_byte_from_i2c(dp, I2C_EDID_DEVICE_ADDR,
+	retval = analogix_dp_read_byte_from_i2c(dp, I2C_EDID_DEVICE_ADDR,
 				EDID_EXTENSION_FLAG,
 				&extend_block);
 	if (retval)
@@ -124,7 +110,7 @@ static int exynos_dp_read_edid(struct exynos_dp_device *dp)
 		dev_dbg(dp->dev, "EDID data includes a single extension!\n");
 
 		/* Read EDID data */
-		retval = exynos_dp_read_bytes_from_i2c(dp, I2C_EDID_DEVICE_ADDR,
+		retval = analogix_dp_read_bytes_from_i2c(dp, I2C_EDID_DEVICE_ADDR,
 						EDID_HEADER_PATTERN,
 						EDID_BLOCK_LENGTH,
 						&edid[EDID_HEADER_PATTERN]);
@@ -132,14 +118,14 @@ static int exynos_dp_read_edid(struct exynos_dp_device *dp)
 			dev_err(dp->dev, "EDID Read failed!\n");
 			return -EIO;
 		}
-		sum = exynos_dp_calc_edid_check_sum(edid);
+		sum = analogix_dp_calc_edid_check_sum(edid);
 		if (sum != 0) {
 			dev_err(dp->dev, "EDID bad checksum!\n");
 			return -EIO;
 		}
 
 		/* Read additional EDID data */
-		retval = exynos_dp_read_bytes_from_i2c(dp,
+		retval = analogix_dp_read_bytes_from_i2c(dp,
 				I2C_EDID_DEVICE_ADDR,
 				EDID_BLOCK_LENGTH,
 				EDID_BLOCK_LENGTH,
@@ -148,19 +134,19 @@ static int exynos_dp_read_edid(struct exynos_dp_device *dp)
 			dev_err(dp->dev, "EDID Read failed!\n");
 			return -EIO;
 		}
-		sum = exynos_dp_calc_edid_check_sum(&edid[EDID_BLOCK_LENGTH]);
+		sum = analogix_dp_calc_edid_check_sum(&edid[EDID_BLOCK_LENGTH]);
 		if (sum != 0) {
 			dev_err(dp->dev, "EDID bad checksum!\n");
 			return -EIO;
 		}
 
-		exynos_dp_read_byte_from_dpcd(dp, DP_TEST_REQUEST,
+		analogix_dp_read_byte_from_dpcd(dp, DP_TEST_REQUEST,
 					&test_vector);
 		if (test_vector & DP_TEST_LINK_EDID_READ) {
-			exynos_dp_write_byte_to_dpcd(dp,
+			analogix_dp_write_byte_to_dpcd(dp,
 				DP_TEST_EDID_CHECKSUM,
 				edid[EDID_BLOCK_LENGTH + EDID_CHECKSUM]);
-			exynos_dp_write_byte_to_dpcd(dp,
+			analogix_dp_write_byte_to_dpcd(dp,
 				DP_TEST_RESPONSE,
 				DP_TEST_EDID_CHECKSUM_WRITE);
 		}
@@ -168,7 +154,7 @@ static int exynos_dp_read_edid(struct exynos_dp_device *dp)
 		dev_info(dp->dev, "EDID data does not include any extensions.\n");
 
 		/* Read EDID data */
-		retval = exynos_dp_read_bytes_from_i2c(dp,
+		retval = analogix_dp_read_bytes_from_i2c(dp,
 				I2C_EDID_DEVICE_ADDR,
 				EDID_HEADER_PATTERN,
 				EDID_BLOCK_LENGTH,
@@ -177,20 +163,20 @@ static int exynos_dp_read_edid(struct exynos_dp_device *dp)
 			dev_err(dp->dev, "EDID Read failed!\n");
 			return -EIO;
 		}
-		sum = exynos_dp_calc_edid_check_sum(edid);
+		sum = analogix_dp_calc_edid_check_sum(edid);
 		if (sum != 0) {
 			dev_err(dp->dev, "EDID bad checksum!\n");
 			return -EIO;
 		}
 
-		exynos_dp_read_byte_from_dpcd(dp,
+		analogix_dp_read_byte_from_dpcd(dp,
 			DP_TEST_REQUEST,
 			&test_vector);
 		if (test_vector & DP_TEST_LINK_EDID_READ) {
-			exynos_dp_write_byte_to_dpcd(dp,
+			analogix_dp_write_byte_to_dpcd(dp,
 				DP_TEST_EDID_CHECKSUM,
 				edid[EDID_CHECKSUM]);
-			exynos_dp_write_byte_to_dpcd(dp,
+			analogix_dp_write_byte_to_dpcd(dp,
 				DP_TEST_RESPONSE,
 				DP_TEST_EDID_CHECKSUM_WRITE);
 		}
@@ -200,21 +186,21 @@ static int exynos_dp_read_edid(struct exynos_dp_device *dp)
 	return 0;
 }
 
-static int exynos_dp_handle_edid(struct exynos_dp_device *dp)
+static int analogix_dp_handle_edid(struct analogix_dp_device *dp)
 {
 	u8 buf[12];
 	int i;
 	int retval;
 
 	/* Read DPCD DP_DPCD_REV~RECEIVE_PORT1_CAP_1 */
-	retval = exynos_dp_read_bytes_from_dpcd(dp, DP_DPCD_REV,
+	retval = analogix_dp_read_bytes_from_dpcd(dp, DP_DPCD_REV,
 				12, buf);
 	if (retval)
 		return retval;
 
 	/* Read EDID */
 	for (i = 0; i < 3; i++) {
-		retval = exynos_dp_read_edid(dp);
+		retval = analogix_dp_read_edid(dp);
 		if (!retval)
 			break;
 	}
@@ -222,73 +208,73 @@ static int exynos_dp_handle_edid(struct exynos_dp_device *dp)
 	return retval;
 }
 
-static void exynos_dp_enable_rx_to_enhanced_mode(struct exynos_dp_device *dp,
+static void analogix_dp_enable_rx_to_enhanced_mode(struct analogix_dp_device *dp,
 						bool enable)
 {
 	u8 data;
 
-	exynos_dp_read_byte_from_dpcd(dp, DP_LANE_COUNT_SET, &data);
+	analogix_dp_read_byte_from_dpcd(dp, DP_LANE_COUNT_SET, &data);
 
 	if (enable)
-		exynos_dp_write_byte_to_dpcd(dp, DP_LANE_COUNT_SET,
+		analogix_dp_write_byte_to_dpcd(dp, DP_LANE_COUNT_SET,
 			DP_LANE_COUNT_ENHANCED_FRAME_EN |
 			DPCD_LANE_COUNT_SET(data));
 	else
-		exynos_dp_write_byte_to_dpcd(dp, DP_LANE_COUNT_SET,
+		analogix_dp_write_byte_to_dpcd(dp, DP_LANE_COUNT_SET,
 			DPCD_LANE_COUNT_SET(data));
 }
 
-static int exynos_dp_is_enhanced_mode_available(struct exynos_dp_device *dp)
+static int analogix_dp_is_enhanced_mode_available(struct analogix_dp_device *dp)
 {
 	u8 data;
 	int retval;
 
-	exynos_dp_read_byte_from_dpcd(dp, DP_MAX_LANE_COUNT, &data);
+	analogix_dp_read_byte_from_dpcd(dp, DP_MAX_LANE_COUNT, &data);
 	retval = DPCD_ENHANCED_FRAME_CAP(data);
 
 	return retval;
 }
 
-static void exynos_dp_set_enhanced_mode(struct exynos_dp_device *dp)
+static void analogix_dp_set_enhanced_mode(struct analogix_dp_device *dp)
 {
 	u8 data;
 
-	data = exynos_dp_is_enhanced_mode_available(dp);
-	exynos_dp_enable_rx_to_enhanced_mode(dp, data);
-	exynos_dp_enable_enhanced_mode(dp, data);
+	data = analogix_dp_is_enhanced_mode_available(dp);
+	analogix_dp_enable_rx_to_enhanced_mode(dp, data);
+	analogix_dp_enable_enhanced_mode(dp, data);
 }
 
-static void exynos_dp_training_pattern_dis(struct exynos_dp_device *dp)
+static void analogix_dp_training_pattern_dis(struct analogix_dp_device *dp)
 {
-	exynos_dp_set_training_pattern(dp, DP_NONE);
+	analogix_dp_set_training_pattern(dp, DP_NONE);
 
-	exynos_dp_write_byte_to_dpcd(dp,
+	analogix_dp_write_byte_to_dpcd(dp,
 		DP_TRAINING_PATTERN_SET,
 		DP_TRAINING_PATTERN_DISABLE);
 }
 
-static void exynos_dp_set_lane_lane_pre_emphasis(struct exynos_dp_device *dp,
+static void analogix_dp_set_lane_lane_pre_emphasis(struct analogix_dp_device *dp,
 					int pre_emphasis, int lane)
 {
 	switch (lane) {
 	case 0:
-		exynos_dp_set_lane0_pre_emphasis(dp, pre_emphasis);
+		analogix_dp_set_lane0_pre_emphasis(dp, pre_emphasis);
 		break;
 	case 1:
-		exynos_dp_set_lane1_pre_emphasis(dp, pre_emphasis);
+		analogix_dp_set_lane1_pre_emphasis(dp, pre_emphasis);
 		break;
 
 	case 2:
-		exynos_dp_set_lane2_pre_emphasis(dp, pre_emphasis);
+		analogix_dp_set_lane2_pre_emphasis(dp, pre_emphasis);
 		break;
 
 	case 3:
-		exynos_dp_set_lane3_pre_emphasis(dp, pre_emphasis);
+		analogix_dp_set_lane3_pre_emphasis(dp, pre_emphasis);
 		break;
 	}
 }
 
-static int exynos_dp_link_start(struct exynos_dp_device *dp)
+static int analogix_dp_link_start(struct analogix_dp_device *dp)
 {
 	u8 buf[4];
 	int lane, lane_count, pll_tries, retval;
@@ -302,25 +288,25 @@ static int exynos_dp_link_start(struct exynos_dp_device *dp)
 		dp->link_train.cr_loop[lane] = 0;
 
 	/* Set link rate and count as you want to establish*/
-	exynos_dp_set_link_bandwidth(dp, dp->link_train.link_rate);
-	exynos_dp_set_lane_count(dp, dp->link_train.lane_count);
+	analogix_dp_set_link_bandwidth(dp, dp->link_train.link_rate);
+	analogix_dp_set_lane_count(dp, dp->link_train.lane_count);
 
 	/* Setup RX configuration */
 	buf[0] = dp->link_train.link_rate;
 	buf[1] = dp->link_train.lane_count;
-	retval = exynos_dp_write_bytes_to_dpcd(dp, DP_LINK_BW_SET,
+	retval = analogix_dp_write_bytes_to_dpcd(dp, DP_LINK_BW_SET,
 				2, buf);
 	if (retval)
 		return retval;
 
 	/* Set TX pre-emphasis to minimum */
 	for (lane = 0; lane < lane_count; lane++)
-		exynos_dp_set_lane_lane_pre_emphasis(dp,
+		analogix_dp_set_lane_lane_pre_emphasis(dp,
 			PRE_EMPHASIS_LEVEL_0, lane);
 
 	/* Wait for PLL lock */
 	pll_tries = 0;
-	while (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
+	while (analogix_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
 		if (pll_tries == DP_TIMEOUT_LOOP_COUNT) {
 			dev_err(dp->dev, "Wait for PLL lock timed out\n");
 			return -ETIMEDOUT;
@@ -331,10 +317,10 @@ static int exynos_dp_link_start(struct exynos_dp_device *dp)
 	}
 
 	/* Set training pattern 1 */
-	exynos_dp_set_training_pattern(dp, TRAINING_PTN1);
+	analogix_dp_set_training_pattern(dp, TRAINING_PTN1);
 
 	/* Set RX training pattern */
-	retval = exynos_dp_write_byte_to_dpcd(dp,
+	retval = analogix_dp_write_byte_to_dpcd(dp,
 			DP_TRAINING_PATTERN_SET,
 			DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_1);
 	if (retval)
@@ -344,13 +330,13 @@ static int exynos_dp_link_start(struct exynos_dp_device *dp)
 		buf[lane] = DP_TRAIN_PRE_EMPH_LEVEL_0 |
 			    DP_TRAIN_VOLTAGE_SWING_LEVEL_0;
 
-	retval = exynos_dp_write_bytes_to_dpcd(dp, DP_TRAINING_LANE0_SET,
+	retval = analogix_dp_write_bytes_to_dpcd(dp, DP_TRAINING_LANE0_SET,
 			lane_count, buf);
 
 	return retval;
 }
 
-static unsigned char exynos_dp_get_lane_status(u8 link_status[2], int lane)
+static unsigned char analogix_dp_get_lane_status(u8 link_status[2], int lane)
 {
 	int shift = (lane & 1) * 4;
 	u8 link_value = link_status[lane>>1];
@@ -358,20 +344,20 @@ static unsigned char exynos_dp_get_lane_status(u8 link_status[2], int lane)
 	return (link_value >> shift) & 0xf;
 }
 
-static int exynos_dp_clock_recovery_ok(u8 link_status[2], int lane_count)
+static int analogix_dp_clock_recovery_ok(u8 link_status[2], int lane_count)
 {
 	int lane;
 	u8 lane_status;
 
 	for (lane = 0; lane < lane_count; lane++) {
-		lane_status = exynos_dp_get_lane_status(link_status, lane);
+		lane_status = analogix_dp_get_lane_status(link_status, lane);
 		if ((lane_status & DP_LANE_CR_DONE) == 0)
 			return -EINVAL;
 	}
 	return 0;
 }
 
-static int exynos_dp_channel_eq_ok(u8 link_status[2], u8 link_align,
+static int analogix_dp_channel_eq_ok(u8 link_status[2], u8 link_align,
 				int lane_count)
 {
 	int lane;
@@ -381,7 +367,7 @@ static int exynos_dp_channel_eq_ok(u8 link_status[2], u8 link_align,
 		return -EINVAL;
 
 	for (lane = 0; lane < lane_count; lane++) {
-		lane_status = exynos_dp_get_lane_status(link_status, lane);
+		lane_status = analogix_dp_get_lane_status(link_status, lane);
 		lane_status &= DP_CHANNEL_EQ_BITS;
 		if (lane_status != DP_CHANNEL_EQ_BITS)
 			return -EINVAL;
@@ -390,7 +376,7 @@ static int exynos_dp_channel_eq_ok(u8 link_status[2], u8 link_align,
 	return 0;
 }
 
-static unsigned char exynos_dp_get_adjust_request_voltage(u8 adjust_request[2],
+static unsigned char analogix_dp_get_adjust_request_voltage(u8 adjust_request[2],
 							int lane)
 {
 	int shift = (lane & 1) * 4;
@@ -399,7 +385,7 @@ static unsigned char exynos_dp_get_adjust_request_voltage(u8 adjust_request[2],
 	return (link_value >> shift) & 0x3;
 }
 
-static unsigned char exynos_dp_get_adjust_request_pre_emphasis(
+static unsigned char analogix_dp_get_adjust_request_pre_emphasis(
 					u8 adjust_request[2],
 					int lane)
 {
@@ -409,45 +395,45 @@ static unsigned char exynos_dp_get_adjust_request_pre_emphasis(
 	return ((link_value >> shift) & 0xc) >> 2;
 }
 
-static void exynos_dp_set_lane_link_training(struct exynos_dp_device *dp,
+static void analogix_dp_set_lane_link_training(struct analogix_dp_device *dp,
 					u8 training_lane_set, int lane)
 {
 	switch (lane) {
 	case 0:
-		exynos_dp_set_lane0_link_training(dp, training_lane_set);
+		analogix_dp_set_lane0_link_training(dp, training_lane_set);
 		break;
 	case 1:
-		exynos_dp_set_lane1_link_training(dp, training_lane_set);
+		analogix_dp_set_lane1_link_training(dp, training_lane_set);
 		break;
 
 	case 2:
-		exynos_dp_set_lane2_link_training(dp, training_lane_set);
+		analogix_dp_set_lane2_link_training(dp, training_lane_set);
 		break;
 
 	case 3:
-		exynos_dp_set_lane3_link_training(dp, training_lane_set);
+		analogix_dp_set_lane3_link_training(dp, training_lane_set);
 		break;
 	}
 }
 
-static unsigned int exynos_dp_get_lane_link_training(
-				struct exynos_dp_device *dp,
+static unsigned int analogix_dp_get_lane_link_training(
+				struct analogix_dp_device *dp,
 				int lane)
 {
 	u32 reg;
 
 	switch (lane) {
 	case 0:
-		reg = exynos_dp_get_lane0_link_training(dp);
+		reg = analogix_dp_get_lane0_link_training(dp);
 		break;
 	case 1:
-		reg = exynos_dp_get_lane1_link_training(dp);
+		reg = analogix_dp_get_lane1_link_training(dp);
 		break;
 	case 2:
-		reg = exynos_dp_get_lane2_link_training(dp);
+		reg = analogix_dp_get_lane2_link_training(dp);
 		break;
 	case 3:
-		reg = exynos_dp_get_lane3_link_training(dp);
+		reg = analogix_dp_get_lane3_link_training(dp);
 		break;
 	default:
 		WARN_ON(1);
@@ -457,15 +443,15 @@ static unsigned int exynos_dp_get_lane_link_training(
 	return reg;
 }
 
-static void exynos_dp_reduce_link_rate(struct exynos_dp_device *dp)
+static void analogix_dp_reduce_link_rate(struct analogix_dp_device *dp)
 {
-	exynos_dp_training_pattern_dis(dp);
-	exynos_dp_set_enhanced_mode(dp);
+	analogix_dp_training_pattern_dis(dp);
+	analogix_dp_set_enhanced_mode(dp);
 
 	dp->link_train.lt_state = FAILED;
 }
 
-static void exynos_dp_get_adjust_training_lane(struct exynos_dp_device *dp,
+static void analogix_dp_get_adjust_training_lane(struct analogix_dp_device *dp,
 					u8 adjust_request[2])
 {
 	int lane, lane_count;
@@ -473,9 +459,9 @@ static void exynos_dp_get_adjust_training_lane(struct exynos_dp_device *dp,
 
 	lane_count = dp->link_train.lane_count;
 	for (lane = 0; lane < lane_count; lane++) {
-		voltage_swing = exynos_dp_get_adjust_request_voltage(
+		voltage_swing = analogix_dp_get_adjust_request_voltage(
 						adjust_request, lane);
-		pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis(
+		pre_emphasis = analogix_dp_get_adjust_request_pre_emphasis(
 						adjust_request, lane);
 		training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) |
 				DPCD_PRE_EMPHASIS_SET(pre_emphasis);
@@ -489,7 +475,7 @@ static void exynos_dp_get_adjust_training_lane(struct exynos_dp_device *dp,
 	}
 }
 
-static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
+static int analogix_dp_process_clock_recovery(struct analogix_dp_device *dp)
 {
 	int lane, lane_count, retval;
 	u8 voltage_swing, pre_emphasis, training_lane;
@@ -499,21 +485,21 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
 
 	lane_count = dp->link_train.lane_count;
 
-	retval =  exynos_dp_read_bytes_from_dpcd(dp,
+	retval =  analogix_dp_read_bytes_from_dpcd(dp,
 			DP_LANE0_1_STATUS, 2, link_status);
 	if (retval)
 		return retval;
 
-	retval =  exynos_dp_read_bytes_from_dpcd(dp,
+	retval =  analogix_dp_read_bytes_from_dpcd(dp,
 			DP_ADJUST_REQUEST_LANE0_1, 2, adjust_request);
 	if (retval)
 		return retval;
 
-	if (exynos_dp_clock_recovery_ok(link_status, lane_count) == 0) {
+	if (analogix_dp_clock_recovery_ok(link_status, lane_count) == 0) {
 		/* set training pattern 2 for EQ */
-		exynos_dp_set_training_pattern(dp, TRAINING_PTN2);
+		analogix_dp_set_training_pattern(dp, TRAINING_PTN2);
 
-		retval = exynos_dp_write_byte_to_dpcd(dp,
+		retval = analogix_dp_write_byte_to_dpcd(dp,
 				DP_TRAINING_PATTERN_SET,
 				DP_LINK_SCRAMBLING_DISABLE |
 				DP_TRAINING_PATTERN_2);
@@ -524,11 +510,11 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
 		dp->link_train.lt_state = EQUALIZER_TRAINING;
 	} else {
 		for (lane = 0; lane < lane_count; lane++) {
-			training_lane = exynos_dp_get_lane_link_training(
+			training_lane = analogix_dp_get_lane_link_training(
 							dp, lane);
-			voltage_swing = exynos_dp_get_adjust_request_voltage(
+			voltage_swing = analogix_dp_get_adjust_request_voltage(
 							adjust_request, lane);
-			pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis(
+			pre_emphasis = analogix_dp_get_adjust_request_pre_emphasis(
 							adjust_request, lane);
 
 			if (DPCD_VOLTAGE_SWING_GET(training_lane) ==
@@ -543,19 +529,19 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
 				dev_err(dp->dev, "CR Max reached (%d,%d,%d)\n",
 					dp->link_train.cr_loop[lane],
 					voltage_swing, pre_emphasis);
-				exynos_dp_reduce_link_rate(dp);
+				analogix_dp_reduce_link_rate(dp);
 				return -EIO;
 			}
 		}
 	}
 
-	exynos_dp_get_adjust_training_lane(dp, adjust_request);
+	analogix_dp_get_adjust_training_lane(dp, adjust_request);
 
 	for (lane = 0; lane < lane_count; lane++)
-		exynos_dp_set_lane_link_training(dp,
+		analogix_dp_set_lane_link_training(dp,
 			dp->link_train.training_lane[lane], lane);
 
-	retval = exynos_dp_write_bytes_to_dpcd(dp,
+	retval = analogix_dp_write_bytes_to_dpcd(dp,
 			DP_TRAINING_LANE0_SET, lane_count,
 			dp->link_train.training_lane);
 	if (retval)
@@ -564,7 +550,7 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
 	return retval;
 }
 
-static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp)
+static int analogix_dp_process_equalizer_training(struct analogix_dp_device *dp)
 {
 	int lane, lane_count, retval;
 	u32 reg;
@@ -574,46 +560,46 @@ static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp)
 
 	lane_count = dp->link_train.lane_count;
 
-	retval = exynos_dp_read_bytes_from_dpcd(dp,
+	retval = analogix_dp_read_bytes_from_dpcd(dp,
 			DP_LANE0_1_STATUS, 2, link_status);
 	if (retval)
 		return retval;
 
-	if (exynos_dp_clock_recovery_ok(link_status, lane_count)) {
-		exynos_dp_reduce_link_rate(dp);
+	if (analogix_dp_clock_recovery_ok(link_status, lane_count)) {
+		analogix_dp_reduce_link_rate(dp);
 		return -EIO;
 	}
 
-	retval = exynos_dp_read_bytes_from_dpcd(dp,
+	retval = analogix_dp_read_bytes_from_dpcd(dp,
 			DP_ADJUST_REQUEST_LANE0_1, 2, adjust_request);
 	if (retval)
 		return retval;
 
-	retval = exynos_dp_read_byte_from_dpcd(dp,
+	retval = analogix_dp_read_byte_from_dpcd(dp,
 			DP_LANE_ALIGN_STATUS_UPDATED, &link_align);
 	if (retval)
 		return retval;
 
-	exynos_dp_get_adjust_training_lane(dp, adjust_request);
+	analogix_dp_get_adjust_training_lane(dp, adjust_request);
 
-	if (!exynos_dp_channel_eq_ok(link_status, link_align, lane_count)) {
+	if (!analogix_dp_channel_eq_ok(link_status, link_align, lane_count)) {
 		/* traing pattern Set to Normal */
-		exynos_dp_training_pattern_dis(dp);
+		analogix_dp_training_pattern_dis(dp);
 
 		dev_info(dp->dev, "Link Training success!\n");
 
-		exynos_dp_get_link_bandwidth(dp, &reg);
+		analogix_dp_get_link_bandwidth(dp, &reg);
 		dp->link_train.link_rate = reg;
 		dev_dbg(dp->dev, "final bandwidth = %.2x\n",
 			dp->link_train.link_rate);
 
-		exynos_dp_get_lane_count(dp, &reg);
+		analogix_dp_get_lane_count(dp, &reg);
 		dp->link_train.lane_count = reg;
 		dev_dbg(dp->dev, "final lane count = %.2x\n",
 			dp->link_train.lane_count);
 
 		/* set enhanced mode if available */
-		exynos_dp_set_enhanced_mode(dp);
+		analogix_dp_set_enhanced_mode(dp);
 		dp->link_train.lt_state = FINISHED;
 
 		return 0;
@@ -624,21 +610,21 @@ static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp)
 
 	if (dp->link_train.eq_loop > MAX_EQ_LOOP) {
 		dev_err(dp->dev, "EQ Max loop\n");
-		exynos_dp_reduce_link_rate(dp);
+		analogix_dp_reduce_link_rate(dp);
 		return -EIO;
 	}
 
 	for (lane = 0; lane < lane_count; lane++)
-		exynos_dp_set_lane_link_training(dp,
+		analogix_dp_set_lane_link_training(dp,
 			dp->link_train.training_lane[lane], lane);
 
-	retval = exynos_dp_write_bytes_to_dpcd(dp, DP_TRAINING_LANE0_SET,
+	retval = analogix_dp_write_bytes_to_dpcd(dp, DP_TRAINING_LANE0_SET,
 			lane_count, dp->link_train.training_lane);
 
 	return retval;
 }
 
-static void exynos_dp_get_max_rx_bandwidth(struct exynos_dp_device *dp,
+static void analogix_dp_get_max_rx_bandwidth(struct analogix_dp_device *dp,
 					u8 *bandwidth)
 {
 	u8 data;
@@ -647,11 +633,11 @@ static void exynos_dp_get_max_rx_bandwidth(struct exynos_dp_device *dp,
 	 * For DP rev.1.1, Maximum link rate of Main Link lanes
 	 * 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps
 	 */
-	exynos_dp_read_byte_from_dpcd(dp, DP_MAX_LINK_RATE, &data);
+	analogix_dp_read_byte_from_dpcd(dp, DP_MAX_LINK_RATE, &data);
 	*bandwidth = data;
 }
 
-static void exynos_dp_get_max_rx_lane_count(struct exynos_dp_device *dp,
+static void analogix_dp_get_max_rx_lane_count(struct analogix_dp_device *dp,
 					u8 *lane_count)
 {
 	u8 data;
@@ -660,11 +646,11 @@ static void exynos_dp_get_max_rx_lane_count(struct exynos_dp_device *dp,
 	 * For DP rev.1.1, Maximum number of Main Link lanes
 	 * 0x01 = 1 lane, 0x02 = 2 lanes, 0x04 = 4 lanes
 	 */
-	exynos_dp_read_byte_from_dpcd(dp, DP_MAX_LANE_COUNT, &data);
+	analogix_dp_read_byte_from_dpcd(dp, DP_MAX_LANE_COUNT, &data);
 	*lane_count = DPCD_MAX_LANE_COUNT(data);
 }
 
-static void exynos_dp_init_training(struct exynos_dp_device *dp,
+static void analogix_dp_init_training(struct analogix_dp_device *dp,
 			enum link_lane_count_type max_lane,
 			enum link_rate_type max_rate)
 {
@@ -672,11 +658,11 @@ static void exynos_dp_init_training(struct exynos_dp_device *dp,
 	 * MACRO_RST must be applied after the PLL_LOCK to avoid
 	 * the DP inter pair skew issue for at least 10 us
 	 */
-	exynos_dp_reset_macro(dp);
+	analogix_dp_reset_macro(dp);
 
 	/* Initialize by reading RX's DPCD */
-	exynos_dp_get_max_rx_bandwidth(dp, &dp->link_train.link_rate);
-	exynos_dp_get_max_rx_lane_count(dp, &dp->link_train.lane_count);
+	analogix_dp_get_max_rx_bandwidth(dp, &dp->link_train.link_rate);
+	analogix_dp_get_max_rx_lane_count(dp, &dp->link_train.lane_count);
 
 	if ((dp->link_train.link_rate != LINK_RATE_1_62GBPS) &&
 	   (dp->link_train.link_rate != LINK_RATE_2_70GBPS)) {
@@ -698,10 +684,10 @@ static void exynos_dp_init_training(struct exynos_dp_device *dp,
 		dp->link_train.link_rate = max_rate;
 
 	/* All DP analog module power up */
-	exynos_dp_set_analog_power_down(dp, POWER_ALL, 0);
+	analogix_dp_set_analog_power_down(dp, POWER_ALL, 0);
 }
 
-static int exynos_dp_sw_link_training(struct exynos_dp_device *dp)
+static int analogix_dp_sw_link_training(struct analogix_dp_device *dp)
 {
 	int retval = 0, training_finished = 0;
 
@@ -711,17 +697,17 @@ static int exynos_dp_sw_link_training(struct exynos_dp_device *dp)
 	while (!retval && !training_finished) {
 		switch (dp->link_train.lt_state) {
 		case START:
-			retval = exynos_dp_link_start(dp);
+			retval = analogix_dp_link_start(dp);
 			if (retval)
 				dev_err(dp->dev, "LT link start failed!\n");
 			break;
 		case CLOCK_RECOVERY:
-			retval = exynos_dp_process_clock_recovery(dp);
+			retval = analogix_dp_process_clock_recovery(dp);
 			if (retval)
 				dev_err(dp->dev, "LT CR failed!\n");
 			break;
 		case EQUALIZER_TRAINING:
-			retval = exynos_dp_process_equalizer_training(dp);
+			retval = analogix_dp_process_equalizer_training(dp);
 			if (retval)
 				dev_err(dp->dev, "LT EQ failed!\n");
 			break;
@@ -738,7 +724,7 @@ static int exynos_dp_sw_link_training(struct exynos_dp_device *dp)
 	return retval;
 }
 
-static int exynos_dp_set_link_train(struct exynos_dp_device *dp,
+static int analogix_dp_set_link_train(struct analogix_dp_device *dp,
 				u32 count,
 				u32 bwtype)
 {
@@ -746,8 +732,8 @@ static int exynos_dp_set_link_train(struct exynos_dp_device *dp,
 	int retval;
 
 	for (i = 0; i < DP_TIMEOUT_LOOP_COUNT; i++) {
-		exynos_dp_init_training(dp, count, bwtype);
-		retval = exynos_dp_sw_link_training(dp);
+		analogix_dp_init_training(dp, count, bwtype);
+		retval = analogix_dp_sw_link_training(dp);
 		if (retval == 0)
 			break;
 
@@ -757,24 +743,24 @@ static int exynos_dp_set_link_train(struct exynos_dp_device *dp,
 	return retval;
 }
 
-static int exynos_dp_config_video(struct exynos_dp_device *dp)
+static int analogix_dp_config_video(struct analogix_dp_device *dp)
 {
 	int retval = 0;
 	int timeout_loop = 0;
 	int done_count = 0;
 
-	exynos_dp_config_video_slave_mode(dp);
+	analogix_dp_config_video_slave_mode(dp);
 
-	exynos_dp_set_video_color_format(dp);
+	analogix_dp_set_video_color_format(dp);
 
-	if (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
+	if (analogix_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
 		dev_err(dp->dev, "PLL is not locked yet.\n");
 		return -EINVAL;
 	}
 
 	for (;;) {
 		timeout_loop++;
-		if (exynos_dp_is_slave_video_stream_clock_on(dp) == 0)
+		if (analogix_dp_is_slave_video_stream_clock_on(dp) == 0)
 			break;
 		if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) {
 			dev_err(dp->dev, "Timeout of video streamclk ok\n");
@@ -785,22 +771,22 @@ static int exynos_dp_config_video(struct exynos_dp_device *dp)
 	}
 
 	/* Set to use the register calculated M/N video */
-	exynos_dp_set_video_cr_mn(dp, CALCULATED_M, 0, 0);
+	analogix_dp_set_video_cr_mn(dp, CALCULATED_M, 0, 0);
 
 	/* For video bist, Video timing must be generated by register */
-	exynos_dp_set_video_timing_mode(dp, VIDEO_TIMING_FROM_CAPTURE);
+	analogix_dp_set_video_timing_mode(dp, VIDEO_TIMING_FROM_CAPTURE);
 
 	/* Disable video mute */
-	exynos_dp_enable_video_mute(dp, 0);
+	analogix_dp_enable_video_mute(dp, 0);
 
 	/* Configure video slave mode */
-	exynos_dp_enable_video_master(dp, 0);
+	analogix_dp_enable_video_master(dp, 0);
 
 	timeout_loop = 0;
 
 	for (;;) {
 		timeout_loop++;
-		if (exynos_dp_is_video_stream_on(dp) == 0) {
+		if (analogix_dp_is_video_stream_on(dp) == 0) {
 			done_count++;
 			if (done_count > 10)
 				break;
@@ -821,47 +807,47 @@ static int exynos_dp_config_video(struct exynos_dp_device *dp)
 	return retval;
 }
 
-static void exynos_dp_enable_scramble(struct exynos_dp_device *dp, bool enable)
+static void analogix_dp_enable_scramble(struct analogix_dp_device *dp, bool enable)
 {
 	u8 data;
 
 	if (enable) {
-		exynos_dp_enable_scrambling(dp);
+		analogix_dp_enable_scrambling(dp);
 
-		exynos_dp_read_byte_from_dpcd(dp,
+		analogix_dp_read_byte_from_dpcd(dp,
 			DP_TRAINING_PATTERN_SET,
 			&data);
-		exynos_dp_write_byte_to_dpcd(dp,
+		analogix_dp_write_byte_to_dpcd(dp,
 			DP_TRAINING_PATTERN_SET,
 			(u8)(data & ~DP_LINK_SCRAMBLING_DISABLE));
 	} else {
-		exynos_dp_disable_scrambling(dp);
+		analogix_dp_disable_scrambling(dp);
 
-		exynos_dp_read_byte_from_dpcd(dp,
+		analogix_dp_read_byte_from_dpcd(dp,
 			DP_TRAINING_PATTERN_SET,
 			&data);
-		exynos_dp_write_byte_to_dpcd(dp,
+		analogix_dp_write_byte_to_dpcd(dp,
 			DP_TRAINING_PATTERN_SET,
 			(u8)(data | DP_LINK_SCRAMBLING_DISABLE));
 	}
 }
 
-static irqreturn_t exynos_dp_irq_handler(int irq, void *arg)
+static irqreturn_t analogix_dp_irq_handler(int irq, void *arg)
 {
-	struct exynos_dp_device *dp = arg;
+	struct analogix_dp_device *dp = arg;
 
 	enum dp_irq_type irq_type;
 
-	irq_type = exynos_dp_get_irq_type(dp);
+	irq_type = analogix_dp_get_irq_type(dp);
 	switch (irq_type) {
 	case DP_IRQ_TYPE_HP_CABLE_IN:
 		dev_dbg(dp->dev, "Received irq - cable in\n");
 		schedule_work(&dp->hotplug_work);
-		exynos_dp_clear_hotplug_interrupts(dp);
+		analogix_dp_clear_hotplug_interrupts(dp);
 		break;
 	case DP_IRQ_TYPE_HP_CABLE_OUT:
 		dev_dbg(dp->dev, "Received irq - cable out\n");
-		exynos_dp_clear_hotplug_interrupts(dp);
+		analogix_dp_clear_hotplug_interrupts(dp);
 		break;
 	case DP_IRQ_TYPE_HP_CHANGE:
 		/*
@@ -870,7 +856,7 @@ static irqreturn_t exynos_dp_irq_handler(int irq, void *arg)
 		 * only handle cable changes.
 		 */
 		dev_dbg(dp->dev, "Received irq - hotplug change; ignoring.\n");
-		exynos_dp_clear_hotplug_interrupts(dp);
+		analogix_dp_clear_hotplug_interrupts(dp);
 		break;
 	default:
 		dev_err(dp->dev, "Received irq - unknown type!\n");
@@ -879,218 +865,203 @@ static irqreturn_t exynos_dp_irq_handler(int irq, void *arg)
 	return IRQ_HANDLED;
 }
 
-static void exynos_dp_hotplug(struct work_struct *work)
+static void analogix_dp_hotplug(struct work_struct *work)
 {
-	struct exynos_dp_device *dp;
+	struct analogix_dp_device *dp;
 
-	dp = container_of(work, struct exynos_dp_device, hotplug_work);
+	dp = container_of(work, struct analogix_dp_device, hotplug_work);
 
 	if (dp->drm_dev)
 		drm_helper_hpd_irq_event(dp->drm_dev);
 }
 
-static void exynos_dp_commit(struct drm_encoder *encoder)
+static void analogix_dp_commit(struct analogix_dp_device *dp)
 {
-	struct exynos_dp_device *dp = encoder_to_dp(encoder);
 	int ret;
 
 	/* Keep the panel disabled while we configure video */
-	if (dp->panel) {
-		if (drm_panel_disable(dp->panel))
+	if (dp->plat_data->panel) {
+		if (drm_panel_disable(dp->plat_data->panel))
 			DRM_ERROR("failed to disable the panel\n");
 	}
 
-	ret = exynos_dp_detect_hpd(dp);
+	ret = analogix_dp_detect_hpd(dp);
 	if (ret) {
 		/* Cable has been disconnected, we're done */
 		return;
 	}
 
-	ret = exynos_dp_handle_edid(dp);
+	ret = analogix_dp_handle_edid(dp);
 	if (ret) {
 		dev_err(dp->dev, "unable to handle edid\n");
 		return;
 	}
 
-	ret = exynos_dp_set_link_train(dp, dp->video_info->lane_count,
+	ret = analogix_dp_set_link_train(dp, dp->video_info->lane_count,
 					dp->video_info->link_rate);
 	if (ret) {
 		dev_err(dp->dev, "unable to do link train\n");
 		return;
 	}
 
-	exynos_dp_enable_scramble(dp, 1);
-	exynos_dp_enable_rx_to_enhanced_mode(dp, 1);
-	exynos_dp_enable_enhanced_mode(dp, 1);
+	analogix_dp_enable_scramble(dp, 1);
+	analogix_dp_enable_rx_to_enhanced_mode(dp, 1);
+	analogix_dp_enable_enhanced_mode(dp, 1);
 
-	exynos_dp_set_lane_count(dp, dp->video_info->lane_count);
-	exynos_dp_set_link_bandwidth(dp, dp->video_info->link_rate);
+	analogix_dp_set_lane_count(dp, dp->video_info->lane_count);
+	analogix_dp_set_link_bandwidth(dp, dp->video_info->link_rate);
 
-	exynos_dp_init_video(dp);
-	ret = exynos_dp_config_video(dp);
+	analogix_dp_init_video(dp);
+	ret = analogix_dp_config_video(dp);
 	if (ret)
 		dev_err(dp->dev, "unable to config video\n");
 
 	/* Safe to enable the panel now */
-	if (dp->panel) {
-		if (drm_panel_enable(dp->panel))
+	if (dp->plat_data->panel) {
+		if (drm_panel_enable(dp->plat_data->panel))
 			DRM_ERROR("failed to enable the panel\n");
 	}
 
 	/* Enable video */
-	exynos_dp_start_video(dp);
+	analogix_dp_start_video(dp);
 }
 
-static enum drm_connector_status exynos_dp_detect(
-				struct drm_connector *connector, bool force)
+int analogix_dp_get_modes(struct drm_connector *connector)
 {
-	return connector_status_connected;
-}
+	struct analogix_dp_device *dp = to_dp(connector);
+	int num_modes = 0;
 
-static void exynos_dp_connector_destroy(struct drm_connector *connector)
-{
-	drm_connector_unregister(connector);
-	drm_connector_cleanup(connector);
-}
+	if (dp->plat_data->panel)
+		num_modes += drm_panel_get_modes(dp->plat_data->panel);
 
-static const struct drm_connector_funcs exynos_dp_connector_funcs = {
-	.dpms = drm_atomic_helper_connector_dpms,
-	.fill_modes = drm_helper_probe_single_connector_modes,
-	.detect = exynos_dp_detect,
-	.destroy = exynos_dp_connector_destroy,
-	.reset = drm_atomic_helper_connector_reset,
-	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
-	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
-};
-
-static int exynos_dp_get_modes(struct drm_connector *connector)
-{
-	struct exynos_dp_device *dp = ctx_from_connector(connector);
-	struct drm_display_mode *mode;
-
-	if (dp->panel)
-		return drm_panel_get_modes(dp->panel);
-
-	mode = drm_mode_create(connector->dev);
-	if (!mode) {
-		DRM_ERROR("failed to create a new display mode.\n");
-		return 0;
-	}
-
-	drm_display_mode_from_videomode(&dp->priv.vm, mode);
-	mode->width_mm = dp->priv.width_mm;
-	mode->height_mm = dp->priv.height_mm;
-	connector->display_info.width_mm = mode->width_mm;
-	connector->display_info.height_mm = mode->height_mm;
-
-	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
-	drm_mode_set_name(mode);
-	drm_mode_probed_add(connector, mode);
+	if (dp->plat_data->get_modes)
+		num_modes += dp->plat_data->get_modes(dp->plat_data);
 
-	return 1;
+	return num_modes;
 }
 
-static struct drm_encoder *exynos_dp_best_encoder(
-			struct drm_connector *connector)
+static struct drm_encoder *
+analogix_dp_best_encoder(struct drm_connector *connector)
 {
-	struct exynos_dp_device *dp = ctx_from_connector(connector);
+	struct analogix_dp_device *dp = to_dp(connector);
 
-	return &dp->encoder;
+	return dp->encoder;
 }
 
-static const struct drm_connector_helper_funcs exynos_dp_connector_helper_funcs = {
-	.get_modes = exynos_dp_get_modes,
-	.best_encoder = exynos_dp_best_encoder,
+static const struct drm_connector_helper_funcs analogix_dp_connector_helper_funcs = {
+	.get_modes = analogix_dp_get_modes,
+	.best_encoder = analogix_dp_best_encoder,
 };
 
-/* returns the number of bridges attached */
-static int exynos_drm_attach_lcd_bridge(struct exynos_dp_device *dp,
-		struct drm_encoder *encoder)
+enum drm_connector_status
+analogix_dp_detect(struct drm_connector *connector, bool force)
 {
-	int ret;
-
-	encoder->bridge->next = dp->ptn_bridge;
-	dp->ptn_bridge->encoder = encoder;
-	ret = drm_bridge_attach(encoder->dev, dp->ptn_bridge);
-	if (ret) {
-		DRM_ERROR("Failed to attach bridge to drm\n");
-		return ret;
-	}
+	return connector_status_connected;
+}
 
-	return 0;
+static void analogix_dp_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
 }
 
-static int exynos_dp_bridge_attach(struct drm_bridge *bridge)
+static const struct drm_connector_funcs analogix_dp_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.detect = analogix_dp_detect,
+	.destroy = analogix_dp_connector_destroy,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int analogix_dp_bridge_attach(struct drm_bridge *bridge)
 {
-	struct exynos_dp_device *dp = bridge->driver_private;
-	struct drm_encoder *encoder = &dp->encoder;
+	struct analogix_dp_device *dp = bridge->driver_private;
+	struct drm_encoder *encoder = dp->encoder;
 	struct drm_connector *connector = &dp->connector;
 	int ret;
 
-	/* Pre-empt DP connector creation if there's a bridge */
-	if (dp->ptn_bridge) {
-		ret = exynos_drm_attach_lcd_bridge(dp, encoder);
-		if (!ret)
-			return 0;
+	if (!bridge->encoder) {
+		DRM_ERROR("Parent encoder object not found");
+		return -ENODEV;
 	}
 
 	connector->polled = DRM_CONNECTOR_POLL_HPD;
 
 	ret = drm_connector_init(dp->drm_dev, connector,
-			&exynos_dp_connector_funcs, DRM_MODE_CONNECTOR_eDP);
+				 &analogix_dp_connector_funcs,
+				 DRM_MODE_CONNECTOR_eDP);
 	if (ret) {
 		DRM_ERROR("Failed to initialize connector with drm\n");
 		return ret;
 	}
 
-	drm_connector_helper_add(connector, &exynos_dp_connector_helper_funcs);
-	drm_connector_register(connector);
+	drm_connector_helper_add(connector,
+				 &analogix_dp_connector_helper_funcs);
 	drm_mode_connector_attach_encoder(connector, encoder);
 
-	if (dp->panel)
-		ret = drm_panel_attach(dp->panel, &dp->connector);
+	/*
+	 * NOTE: the connector registration is implemented in analogix
+	 * platform driver, that to say connector would be exist after
+	 * plat_data->attch return, that's why we record the connector
+	 * point after plat attached.
+	 */
+	 if (dp->plat_data->attach) {
+		 ret = dp->plat_data->attach(dp->plat_data, bridge, connector);
+		 if (ret) {
+			 DRM_ERROR("Failed at platform attch func\n");
+			 return ret;
+		 }
+	}
 
-	return ret;
+	if (dp->plat_data->panel) {
+		ret = drm_panel_attach(dp->plat_data->panel, &dp->connector);
+		if (ret) {
+			DRM_ERROR("Failed to attach panel\n");
+			return ret;
+		}
+	}
+
+	return 0;
 }
 
-static void exynos_dp_bridge_enable(struct drm_bridge *bridge)
+static void analogix_dp_bridge_enable(struct drm_bridge *bridge)
 {
-	struct exynos_dp_device *dp = bridge->driver_private;
-	struct exynos_drm_crtc *crtc = dp_to_crtc(dp);
+	struct analogix_dp_device *dp = bridge->driver_private;
 
 	if (dp->dpms_mode == DRM_MODE_DPMS_ON)
 		return;
 
 	pm_runtime_get_sync(dp->dev);
 
-	if (dp->panel) {
-		if (drm_panel_prepare(dp->panel)) {
+	if (dp->plat_data->panel) {
+		if (drm_panel_prepare(dp->plat_data->panel)) {
 			DRM_ERROR("failed to setup the panel\n");
 			return;
 		}
 	}
 
-	if (crtc->ops->clock_enable)
-		crtc->ops->clock_enable(dp_to_crtc(dp), true);
+	if (dp->plat_data->power_on)
+		dp->plat_data->power_on(dp->plat_data);
 
 	phy_power_on(dp->phy);
-	exynos_dp_init_dp(dp);
+	analogix_dp_init_dp(dp);
 	enable_irq(dp->irq);
-	exynos_dp_commit(&dp->encoder);
+	analogix_dp_commit(dp);
 
 	dp->dpms_mode = DRM_MODE_DPMS_ON;
 }
 
-static void exynos_dp_bridge_disable(struct drm_bridge *bridge)
+static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
 {
-	struct exynos_dp_device *dp = bridge->driver_private;
-	struct exynos_drm_crtc *crtc = dp_to_crtc(dp);
+	struct analogix_dp_device *dp = bridge->driver_private;
 
 	if (dp->dpms_mode != DRM_MODE_DPMS_ON)
 		return;
 
-	if (dp->panel) {
-		if (drm_panel_disable(dp->panel)) {
+	if (dp->plat_data->panel) {
+		if (drm_panel_disable(dp->plat_data->panel)) {
 			DRM_ERROR("failed to disable the panel\n");
 			return;
 		}
@@ -1100,11 +1071,11 @@ static void exynos_dp_bridge_disable(struct drm_bridge *bridge)
 	flush_work(&dp->hotplug_work);
 	phy_power_off(dp->phy);
 
-	if (crtc->ops->clock_enable)
-		crtc->ops->clock_enable(dp_to_crtc(dp), false);
+	if (dp->plat_data->power_off)
+		dp->plat_data->power_off(dp->plat_data);
 
-	if (dp->panel) {
-		if (drm_panel_unprepare(dp->panel))
+	if (dp->plat_data->panel) {
+		if (drm_panel_unprepare(dp->plat_data->panel))
 			DRM_ERROR("failed to turnoff the panel\n");
 	}
 
@@ -1113,23 +1084,22 @@ static void exynos_dp_bridge_disable(struct drm_bridge *bridge)
 	dp->dpms_mode = DRM_MODE_DPMS_OFF;
 }
 
-static void exynos_dp_bridge_nop(struct drm_bridge *bridge)
+static void analogix_dp_bridge_nop(struct drm_bridge *bridge)
 {
 	/* do nothing */
 }
 
-static const struct drm_bridge_funcs exynos_dp_bridge_funcs = {
-	.enable = exynos_dp_bridge_enable,
-	.disable = exynos_dp_bridge_disable,
-	.pre_enable = exynos_dp_bridge_nop,
-	.post_disable = exynos_dp_bridge_nop,
-	.attach = exynos_dp_bridge_attach,
+static const struct drm_bridge_funcs analogix_dp_bridge_funcs = {
+	.enable = analogix_dp_bridge_enable,
+	.disable = analogix_dp_bridge_disable,
+	.pre_enable = analogix_dp_bridge_nop,
+	.post_disable = analogix_dp_bridge_nop,
+	.attach = analogix_dp_bridge_attach,
 };
 
-static int exynos_dp_create_connector(struct drm_encoder *encoder)
+static int analogix_dp_create_bridge(struct drm_device *drm_dev,
+				     struct analogix_dp_device *dp)
 {
-	struct exynos_dp_device *dp = encoder_to_dp(encoder);
-	struct drm_device *drm_dev = dp->drm_dev;
 	struct drm_bridge *bridge;
 	int ret;
 
@@ -1141,10 +1111,10 @@ static int exynos_dp_create_connector(struct drm_encoder *encoder)
 
 	dp->bridge = bridge;
 
-	encoder->bridge = bridge;
+	dp->encoder->bridge = bridge;
 	bridge->driver_private = dp;
-	bridge->encoder = encoder;
-	bridge->funcs = &exynos_dp_bridge_funcs;
+	bridge->encoder = dp->encoder;
+	bridge->funcs = &analogix_dp_bridge_funcs;
 
 	ret = drm_bridge_attach(drm_dev, bridge);
 	if (ret) {
@@ -1155,39 +1125,7 @@ static int exynos_dp_create_connector(struct drm_encoder *encoder)
 	return 0;
 }
 
-static bool exynos_dp_mode_fixup(struct drm_encoder *encoder,
-				 const struct drm_display_mode *mode,
-				 struct drm_display_mode *adjusted_mode)
-{
-	return true;
-}
-
-static void exynos_dp_mode_set(struct drm_encoder *encoder,
-			       struct drm_display_mode *mode,
-			       struct drm_display_mode *adjusted_mode)
-{
-}
-
-static void exynos_dp_enable(struct drm_encoder *encoder)
-{
-}
-
-static void exynos_dp_disable(struct drm_encoder *encoder)
-{
-}
-
-static const struct drm_encoder_helper_funcs exynos_dp_encoder_helper_funcs = {
-	.mode_fixup = exynos_dp_mode_fixup,
-	.mode_set = exynos_dp_mode_set,
-	.enable = exynos_dp_enable,
-	.disable = exynos_dp_disable,
-};
-
-static const struct drm_encoder_funcs exynos_dp_encoder_funcs = {
-	.destroy = drm_encoder_cleanup,
-};
-
-static struct video_info *exynos_dp_dt_parse_pdata(struct device *dev)
+static struct video_info *analogix_dp_dt_parse_pdata(struct device *dev)
 {
 	struct device_node *dp_node = dev->of_node;
 	struct video_info *dp_video_config;
@@ -1245,33 +1183,37 @@ static struct video_info *exynos_dp_dt_parse_pdata(struct device *dev)
 	return dp_video_config;
 }
 
-static int exynos_dp_dt_parse_panel(struct exynos_dp_device *dp)
+int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev,
+		     struct analogix_dp_plat_data *plat_data)
 {
+	struct platform_device *pdev = to_platform_device(dev);
+	struct analogix_dp_device *dp;
+	struct resource *res;
+	unsigned int irq_flags;
 	int ret;
 
-	ret = of_get_videomode(dp->dev->of_node, &dp->priv.vm,
-			OF_USE_NATIVE_MODE);
-	if (ret) {
-		DRM_ERROR("failed: of_get_videomode() : %d\n", ret);
-		return ret;
+	if (!plat_data) {
+		dev_err(dev, "Invalided input plat_data\n");
+		return -EINVAL;
 	}
-	return 0;
-}
 
-static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
-{
-	struct exynos_dp_device *dp = dev_get_drvdata(dev);
-	struct platform_device *pdev = to_platform_device(dev);
-	struct drm_device *drm_dev = data;
-	struct drm_encoder *encoder = &dp->encoder;
-	struct resource *res;
-	unsigned int irq_flags;
-	int pipe, ret = 0;
+	dp = devm_kzalloc(dev, sizeof(struct analogix_dp_device), GFP_KERNEL);
+	if (!dp)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, dp);
 
 	dp->dev = &pdev->dev;
 	dp->dpms_mode = DRM_MODE_DPMS_OFF;
 
-	dp->video_info = exynos_dp_dt_parse_pdata(&pdev->dev);
+	/*
+	 * platform dp driver need containor_of the plat_data to get
+	 * the driver private data, so we need to store the point of
+	 * plat_data, not the context of plat_data.
+	 */
+	dp->plat_data = plat_data;
+
+	dp->video_info = analogix_dp_dt_parse_pdata(&pdev->dev);
 	if (IS_ERR(dp->video_info))
 		return PTR_ERR(dp->video_info);
 
@@ -1291,12 +1233,6 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
 		}
 	}
 
-	if (!dp->panel && !dp->ptn_bridge) {
-		ret = exynos_dp_dt_parse_panel(dp);
-		if (ret)
-			return ret;
-	}
-
 	dp->clock = devm_clk_get(&pdev->dev, "dp");
 	if (IS_ERR(dp->clock)) {
 		dev_err(&pdev->dev, "failed to get clock\n");
@@ -1311,7 +1247,10 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
 	if (IS_ERR(dp->reg_base))
 		return PTR_ERR(dp->reg_base);
 
-	dp->hpd_gpio = of_get_named_gpio(dev->of_node, "samsung,hpd-gpio", 0);
+	dp->hpd_gpio = of_get_named_gpio(dev->of_node, "hpd-gpios", 0);
+	if (!gpio_is_valid(dp->hpd_gpio))
+		dp->hpd_gpio = of_get_named_gpio(dev->of_node,
+						 "samsung,hpd-gpio", 0);
 
 	if (gpio_is_valid(dp->hpd_gpio)) {
 		/*
@@ -1340,143 +1279,60 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
 		return -ENODEV;
 	}
 
-	INIT_WORK(&dp->hotplug_work, exynos_dp_hotplug);
+	INIT_WORK(&dp->hotplug_work, analogix_dp_hotplug);
 
-	ret = devm_request_irq(&pdev->dev, dp->irq, exynos_dp_irq_handler,
-			irq_flags, "exynos-dp", dp);
+	pm_runtime_enable(dev);
+
+	ret = devm_request_irq(&pdev->dev, dp->irq, analogix_dp_irq_handler,
+			irq_flags, "analogix-dp", dp);
 	if (ret) {
 		dev_err(&pdev->dev, "failed to request irq\n");
-		return ret;
+		goto err_disable_pm_runtime;
 	}
 	disable_irq(dp->irq);
 
 	dp->drm_dev = drm_dev;
+	dp->encoder = dp->plat_data->encoder;
 
-	pipe = exynos_drm_crtc_get_pipe_from_type(drm_dev,
-						  EXYNOS_DISPLAY_TYPE_LCD);
-	if (pipe < 0)
-		return pipe;
-
-	encoder->possible_crtcs = 1 << pipe;
-
-	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
-
-	drm_encoder_init(drm_dev, encoder, &exynos_dp_encoder_funcs,
-			 DRM_MODE_ENCODER_TMDS, NULL);
-
-	drm_encoder_helper_add(encoder, &exynos_dp_encoder_helper_funcs);
-
-	ret = exynos_dp_create_connector(encoder);
+	ret = analogix_dp_create_bridge(drm_dev, dp);
 	if (ret) {
-		DRM_ERROR("failed to create connector ret = %d\n", ret);
-		drm_encoder_cleanup(encoder);
-		return ret;
+		DRM_ERROR("failed to create bridge (%d)\n", ret);
+		drm_encoder_cleanup(dp->encoder);
+		goto err_disable_pm_runtime;
 	}
 
 	return 0;
-}
-
-static void exynos_dp_unbind(struct device *dev, struct device *master,
-				void *data)
-{
-	struct exynos_dp_device *dp = dev_get_drvdata(dev);
-
-	exynos_dp_disable(&dp->encoder);
-}
-
-static const struct component_ops exynos_dp_ops = {
-	.bind	= exynos_dp_bind,
-	.unbind	= exynos_dp_unbind,
-};
-
-static int exynos_dp_probe(struct platform_device *pdev)
-{
-	struct device *dev = &pdev->dev;
-	struct device_node *panel_node = NULL, *bridge_node, *endpoint = NULL;
-	struct exynos_dp_device *dp;
-	int ret;
-
-	dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device),
-				GFP_KERNEL);
-	if (!dp)
-		return -ENOMEM;
-
-	platform_set_drvdata(pdev, dp);
-
-	/* This is for the backward compatibility. */
-	panel_node = of_parse_phandle(dev->of_node, "panel", 0);
-	if (panel_node) {
-		dp->panel = of_drm_find_panel(panel_node);
-		of_node_put(panel_node);
-		if (!dp->panel)
-			return -EPROBE_DEFER;
-	} else {
-		endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
-		if (endpoint) {
-			panel_node = of_graph_get_remote_port_parent(endpoint);
-			if (panel_node) {
-				dp->panel = of_drm_find_panel(panel_node);
-				of_node_put(panel_node);
-				if (!dp->panel)
-					return -EPROBE_DEFER;
-			} else {
-				DRM_ERROR("no port node for panel device.\n");
-				return -EINVAL;
-			}
-		}
-	}
-
-	if (endpoint)
-		goto out;
-
-	endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
-	if (endpoint) {
-		bridge_node = of_graph_get_remote_port_parent(endpoint);
-		if (bridge_node) {
-			dp->ptn_bridge = of_drm_find_bridge(bridge_node);
-			of_node_put(bridge_node);
-			if (!dp->ptn_bridge)
-				return -EPROBE_DEFER;
-		} else
-			return -EPROBE_DEFER;
-	}
-
-out:
-	pm_runtime_enable(dev);
-
-	ret = component_add(&pdev->dev, &exynos_dp_ops);
-	if (ret)
-		goto err_disable_pm_runtime;
-
-	return ret;
 
 err_disable_pm_runtime:
 	pm_runtime_disable(dev);
 
 	return ret;
 }
+EXPORT_SYMBOL_GPL(analogix_dp_bind);
 
-static int exynos_dp_remove(struct platform_device *pdev)
+void analogix_dp_unbind(struct device *dev, struct device *master,
+			void *data)
 {
-	pm_runtime_disable(&pdev->dev);
-	component_del(&pdev->dev, &exynos_dp_ops);
+	struct analogix_dp_device *dp = dev_get_drvdata(dev);
 
-	return 0;
+	analogix_dp_bridge_disable(dp->bridge);
+	pm_runtime_disable(dev);
 }
+EXPORT_SYMBOL_GPL(analogix_dp_unbind);
 
 #ifdef CONFIG_PM
-static int exynos_dp_suspend(struct device *dev)
+int analogix_dp_suspend(struct device *dev)
 {
-	struct exynos_dp_device *dp = dev_get_drvdata(dev);
+	struct analogix_dp_device *dp = dev_get_drvdata(dev);
 
 	clk_disable_unprepare(dp->clock);
-
 	return 0;
 }
+EXPORT_SYMBOL_GPL(analogix_dp_suspend);
 
-static int exynos_dp_resume(struct device *dev)
+int analogix_dp_resume(struct device *dev)
 {
-	struct exynos_dp_device *dp = dev_get_drvdata(dev);
+	struct analogix_dp_device *dp = dev_get_drvdata(dev);
 	int ret;
 
 	ret = clk_prepare_enable(dp->clock);
@@ -1487,29 +1343,9 @@ static int exynos_dp_resume(struct device *dev)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(analogix_dp_resume);
 #endif
 
-static const struct dev_pm_ops exynos_dp_pm_ops = {
-	SET_RUNTIME_PM_OPS(exynos_dp_suspend, exynos_dp_resume, NULL)
-};
-
-static const struct of_device_id exynos_dp_match[] = {
-	{ .compatible = "samsung,exynos5-dp" },
-	{},
-};
-MODULE_DEVICE_TABLE(of, exynos_dp_match);
-
-struct platform_driver dp_driver = {
-	.probe		= exynos_dp_probe,
-	.remove		= exynos_dp_remove,
-	.driver		= {
-		.name	= "exynos-dp",
-		.owner	= THIS_MODULE,
-		.pm	= &exynos_dp_pm_ops,
-		.of_match_table = exynos_dp_match,
-	},
-};
-
 MODULE_AUTHOR("Jingoo Han <jg1.han at samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC DP Driver");
+MODULE_DESCRIPTION("Analogix DP Core Driver");
 MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
new file mode 100644
index 0000000..70724ec
--- /dev/null
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
@@ -0,0 +1,277 @@
+/*
+ * Header file for Analogix DP (Display Port) core interface driver.
+ *
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ * Author: Jingoo Han <jg1.han at samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef _ANALOGIX_DP_CORE_H
+#define _ANALOGIX_DP_CORE_H
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_dp_helper.h>
+
+#define DP_TIMEOUT_LOOP_COUNT		100
+#define MAX_CR_LOOP			5
+#define MAX_EQ_LOOP			5
+
+enum link_rate_type {
+	LINK_RATE_1_62GBPS = 0x06,
+	LINK_RATE_2_70GBPS = 0x0a
+};
+
+enum link_lane_count_type {
+	LANE_COUNT1 = 1,
+	LANE_COUNT2 = 2,
+	LANE_COUNT4 = 4
+};
+
+enum link_training_state {
+	START,
+	CLOCK_RECOVERY,
+	EQUALIZER_TRAINING,
+	FINISHED,
+	FAILED
+};
+
+enum voltage_swing_level {
+	VOLTAGE_LEVEL_0,
+	VOLTAGE_LEVEL_1,
+	VOLTAGE_LEVEL_2,
+	VOLTAGE_LEVEL_3,
+};
+
+enum pre_emphasis_level {
+	PRE_EMPHASIS_LEVEL_0,
+	PRE_EMPHASIS_LEVEL_1,
+	PRE_EMPHASIS_LEVEL_2,
+	PRE_EMPHASIS_LEVEL_3,
+};
+
+enum pattern_set {
+	PRBS7,
+	D10_2,
+	TRAINING_PTN1,
+	TRAINING_PTN2,
+	DP_NONE
+};
+
+enum color_space {
+	COLOR_RGB,
+	COLOR_YCBCR422,
+	COLOR_YCBCR444
+};
+
+enum color_depth {
+	COLOR_6,
+	COLOR_8,
+	COLOR_10,
+	COLOR_12
+};
+
+enum color_coefficient {
+	COLOR_YCBCR601,
+	COLOR_YCBCR709
+};
+
+enum dynamic_range {
+	VESA,
+	CEA
+};
+
+enum pll_status {
+	PLL_UNLOCKED,
+	PLL_LOCKED
+};
+
+enum clock_recovery_m_value_type {
+	CALCULATED_M,
+	REGISTER_M
+};
+
+enum video_timing_recognition_type {
+	VIDEO_TIMING_FROM_CAPTURE,
+	VIDEO_TIMING_FROM_REGISTER
+};
+
+enum analog_power_block {
+	AUX_BLOCK,
+	CH0_BLOCK,
+	CH1_BLOCK,
+	CH2_BLOCK,
+	CH3_BLOCK,
+	ANALOG_TOTAL,
+	POWER_ALL
+};
+
+enum dp_irq_type {
+	DP_IRQ_TYPE_HP_CABLE_IN,
+	DP_IRQ_TYPE_HP_CABLE_OUT,
+	DP_IRQ_TYPE_HP_CHANGE,
+	DP_IRQ_TYPE_UNKNOWN,
+};
+
+struct video_info {
+	char *name;
+
+	bool h_sync_polarity;
+	bool v_sync_polarity;
+	bool interlaced;
+
+	enum color_space color_space;
+	enum dynamic_range dynamic_range;
+	enum color_coefficient ycbcr_coeff;
+	enum color_depth color_depth;
+
+	enum link_rate_type link_rate;
+	enum link_lane_count_type lane_count;
+};
+
+struct link_train {
+	int eq_loop;
+	int cr_loop[4];
+
+	u8 link_rate;
+	u8 lane_count;
+	u8 training_lane[4];
+
+	enum link_training_state lt_state;
+};
+
+struct analogix_dp_device {
+	struct drm_encoder	*encoder;
+	struct drm_connector	connector;
+	struct device		*dev;
+	struct drm_device	*drm_dev;
+	struct drm_bridge	*bridge;
+	struct clk		*clock;
+	unsigned int		irq;
+	void __iomem		*reg_base;
+
+	struct video_info	*video_info;
+	struct link_train	link_train;
+	struct work_struct	hotplug_work;
+	struct phy		*phy;
+	int			dpms_mode;
+	int			hpd_gpio;
+
+	struct analogix_dp_plat_data *plat_data;
+};
+
+/* analogix_dp_reg.c */
+void analogix_dp_enable_video_mute(struct analogix_dp_device *dp, bool enable);
+void analogix_dp_stop_video(struct analogix_dp_device *dp);
+void analogix_dp_lane_swap(struct analogix_dp_device *dp, bool enable);
+void analogix_dp_init_analog_param(struct analogix_dp_device *dp);
+void analogix_dp_init_interrupt(struct analogix_dp_device *dp);
+void analogix_dp_reset(struct analogix_dp_device *dp);
+void analogix_dp_swreset(struct analogix_dp_device *dp);
+void analogix_dp_config_interrupt(struct analogix_dp_device *dp);
+enum pll_status analogix_dp_get_pll_lock_status(struct analogix_dp_device *dp);
+void analogix_dp_set_pll_power_down(struct analogix_dp_device *dp, bool enable);
+void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp,
+				       enum analog_power_block block,
+				       bool enable);
+void analogix_dp_init_analog_func(struct analogix_dp_device *dp);
+void analogix_dp_init_hpd(struct analogix_dp_device *dp);
+enum dp_irq_type analogix_dp_get_irq_type(struct analogix_dp_device *dp);
+void analogix_dp_clear_hotplug_interrupts(struct analogix_dp_device *dp);
+void analogix_dp_reset_aux(struct analogix_dp_device *dp);
+void analogix_dp_init_aux(struct analogix_dp_device *dp);
+int analogix_dp_get_plug_in_status(struct analogix_dp_device *dp);
+void analogix_dp_enable_sw_function(struct analogix_dp_device *dp);
+int analogix_dp_start_aux_transaction(struct analogix_dp_device *dp);
+int analogix_dp_write_byte_to_dpcd(struct analogix_dp_device *dp,
+				unsigned int reg_addr,
+				unsigned char data);
+int analogix_dp_read_byte_from_dpcd(struct analogix_dp_device *dp,
+				unsigned int reg_addr,
+				unsigned char *data);
+int analogix_dp_write_bytes_to_dpcd(struct analogix_dp_device *dp,
+				unsigned int reg_addr,
+				unsigned int count,
+				unsigned char data[]);
+int analogix_dp_read_bytes_from_dpcd(struct analogix_dp_device *dp,
+				unsigned int reg_addr,
+				unsigned int count,
+				unsigned char data[]);
+int analogix_dp_select_i2c_device(struct analogix_dp_device *dp,
+				unsigned int device_addr,
+				unsigned int reg_addr);
+int analogix_dp_read_byte_from_i2c(struct analogix_dp_device *dp,
+				unsigned int device_addr,
+				unsigned int reg_addr,
+				unsigned int *data);
+int analogix_dp_read_bytes_from_i2c(struct analogix_dp_device *dp,
+				unsigned int device_addr,
+				unsigned int reg_addr,
+				unsigned int count,
+				unsigned char edid[]);
+void analogix_dp_set_link_bandwidth(struct analogix_dp_device *dp, u32 bwtype);
+void analogix_dp_get_link_bandwidth(struct analogix_dp_device *dp, u32 *bwtype);
+void analogix_dp_set_lane_count(struct analogix_dp_device *dp, u32 count);
+void analogix_dp_get_lane_count(struct analogix_dp_device *dp, u32 *count);
+void analogix_dp_enable_enhanced_mode(struct analogix_dp_device *dp, bool enable);
+void analogix_dp_set_training_pattern(struct analogix_dp_device *dp,
+				enum pattern_set pattern);
+void analogix_dp_set_lane0_pre_emphasis(struct analogix_dp_device *dp, u32 level);
+void analogix_dp_set_lane1_pre_emphasis(struct analogix_dp_device *dp, u32 level);
+void analogix_dp_set_lane2_pre_emphasis(struct analogix_dp_device *dp, u32 level);
+void analogix_dp_set_lane3_pre_emphasis(struct analogix_dp_device *dp, u32 level);
+void analogix_dp_set_lane0_link_training(struct analogix_dp_device *dp,
+				u32 training_lane);
+void analogix_dp_set_lane1_link_training(struct analogix_dp_device *dp,
+				u32 training_lane);
+void analogix_dp_set_lane2_link_training(struct analogix_dp_device *dp,
+				u32 training_lane);
+void analogix_dp_set_lane3_link_training(struct analogix_dp_device *dp,
+				u32 training_lane);
+u32 analogix_dp_get_lane0_link_training(struct analogix_dp_device *dp);
+u32 analogix_dp_get_lane1_link_training(struct analogix_dp_device *dp);
+u32 analogix_dp_get_lane2_link_training(struct analogix_dp_device *dp);
+u32 analogix_dp_get_lane3_link_training(struct analogix_dp_device *dp);
+void analogix_dp_reset_macro(struct analogix_dp_device *dp);
+void analogix_dp_init_video(struct analogix_dp_device *dp);
+
+void analogix_dp_set_video_color_format(struct analogix_dp_device *dp);
+int analogix_dp_is_slave_video_stream_clock_on(struct analogix_dp_device *dp);
+void analogix_dp_set_video_cr_mn(struct analogix_dp_device *dp,
+			enum clock_recovery_m_value_type type,
+			u32 m_value,
+			u32 n_value);
+void analogix_dp_set_video_timing_mode(struct analogix_dp_device *dp, u32 type);
+void analogix_dp_enable_video_master(struct analogix_dp_device *dp, bool enable);
+void analogix_dp_start_video(struct analogix_dp_device *dp);
+int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp);
+void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp);
+void analogix_dp_enable_scrambling(struct analogix_dp_device *dp);
+void analogix_dp_disable_scrambling(struct analogix_dp_device *dp);
+
+/* I2C EDID Chip ID, Slave Address */
+#define I2C_EDID_DEVICE_ADDR			0x50
+#define I2C_E_EDID_DEVICE_ADDR			0x30
+
+#define EDID_BLOCK_LENGTH			0x80
+#define EDID_HEADER_PATTERN			0x00
+#define EDID_EXTENSION_FLAG			0x7e
+#define EDID_CHECKSUM				0x7f
+
+/* DP_MAX_LANE_COUNT */
+#define DPCD_ENHANCED_FRAME_CAP(x)		(((x) >> 7) & 0x1)
+#define DPCD_MAX_LANE_COUNT(x)			((x) & 0x1f)
+
+/* DP_LANE_COUNT_SET */
+#define DPCD_LANE_COUNT_SET(x)			((x) & 0x1f)
+
+/* DP_TRAINING_LANE0_SET */
+#define DPCD_PRE_EMPHASIS_SET(x)		(((x) & 0x3) << 3)
+#define DPCD_PRE_EMPHASIS_GET(x)		(((x) >> 3) & 0x3)
+#define DPCD_VOLTAGE_SWING_SET(x)		(((x) & 0x3) << 0)
+#define DPCD_VOLTAGE_SWING_GET(x)		(((x) >> 0) & 0x3)
+
+#endif /* _ANALOGIX_DP_CORE_H */
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
new file mode 100644
index 0000000..442cc66
--- /dev/null
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
@@ -0,0 +1,1263 @@
+/*
+ * Analogix DP (Display port) core register interface driver.
+ *
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ * Author: Jingoo Han <jg1.han at samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+
+#include "analogix_dp_core.h"
+#include "analogix_dp_reg.h"
+
+#define COMMON_INT_MASK_1	0
+#define COMMON_INT_MASK_2	0
+#define COMMON_INT_MASK_3	0
+#define COMMON_INT_MASK_4	(HOTPLUG_CHG | HPD_LOST | PLUG)
+#define INT_STA_MASK		INT_HPD
+
+void analogix_dp_enable_video_mute(struct analogix_dp_device *dp, bool enable)
+{
+	u32 reg;
+
+	if (enable) {
+		reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_1);
+		reg |= HDCP_VIDEO_MUTE;
+		writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_1);
+	} else {
+		reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_1);
+		reg &= ~HDCP_VIDEO_MUTE;
+		writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_1);
+	}
+}
+
+void analogix_dp_stop_video(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_1);
+	reg &= ~VIDEO_EN;
+	writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_1);
+}
+
+void analogix_dp_lane_swap(struct analogix_dp_device *dp, bool enable)
+{
+	u32 reg;
+
+	if (enable)
+		reg = LANE3_MAP_LOGIC_LANE_0 | LANE2_MAP_LOGIC_LANE_1 |
+			LANE1_MAP_LOGIC_LANE_2 | LANE0_MAP_LOGIC_LANE_3;
+	else
+		reg = LANE3_MAP_LOGIC_LANE_3 | LANE2_MAP_LOGIC_LANE_2 |
+			LANE1_MAP_LOGIC_LANE_1 | LANE0_MAP_LOGIC_LANE_0;
+
+	writel(reg, dp->reg_base + ANALOGIX_DP_LANE_MAP);
+}
+
+void analogix_dp_init_analog_param(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = TX_TERMINAL_CTRL_50_OHM;
+	writel(reg, dp->reg_base + ANALOGIX_DP_ANALOG_CTL_1);
+
+	reg = SEL_24M | TX_DVDD_BIT_1_0625V;
+	writel(reg, dp->reg_base + ANALOGIX_DP_ANALOG_CTL_2);
+
+	reg = DRIVE_DVDD_BIT_1_0625V | VCO_BIT_600_MICRO;
+	writel(reg, dp->reg_base + ANALOGIX_DP_ANALOG_CTL_3);
+
+	reg = PD_RING_OSC | AUX_TERMINAL_CTRL_50_OHM |
+		TX_CUR1_2X | TX_CUR_16_MA;
+	writel(reg, dp->reg_base + ANALOGIX_DP_PLL_FILTER_CTL_1);
+
+	reg = CH3_AMP_400_MV | CH2_AMP_400_MV |
+		CH1_AMP_400_MV | CH0_AMP_400_MV;
+	writel(reg, dp->reg_base + ANALOGIX_DP_TX_AMP_TUNING_CTL);
+}
+
+void analogix_dp_init_interrupt(struct analogix_dp_device *dp)
+{
+	/* Set interrupt pin assertion polarity as high */
+	writel(INT_POL1 | INT_POL0, dp->reg_base + ANALOGIX_DP_INT_CTL);
+
+	/* Clear pending regisers */
+	writel(0xff, dp->reg_base + ANALOGIX_DP_COMMON_INT_STA_1);
+	writel(0x4f, dp->reg_base + ANALOGIX_DP_COMMON_INT_STA_2);
+	writel(0xe0, dp->reg_base + ANALOGIX_DP_COMMON_INT_STA_3);
+	writel(0xe7, dp->reg_base + ANALOGIX_DP_COMMON_INT_STA_4);
+	writel(0x63, dp->reg_base + ANALOGIX_DP_INT_STA);
+
+	/* 0:mask,1: unmask */
+	writel(0x00, dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_1);
+	writel(0x00, dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_2);
+	writel(0x00, dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_3);
+	writel(0x00, dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_4);
+	writel(0x00, dp->reg_base + ANALOGIX_DP_INT_STA_MASK);
+}
+
+void analogix_dp_reset(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	analogix_dp_stop_video(dp);
+	analogix_dp_enable_video_mute(dp, 0);
+
+	reg = MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N |
+		AUD_FIFO_FUNC_EN_N | AUD_FUNC_EN_N |
+		HDCP_FUNC_EN_N | SW_FUNC_EN_N;
+	writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_1);
+
+	reg = SSC_FUNC_EN_N | AUX_FUNC_EN_N |
+		SERDES_FIFO_FUNC_EN_N |
+		LS_CLK_DOMAIN_FUNC_EN_N;
+	writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_2);
+
+	usleep_range(20, 30);
+
+	analogix_dp_lane_swap(dp, 0);
+
+	writel(0x0, dp->reg_base + ANALOGIX_DP_SYS_CTL_1);
+	writel(0x40, dp->reg_base + ANALOGIX_DP_SYS_CTL_2);
+	writel(0x0, dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
+	writel(0x0, dp->reg_base + ANALOGIX_DP_SYS_CTL_4);
+
+	writel(0x0, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+	writel(0x0, dp->reg_base + ANALOGIX_DP_HDCP_CTL);
+
+	writel(0x5e, dp->reg_base + ANALOGIX_DP_HPD_DEGLITCH_L);
+	writel(0x1a, dp->reg_base + ANALOGIX_DP_HPD_DEGLITCH_H);
+
+	writel(0x10, dp->reg_base + ANALOGIX_DP_LINK_DEBUG_CTL);
+
+	writel(0x0, dp->reg_base + ANALOGIX_DP_PHY_TEST);
+
+	writel(0x0, dp->reg_base + ANALOGIX_DP_VIDEO_FIFO_THRD);
+	writel(0x20, dp->reg_base + ANALOGIX_DP_AUDIO_MARGIN);
+
+	writel(0x4, dp->reg_base + ANALOGIX_DP_M_VID_GEN_FILTER_TH);
+	writel(0x2, dp->reg_base + ANALOGIX_DP_M_AUD_GEN_FILTER_TH);
+
+	writel(0x00000101, dp->reg_base + ANALOGIX_DP_SOC_GENERAL_CTL);
+}
+
+void analogix_dp_swreset(struct analogix_dp_device *dp)
+{
+	writel(RESET_DP_TX, dp->reg_base + ANALOGIX_DP_TX_SW_RESET);
+}
+
+void analogix_dp_config_interrupt(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	/* 0: mask, 1: unmask */
+	reg = COMMON_INT_MASK_1;
+	writel(reg, dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_1);
+
+	reg = COMMON_INT_MASK_2;
+	writel(reg, dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_2);
+
+	reg = COMMON_INT_MASK_3;
+	writel(reg, dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_3);
+
+	reg = COMMON_INT_MASK_4;
+	writel(reg, dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_4);
+
+	reg = INT_STA_MASK;
+	writel(reg, dp->reg_base + ANALOGIX_DP_INT_STA_MASK);
+}
+
+enum pll_status analogix_dp_get_pll_lock_status(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_DEBUG_CTL);
+	if (reg & PLL_LOCK)
+		return PLL_LOCKED;
+	else
+		return PLL_UNLOCKED;
+}
+
+void analogix_dp_set_pll_power_down(struct analogix_dp_device *dp, bool enable)
+{
+	u32 reg;
+
+	if (enable) {
+		reg = readl(dp->reg_base + ANALOGIX_DP_PLL_CTL);
+		reg |= DP_PLL_PD;
+		writel(reg, dp->reg_base + ANALOGIX_DP_PLL_CTL);
+	} else {
+		reg = readl(dp->reg_base + ANALOGIX_DP_PLL_CTL);
+		reg &= ~DP_PLL_PD;
+		writel(reg, dp->reg_base + ANALOGIX_DP_PLL_CTL);
+	}
+}
+
+void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp,
+				enum analog_power_block block,
+				bool enable)
+{
+	u32 reg;
+
+	switch (block) {
+	case AUX_BLOCK:
+		if (enable) {
+			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg |= AUX_PD;
+			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+		} else {
+			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg &= ~AUX_PD;
+			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+		}
+		break;
+	case CH0_BLOCK:
+		if (enable) {
+			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg |= CH0_PD;
+			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+		} else {
+			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg &= ~CH0_PD;
+			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+		}
+		break;
+	case CH1_BLOCK:
+		if (enable) {
+			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg |= CH1_PD;
+			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+		} else {
+			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg &= ~CH1_PD;
+			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+		}
+		break;
+	case CH2_BLOCK:
+		if (enable) {
+			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg |= CH2_PD;
+			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+		} else {
+			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg &= ~CH2_PD;
+			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+		}
+		break;
+	case CH3_BLOCK:
+		if (enable) {
+			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg |= CH3_PD;
+			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+		} else {
+			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg &= ~CH3_PD;
+			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+		}
+		break;
+	case ANALOG_TOTAL:
+		if (enable) {
+			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg |= DP_PHY_PD;
+			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+		} else {
+			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg &= ~DP_PHY_PD;
+			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+		}
+		break;
+	case POWER_ALL:
+		if (enable) {
+			reg = DP_PHY_PD | AUX_PD | CH3_PD | CH2_PD |
+				CH1_PD | CH0_PD;
+			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+		} else {
+			writel(0x00, dp->reg_base + ANALOGIX_DP_PHY_PD);
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+void analogix_dp_init_analog_func(struct analogix_dp_device *dp)
+{
+	u32 reg;
+	int timeout_loop = 0;
+
+	analogix_dp_set_analog_power_down(dp, POWER_ALL, 0);
+
+	reg = PLL_LOCK_CHG;
+	writel(reg, dp->reg_base + ANALOGIX_DP_COMMON_INT_STA_1);
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_DEBUG_CTL);
+	reg &= ~(F_PLL_LOCK | PLL_LOCK_CTRL);
+	writel(reg, dp->reg_base + ANALOGIX_DP_DEBUG_CTL);
+
+	/* Power up PLL */
+	if (analogix_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
+		analogix_dp_set_pll_power_down(dp, 0);
+
+		while (analogix_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
+			timeout_loop++;
+			if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) {
+				dev_err(dp->dev, "failed to get pll lock status\n");
+				return;
+			}
+			usleep_range(10, 20);
+		}
+	}
+
+	/* Enable Serdes FIFO function and Link symbol clock domain module */
+	reg = readl(dp->reg_base + ANALOGIX_DP_FUNC_EN_2);
+	reg &= ~(SERDES_FIFO_FUNC_EN_N | LS_CLK_DOMAIN_FUNC_EN_N
+		| AUX_FUNC_EN_N);
+	writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_2);
+}
+
+void analogix_dp_clear_hotplug_interrupts(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	if (gpio_is_valid(dp->hpd_gpio))
+		return;
+
+	reg = HOTPLUG_CHG | HPD_LOST | PLUG;
+	writel(reg, dp->reg_base + ANALOGIX_DP_COMMON_INT_STA_4);
+
+	reg = INT_HPD;
+	writel(reg, dp->reg_base + ANALOGIX_DP_INT_STA);
+}
+
+void analogix_dp_init_hpd(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	if (gpio_is_valid(dp->hpd_gpio))
+		return;
+
+	analogix_dp_clear_hotplug_interrupts(dp);
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
+	reg &= ~(F_HPD | HPD_CTRL);
+	writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
+}
+
+enum dp_irq_type analogix_dp_get_irq_type(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	if (gpio_is_valid(dp->hpd_gpio)) {
+		reg = gpio_get_value(dp->hpd_gpio);
+		if (reg)
+			return DP_IRQ_TYPE_HP_CABLE_IN;
+		else
+			return DP_IRQ_TYPE_HP_CABLE_OUT;
+	} else {
+		/* Parse hotplug interrupt status register */
+		reg = readl(dp->reg_base + ANALOGIX_DP_COMMON_INT_STA_4);
+
+		if (reg & PLUG)
+			return DP_IRQ_TYPE_HP_CABLE_IN;
+
+		if (reg & HPD_LOST)
+			return DP_IRQ_TYPE_HP_CABLE_OUT;
+
+		if (reg & HOTPLUG_CHG)
+			return DP_IRQ_TYPE_HP_CHANGE;
+
+		return DP_IRQ_TYPE_UNKNOWN;
+	}
+}
+
+void analogix_dp_reset_aux(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	/* Disable AUX channel module */
+	reg = readl(dp->reg_base + ANALOGIX_DP_FUNC_EN_2);
+	reg |= AUX_FUNC_EN_N;
+	writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_2);
+}
+
+void analogix_dp_init_aux(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	/* Clear inerrupts related to AUX channel */
+	reg = RPLY_RECEIV | AUX_ERR;
+	writel(reg, dp->reg_base + ANALOGIX_DP_INT_STA);
+
+	analogix_dp_reset_aux(dp);
+
+	/* Disable AUX transaction H/W retry */
+	reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3) | AUX_HW_RETRY_COUNT_SEL(0)|
+		AUX_HW_RETRY_INTERVAL_600_MICROSECONDS;
+	writel(reg, dp->reg_base + ANALOGIX_DP_AUX_HW_RETRY_CTL);
+
+	/* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */
+	reg = DEFER_CTRL_EN | DEFER_COUNT(1);
+	writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_DEFER_CTL);
+
+	/* Enable AUX channel module */
+	reg = readl(dp->reg_base + ANALOGIX_DP_FUNC_EN_2);
+	reg &= ~AUX_FUNC_EN_N;
+	writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_2);
+}
+
+int analogix_dp_get_plug_in_status(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	if (gpio_is_valid(dp->hpd_gpio)) {
+		if (gpio_get_value(dp->hpd_gpio))
+			return 0;
+	} else {
+		reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
+		if (reg & HPD_STATUS)
+			return 0;
+	}
+
+	return -EINVAL;
+}
+
+void analogix_dp_enable_sw_function(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_FUNC_EN_1);
+	reg &= ~SW_FUNC_EN_N;
+	writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_1);
+}
+
+int analogix_dp_start_aux_transaction(struct analogix_dp_device *dp)
+{
+	int reg;
+	int retval = 0;
+	int timeout_loop = 0;
+
+	/* Enable AUX CH operation */
+	reg = readl(dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_2);
+	reg |= AUX_EN;
+	writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_2);
+
+	/* Is AUX CH command reply received? */
+	reg = readl(dp->reg_base + ANALOGIX_DP_INT_STA);
+	while (!(reg & RPLY_RECEIV)) {
+		timeout_loop++;
+		if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) {
+			dev_err(dp->dev, "AUX CH command reply failed!\n");
+			return -ETIMEDOUT;
+		}
+		reg = readl(dp->reg_base + ANALOGIX_DP_INT_STA);
+		usleep_range(10, 11);
+	}
+
+	/* Clear interrupt source for AUX CH command reply */
+	writel(RPLY_RECEIV, dp->reg_base + ANALOGIX_DP_INT_STA);
+
+	/* Clear interrupt source for AUX CH access error */
+	reg = readl(dp->reg_base + ANALOGIX_DP_INT_STA);
+	if (reg & AUX_ERR) {
+		writel(AUX_ERR, dp->reg_base + ANALOGIX_DP_INT_STA);
+		return -EREMOTEIO;
+	}
+
+	/* Check AUX CH error access status */
+	reg = readl(dp->reg_base + ANALOGIX_DP_AUX_CH_STA);
+	if ((reg & AUX_STATUS_MASK) != 0) {
+		dev_err(dp->dev, "AUX CH error happens: %d\n\n",
+			reg & AUX_STATUS_MASK);
+		return -EREMOTEIO;
+	}
+
+	return retval;
+}
+
+int analogix_dp_write_byte_to_dpcd(struct analogix_dp_device *dp,
+				unsigned int reg_addr,
+				unsigned char data)
+{
+	u32 reg;
+	int i;
+	int retval;
+
+	for (i = 0; i < 3; i++) {
+		/* Clear AUX CH data buffer */
+		reg = BUF_CLR;
+		writel(reg, dp->reg_base + ANALOGIX_DP_BUFFER_DATA_CTL);
+
+		/* Select DPCD device address */
+		reg = AUX_ADDR_7_0(reg_addr);
+		writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_7_0);
+		reg = AUX_ADDR_15_8(reg_addr);
+		writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_15_8);
+		reg = AUX_ADDR_19_16(reg_addr);
+		writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_19_16);
+
+		/* Write data buffer */
+		reg = (unsigned int)data;
+		writel(reg, dp->reg_base + ANALOGIX_DP_BUF_DATA_0);
+
+		/*
+		 * Set DisplayPort transaction and write 1 byte
+		 * If bit 3 is 1, DisplayPort transaction.
+		 * If Bit 3 is 0, I2C transaction.
+		 */
+		reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE;
+		writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_1);
+
+		/* Start AUX transaction */
+		retval = analogix_dp_start_aux_transaction(dp);
+		if (retval == 0)
+			break;
+		else
+			dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
+				__func__);
+	}
+
+	return retval;
+}
+
+int analogix_dp_read_byte_from_dpcd(struct analogix_dp_device *dp,
+				unsigned int reg_addr,
+				unsigned char *data)
+{
+	u32 reg;
+	int i;
+	int retval;
+
+	for (i = 0; i < 3; i++) {
+		/* Clear AUX CH data buffer */
+		reg = BUF_CLR;
+		writel(reg, dp->reg_base + ANALOGIX_DP_BUFFER_DATA_CTL);
+
+		/* Select DPCD device address */
+		reg = AUX_ADDR_7_0(reg_addr);
+		writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_7_0);
+		reg = AUX_ADDR_15_8(reg_addr);
+		writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_15_8);
+		reg = AUX_ADDR_19_16(reg_addr);
+		writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_19_16);
+
+		/*
+		 * Set DisplayPort transaction and read 1 byte
+		 * If bit 3 is 1, DisplayPort transaction.
+		 * If Bit 3 is 0, I2C transaction.
+		 */
+		reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ;
+		writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_1);
+
+		/* Start AUX transaction */
+		retval = analogix_dp_start_aux_transaction(dp);
+		if (retval == 0)
+			break;
+		else
+			dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
+				__func__);
+	}
+
+	/* Read data buffer */
+	reg = readl(dp->reg_base + ANALOGIX_DP_BUF_DATA_0);
+	*data = (unsigned char)(reg & 0xff);
+
+	return retval;
+}
+
+int analogix_dp_write_bytes_to_dpcd(struct analogix_dp_device *dp,
+				unsigned int reg_addr,
+				unsigned int count,
+				unsigned char data[])
+{
+	u32 reg;
+	unsigned int start_offset;
+	unsigned int cur_data_count;
+	unsigned int cur_data_idx;
+	int i;
+	int retval = 0;
+
+	/* Clear AUX CH data buffer */
+	reg = BUF_CLR;
+	writel(reg, dp->reg_base + ANALOGIX_DP_BUFFER_DATA_CTL);
+
+	start_offset = 0;
+	while (start_offset < count) {
+		/* Buffer size of AUX CH is 16 * 4bytes */
+		if ((count - start_offset) > 16)
+			cur_data_count = 16;
+		else
+			cur_data_count = count - start_offset;
+
+		for (i = 0; i < 3; i++) {
+			/* Select DPCD device address */
+			reg = AUX_ADDR_7_0(reg_addr + start_offset);
+			writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_7_0);
+			reg = AUX_ADDR_15_8(reg_addr + start_offset);
+			writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_15_8);
+			reg = AUX_ADDR_19_16(reg_addr + start_offset);
+			writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_19_16);
+
+			for (cur_data_idx = 0; cur_data_idx < cur_data_count;
+			     cur_data_idx++) {
+				reg = data[start_offset + cur_data_idx];
+				writel(reg, dp->reg_base + ANALOGIX_DP_BUF_DATA_0
+							  + 4 * cur_data_idx);
+			}
+
+			/*
+			 * Set DisplayPort transaction and write
+			 * If bit 3 is 1, DisplayPort transaction.
+			 * If Bit 3 is 0, I2C transaction.
+			 */
+			reg = AUX_LENGTH(cur_data_count) |
+				AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE;
+			writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_1);
+
+			/* Start AUX transaction */
+			retval = analogix_dp_start_aux_transaction(dp);
+			if (retval == 0)
+				break;
+			else
+				dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
+					__func__);
+		}
+
+		start_offset += cur_data_count;
+	}
+
+	return retval;
+}
+
+int analogix_dp_read_bytes_from_dpcd(struct analogix_dp_device *dp,
+				unsigned int reg_addr,
+				unsigned int count,
+				unsigned char data[])
+{
+	u32 reg;
+	unsigned int start_offset;
+	unsigned int cur_data_count;
+	unsigned int cur_data_idx;
+	int i;
+	int retval = 0;
+
+	/* Clear AUX CH data buffer */
+	reg = BUF_CLR;
+	writel(reg, dp->reg_base + ANALOGIX_DP_BUFFER_DATA_CTL);
+
+	start_offset = 0;
+	while (start_offset < count) {
+		/* Buffer size of AUX CH is 16 * 4bytes */
+		if ((count - start_offset) > 16)
+			cur_data_count = 16;
+		else
+			cur_data_count = count - start_offset;
+
+		/* AUX CH Request Transaction process */
+		for (i = 0; i < 3; i++) {
+			/* Select DPCD device address */
+			reg = AUX_ADDR_7_0(reg_addr + start_offset);
+			writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_7_0);
+			reg = AUX_ADDR_15_8(reg_addr + start_offset);
+			writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_15_8);
+			reg = AUX_ADDR_19_16(reg_addr + start_offset);
+			writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_19_16);
+
+			/*
+			 * Set DisplayPort transaction and read
+			 * If bit 3 is 1, DisplayPort transaction.
+			 * If Bit 3 is 0, I2C transaction.
+			 */
+			reg = AUX_LENGTH(cur_data_count) |
+				AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ;
+			writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_1);
+
+			/* Start AUX transaction */
+			retval = analogix_dp_start_aux_transaction(dp);
+			if (retval == 0)
+				break;
+			else
+				dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
+					__func__);
+		}
+
+		for (cur_data_idx = 0; cur_data_idx < cur_data_count;
+		    cur_data_idx++) {
+			reg = readl(dp->reg_base + ANALOGIX_DP_BUF_DATA_0
+						 + 4 * cur_data_idx);
+			data[start_offset + cur_data_idx] =
+				(unsigned char)reg;
+		}
+
+		start_offset += cur_data_count;
+	}
+
+	return retval;
+}
+
+int analogix_dp_select_i2c_device(struct analogix_dp_device *dp,
+				unsigned int device_addr,
+				unsigned int reg_addr)
+{
+	u32 reg;
+	int retval;
+
+	/* Set EDID device address */
+	reg = device_addr;
+	writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_7_0);
+	writel(0x0, dp->reg_base + ANALOGIX_DP_AUX_ADDR_15_8);
+	writel(0x0, dp->reg_base + ANALOGIX_DP_AUX_ADDR_19_16);
+
+	/* Set offset from base address of EDID device */
+	writel(reg_addr, dp->reg_base + ANALOGIX_DP_BUF_DATA_0);
+
+	/*
+	 * Set I2C transaction and write address
+	 * If bit 3 is 1, DisplayPort transaction.
+	 * If Bit 3 is 0, I2C transaction.
+	 */
+	reg = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_MOT |
+		AUX_TX_COMM_WRITE;
+	writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_1);
+
+	/* Start AUX transaction */
+	retval = analogix_dp_start_aux_transaction(dp);
+	if (retval != 0)
+		dev_dbg(dp->dev, "%s: Aux Transaction fail!\n", __func__);
+
+	return retval;
+}
+
+int analogix_dp_read_byte_from_i2c(struct analogix_dp_device *dp,
+				unsigned int device_addr,
+				unsigned int reg_addr,
+				unsigned int *data)
+{
+	u32 reg;
+	int i;
+	int retval;
+
+	for (i = 0; i < 3; i++) {
+		/* Clear AUX CH data buffer */
+		reg = BUF_CLR;
+		writel(reg, dp->reg_base + ANALOGIX_DP_BUFFER_DATA_CTL);
+
+		/* Select EDID device */
+		retval = analogix_dp_select_i2c_device(dp, device_addr, reg_addr);
+		if (retval != 0)
+			continue;
+
+		/*
+		 * Set I2C transaction and read data
+		 * If bit 3 is 1, DisplayPort transaction.
+		 * If Bit 3 is 0, I2C transaction.
+		 */
+		reg = AUX_TX_COMM_I2C_TRANSACTION |
+			AUX_TX_COMM_READ;
+		writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_1);
+
+		/* Start AUX transaction */
+		retval = analogix_dp_start_aux_transaction(dp);
+		if (retval == 0)
+			break;
+		else
+			dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
+				__func__);
+	}
+
+	/* Read data */
+	if (retval == 0)
+		*data = readl(dp->reg_base + ANALOGIX_DP_BUF_DATA_0);
+
+	return retval;
+}
+
+int analogix_dp_read_bytes_from_i2c(struct analogix_dp_device *dp,
+				unsigned int device_addr,
+				unsigned int reg_addr,
+				unsigned int count,
+				unsigned char edid[])
+{
+	u32 reg;
+	unsigned int i, j;
+	unsigned int cur_data_idx;
+	unsigned int defer = 0;
+	int retval = 0;
+
+	for (i = 0; i < count; i += 16) {
+		for (j = 0; j < 3; j++) {
+			/* Clear AUX CH data buffer */
+			reg = BUF_CLR;
+			writel(reg, dp->reg_base + ANALOGIX_DP_BUFFER_DATA_CTL);
+
+			/* Set normal AUX CH command */
+			reg = readl(dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_2);
+			reg &= ~ADDR_ONLY;
+			writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_2);
+
+			/*
+			 * If Rx sends defer, Tx sends only reads
+			 * request without sending address
+			 */
+			if (!defer)
+				retval = analogix_dp_select_i2c_device(dp,
+						device_addr, reg_addr + i);
+			else
+				defer = 0;
+
+			if (retval == 0) {
+				/*
+				 * Set I2C transaction and write data
+				 * If bit 3 is 1, DisplayPort transaction.
+				 * If Bit 3 is 0, I2C transaction.
+				 */
+				reg = AUX_LENGTH(16) |
+					AUX_TX_COMM_I2C_TRANSACTION |
+					AUX_TX_COMM_READ;
+				writel(reg, dp->reg_base +
+					ANALOGIX_DP_AUX_CH_CTL_1);
+
+				/* Start AUX transaction */
+				retval = analogix_dp_start_aux_transaction(dp);
+				if (retval == 0)
+					break;
+				else
+					dev_dbg(dp->dev,
+						"%s: Aux Transaction fail!\n",
+						__func__);
+			}
+			/* Check if Rx sends defer */
+			reg = readl(dp->reg_base + ANALOGIX_DP_AUX_RX_COMM);
+			if (reg == AUX_RX_COMM_AUX_DEFER ||
+				reg == AUX_RX_COMM_I2C_DEFER) {
+				dev_err(dp->dev, "Defer: %d\n\n", reg);
+				defer = 1;
+			}
+		}
+
+		for (cur_data_idx = 0; cur_data_idx < 16; cur_data_idx++) {
+			reg = readl(dp->reg_base + ANALOGIX_DP_BUF_DATA_0
+						 + 4 * cur_data_idx);
+			edid[i + cur_data_idx] = (unsigned char)reg;
+		}
+	}
+
+	return retval;
+}
+
+void analogix_dp_set_link_bandwidth(struct analogix_dp_device *dp, u32 bwtype)
+{
+	u32 reg;
+
+	reg = bwtype;
+	if ((bwtype == LINK_RATE_2_70GBPS) || (bwtype == LINK_RATE_1_62GBPS))
+		writel(reg, dp->reg_base + ANALOGIX_DP_LINK_BW_SET);
+}
+
+void analogix_dp_get_link_bandwidth(struct analogix_dp_device *dp, u32 *bwtype)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_LINK_BW_SET);
+	*bwtype = reg;
+}
+
+void analogix_dp_set_lane_count(struct analogix_dp_device *dp, u32 count)
+{
+	u32 reg;
+
+	reg = count;
+	writel(reg, dp->reg_base + ANALOGIX_DP_LANE_COUNT_SET);
+}
+
+void analogix_dp_get_lane_count(struct analogix_dp_device *dp, u32 *count)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_LANE_COUNT_SET);
+	*count = reg;
+}
+
+void analogix_dp_enable_enhanced_mode(struct analogix_dp_device *dp, bool enable)
+{
+	u32 reg;
+
+	if (enable) {
+		reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_4);
+		reg |= ENHANCED;
+		writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_4);
+	} else {
+		reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_4);
+		reg &= ~ENHANCED;
+		writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_4);
+	}
+}
+
+void analogix_dp_set_training_pattern(struct analogix_dp_device *dp,
+				enum pattern_set pattern)
+{
+	u32 reg;
+
+	switch (pattern) {
+	case PRBS7:
+		reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_PRBS7;
+		writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
+		break;
+	case D10_2:
+		reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_D10_2;
+		writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
+		break;
+	case TRAINING_PTN1:
+		reg = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN1;
+		writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
+		break;
+	case TRAINING_PTN2:
+		reg = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN2;
+		writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
+		break;
+	case DP_NONE:
+		reg = SCRAMBLING_ENABLE |
+			LINK_QUAL_PATTERN_SET_DISABLE |
+			SW_TRAINING_PATTERN_SET_NORMAL;
+		writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
+		break;
+	default:
+		break;
+	}
+}
+
+void analogix_dp_set_lane0_pre_emphasis(struct analogix_dp_device *dp, u32 level)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_LN0_LINK_TRAINING_CTL);
+	reg &= ~PRE_EMPHASIS_SET_MASK;
+	reg |= level << PRE_EMPHASIS_SET_SHIFT;
+	writel(reg, dp->reg_base + ANALOGIX_DP_LN0_LINK_TRAINING_CTL);
+}
+
+void analogix_dp_set_lane1_pre_emphasis(struct analogix_dp_device *dp, u32 level)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_LN1_LINK_TRAINING_CTL);
+	reg &= ~PRE_EMPHASIS_SET_MASK;
+	reg |= level << PRE_EMPHASIS_SET_SHIFT;
+	writel(reg, dp->reg_base + ANALOGIX_DP_LN1_LINK_TRAINING_CTL);
+}
+
+void analogix_dp_set_lane2_pre_emphasis(struct analogix_dp_device *dp, u32 level)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_LN2_LINK_TRAINING_CTL);
+	reg &= ~PRE_EMPHASIS_SET_MASK;
+	reg |= level << PRE_EMPHASIS_SET_SHIFT;
+	writel(reg, dp->reg_base + ANALOGIX_DP_LN2_LINK_TRAINING_CTL);
+}
+
+void analogix_dp_set_lane3_pre_emphasis(struct analogix_dp_device *dp, u32 level)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_LN3_LINK_TRAINING_CTL);
+	reg &= ~PRE_EMPHASIS_SET_MASK;
+	reg |= level << PRE_EMPHASIS_SET_SHIFT;
+	writel(reg, dp->reg_base + ANALOGIX_DP_LN3_LINK_TRAINING_CTL);
+}
+
+void analogix_dp_set_lane0_link_training(struct analogix_dp_device *dp,
+					u32 training_lane)
+{
+	u32 reg;
+
+	reg = training_lane;
+	writel(reg, dp->reg_base + ANALOGIX_DP_LN0_LINK_TRAINING_CTL);
+}
+
+void analogix_dp_set_lane1_link_training(struct analogix_dp_device *dp,
+					u32 training_lane)
+{
+	u32 reg;
+
+	reg = training_lane;
+	writel(reg, dp->reg_base + ANALOGIX_DP_LN1_LINK_TRAINING_CTL);
+}
+
+void analogix_dp_set_lane2_link_training(struct analogix_dp_device *dp,
+					 u32 training_lane)
+{
+	u32 reg;
+
+	reg = training_lane;
+	writel(reg, dp->reg_base + ANALOGIX_DP_LN2_LINK_TRAINING_CTL);
+}
+
+void analogix_dp_set_lane3_link_training(struct analogix_dp_device *dp,
+					u32 training_lane)
+{
+	u32 reg;
+
+	reg = training_lane;
+	writel(reg, dp->reg_base + ANALOGIX_DP_LN3_LINK_TRAINING_CTL);
+}
+
+u32 analogix_dp_get_lane0_link_training(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_LN0_LINK_TRAINING_CTL);
+	return reg;
+}
+
+u32 analogix_dp_get_lane1_link_training(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_LN1_LINK_TRAINING_CTL);
+	return reg;
+}
+
+u32 analogix_dp_get_lane2_link_training(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_LN2_LINK_TRAINING_CTL);
+	return reg;
+}
+
+u32 analogix_dp_get_lane3_link_training(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_LN3_LINK_TRAINING_CTL);
+	return reg;
+}
+
+void analogix_dp_reset_macro(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_PHY_TEST);
+	reg |= MACRO_RST;
+	writel(reg, dp->reg_base + ANALOGIX_DP_PHY_TEST);
+
+	/* 10 us is the minimum reset time. */
+	usleep_range(10, 20);
+
+	reg &= ~MACRO_RST;
+	writel(reg, dp->reg_base + ANALOGIX_DP_PHY_TEST);
+}
+
+void analogix_dp_init_video(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = VSYNC_DET | VID_FORMAT_CHG | VID_CLK_CHG;
+	writel(reg, dp->reg_base + ANALOGIX_DP_COMMON_INT_STA_1);
+
+	reg = 0x0;
+	writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_1);
+
+	reg = CHA_CRI(4) | CHA_CTRL;
+	writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_2);
+
+	reg = 0x0;
+	writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
+
+	reg = VID_HRES_TH(2) | VID_VRES_TH(0);
+	writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_8);
+}
+
+void analogix_dp_set_video_color_format(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	/* Configure the input color depth, color space, dynamic range */
+	reg = (dp->video_info->dynamic_range << IN_D_RANGE_SHIFT) |
+		(dp->video_info->color_depth << IN_BPC_SHIFT) |
+		(dp->video_info->color_space << IN_COLOR_F_SHIFT);
+	writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_2);
+
+	/* Set Input Color YCbCr Coefficients to ITU601 or ITU709 */
+	reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
+	reg &= ~IN_YC_COEFFI_MASK;
+	if (dp->video_info->ycbcr_coeff)
+		reg |= IN_YC_COEFFI_ITU709;
+	else
+		reg |= IN_YC_COEFFI_ITU601;
+	writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
+}
+
+int analogix_dp_is_slave_video_stream_clock_on(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_1);
+	writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_1);
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_1);
+
+	if (!(reg & DET_STA)) {
+		dev_dbg(dp->dev, "Input stream clock not detected.\n");
+		return -EINVAL;
+	}
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_2);
+	writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_2);
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_2);
+	dev_dbg(dp->dev, "wait SYS_CTL_2.\n");
+
+	if (reg & CHA_STA) {
+		dev_dbg(dp->dev, "Input stream clk is changing\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+void analogix_dp_set_video_cr_mn(struct analogix_dp_device *dp,
+		enum clock_recovery_m_value_type type,
+		u32 m_value,
+		u32 n_value)
+{
+	u32 reg;
+
+	if (type == REGISTER_M) {
+		reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_4);
+		reg |= FIX_M_VID;
+		writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_4);
+		reg = m_value & 0xff;
+		writel(reg, dp->reg_base + ANALOGIX_DP_M_VID_0);
+		reg = (m_value >> 8) & 0xff;
+		writel(reg, dp->reg_base + ANALOGIX_DP_M_VID_1);
+		reg = (m_value >> 16) & 0xff;
+		writel(reg, dp->reg_base + ANALOGIX_DP_M_VID_2);
+
+		reg = n_value & 0xff;
+		writel(reg, dp->reg_base + ANALOGIX_DP_N_VID_0);
+		reg = (n_value >> 8) & 0xff;
+		writel(reg, dp->reg_base + ANALOGIX_DP_N_VID_1);
+		reg = (n_value >> 16) & 0xff;
+		writel(reg, dp->reg_base + ANALOGIX_DP_N_VID_2);
+	} else  {
+		reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_4);
+		reg &= ~FIX_M_VID;
+		writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_4);
+
+		writel(0x00, dp->reg_base + ANALOGIX_DP_N_VID_0);
+		writel(0x80, dp->reg_base + ANALOGIX_DP_N_VID_1);
+		writel(0x00, dp->reg_base + ANALOGIX_DP_N_VID_2);
+	}
+}
+
+void analogix_dp_set_video_timing_mode(struct analogix_dp_device *dp, u32 type)
+{
+	u32 reg;
+
+	if (type == VIDEO_TIMING_FROM_CAPTURE) {
+		reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+		reg &= ~FORMAT_SEL;
+		writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+	} else {
+		reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+		reg |= FORMAT_SEL;
+		writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+	}
+}
+
+void analogix_dp_enable_video_master(struct analogix_dp_device *dp, bool enable)
+{
+	u32 reg;
+
+	if (enable) {
+		reg = readl(dp->reg_base + ANALOGIX_DP_SOC_GENERAL_CTL);
+		reg &= ~VIDEO_MODE_MASK;
+		reg |= VIDEO_MASTER_MODE_EN | VIDEO_MODE_MASTER_MODE;
+		writel(reg, dp->reg_base + ANALOGIX_DP_SOC_GENERAL_CTL);
+	} else {
+		reg = readl(dp->reg_base + ANALOGIX_DP_SOC_GENERAL_CTL);
+		reg &= ~VIDEO_MODE_MASK;
+		reg |= VIDEO_MODE_SLAVE_MODE;
+		writel(reg, dp->reg_base + ANALOGIX_DP_SOC_GENERAL_CTL);
+	}
+}
+
+void analogix_dp_start_video(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_1);
+	reg |= VIDEO_EN;
+	writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_1);
+}
+
+int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
+	writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
+	if (!(reg & STRM_VALID)) {
+		dev_dbg(dp->dev, "Input video stream is not detected.\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_FUNC_EN_1);
+	reg &= ~(MASTER_VID_FUNC_EN_N|SLAVE_VID_FUNC_EN_N);
+	reg |= MASTER_VID_FUNC_EN_N;
+	writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_1);
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+	reg &= ~INTERACE_SCAN_CFG;
+	reg |= (dp->video_info->interlaced << 2);
+	writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+	reg &= ~VSYNC_POLARITY_CFG;
+	reg |= (dp->video_info->v_sync_polarity << 1);
+	writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+	reg &= ~HSYNC_POLARITY_CFG;
+	reg |= (dp->video_info->h_sync_polarity << 0);
+	writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+
+	reg = AUDIO_MODE_SPDIF_MODE | VIDEO_MODE_SLAVE_MODE;
+	writel(reg, dp->reg_base + ANALOGIX_DP_SOC_GENERAL_CTL);
+}
+
+void analogix_dp_enable_scrambling(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
+	reg &= ~SCRAMBLING_DISABLE;
+	writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
+}
+
+void analogix_dp_disable_scrambling(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
+	reg |= SCRAMBLING_DISABLE;
+	writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
+}
diff --git a/drivers/gpu/drm/exynos/exynos_dp_reg.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
similarity index 64%
rename from drivers/gpu/drm/exynos/exynos_dp_reg.h
rename to drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
index 2e9bd0e..738db4c 100644
--- a/drivers/gpu/drm/exynos/exynos_dp_reg.h
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
@@ -1,5 +1,5 @@
 /*
- * Register definition file for Samsung DP driver
+ * Register definition file for Analogix DP core driver
  *
  * Copyright (C) 2012 Samsung Electronics Co., Ltd.
  * Author: Jingoo Han <jg1.han at samsung.com>
@@ -9,96 +9,96 @@
  * published by the Free Software Foundation.
  */
 
-#ifndef _EXYNOS_DP_REG_H
-#define _EXYNOS_DP_REG_H
-
-#define EXYNOS_DP_TX_SW_RESET			0x14
-#define EXYNOS_DP_FUNC_EN_1			0x18
-#define EXYNOS_DP_FUNC_EN_2			0x1C
-#define EXYNOS_DP_VIDEO_CTL_1			0x20
-#define EXYNOS_DP_VIDEO_CTL_2			0x24
-#define EXYNOS_DP_VIDEO_CTL_3			0x28
-
-#define EXYNOS_DP_VIDEO_CTL_8			0x3C
-#define EXYNOS_DP_VIDEO_CTL_10			0x44
-
-#define EXYNOS_DP_LANE_MAP			0x35C
-
-#define EXYNOS_DP_ANALOG_CTL_1			0x370
-#define EXYNOS_DP_ANALOG_CTL_2			0x374
-#define EXYNOS_DP_ANALOG_CTL_3			0x378
-#define EXYNOS_DP_PLL_FILTER_CTL_1		0x37C
-#define EXYNOS_DP_TX_AMP_TUNING_CTL		0x380
-
-#define EXYNOS_DP_AUX_HW_RETRY_CTL		0x390
-
-#define EXYNOS_DP_COMMON_INT_STA_1		0x3C4
-#define EXYNOS_DP_COMMON_INT_STA_2		0x3C8
-#define EXYNOS_DP_COMMON_INT_STA_3		0x3CC
-#define EXYNOS_DP_COMMON_INT_STA_4		0x3D0
-#define EXYNOS_DP_INT_STA			0x3DC
-#define EXYNOS_DP_COMMON_INT_MASK_1		0x3E0
-#define EXYNOS_DP_COMMON_INT_MASK_2		0x3E4
-#define EXYNOS_DP_COMMON_INT_MASK_3		0x3E8
-#define EXYNOS_DP_COMMON_INT_MASK_4		0x3EC
-#define EXYNOS_DP_INT_STA_MASK			0x3F8
-#define EXYNOS_DP_INT_CTL			0x3FC
-
-#define EXYNOS_DP_SYS_CTL_1			0x600
-#define EXYNOS_DP_SYS_CTL_2			0x604
-#define EXYNOS_DP_SYS_CTL_3			0x608
-#define EXYNOS_DP_SYS_CTL_4			0x60C
-
-#define EXYNOS_DP_PKT_SEND_CTL			0x640
-#define EXYNOS_DP_HDCP_CTL			0x648
-
-#define EXYNOS_DP_LINK_BW_SET			0x680
-#define EXYNOS_DP_LANE_COUNT_SET		0x684
-#define EXYNOS_DP_TRAINING_PTN_SET		0x688
-#define EXYNOS_DP_LN0_LINK_TRAINING_CTL		0x68C
-#define EXYNOS_DP_LN1_LINK_TRAINING_CTL		0x690
-#define EXYNOS_DP_LN2_LINK_TRAINING_CTL		0x694
-#define EXYNOS_DP_LN3_LINK_TRAINING_CTL		0x698
-
-#define EXYNOS_DP_DEBUG_CTL			0x6C0
-#define EXYNOS_DP_HPD_DEGLITCH_L		0x6C4
-#define EXYNOS_DP_HPD_DEGLITCH_H		0x6C8
-#define EXYNOS_DP_LINK_DEBUG_CTL		0x6E0
-
-#define EXYNOS_DP_M_VID_0			0x700
-#define EXYNOS_DP_M_VID_1			0x704
-#define EXYNOS_DP_M_VID_2			0x708
-#define EXYNOS_DP_N_VID_0			0x70C
-#define EXYNOS_DP_N_VID_1			0x710
-#define EXYNOS_DP_N_VID_2			0x714
-
-#define EXYNOS_DP_PLL_CTL			0x71C
-#define EXYNOS_DP_PHY_PD			0x720
-#define EXYNOS_DP_PHY_TEST			0x724
-
-#define EXYNOS_DP_VIDEO_FIFO_THRD		0x730
-#define EXYNOS_DP_AUDIO_MARGIN			0x73C
-
-#define EXYNOS_DP_M_VID_GEN_FILTER_TH		0x764
-#define EXYNOS_DP_M_AUD_GEN_FILTER_TH		0x778
-#define EXYNOS_DP_AUX_CH_STA			0x780
-#define EXYNOS_DP_AUX_CH_DEFER_CTL		0x788
-#define EXYNOS_DP_AUX_RX_COMM			0x78C
-#define EXYNOS_DP_BUFFER_DATA_CTL		0x790
-#define EXYNOS_DP_AUX_CH_CTL_1			0x794
-#define EXYNOS_DP_AUX_ADDR_7_0			0x798
-#define EXYNOS_DP_AUX_ADDR_15_8			0x79C
-#define EXYNOS_DP_AUX_ADDR_19_16		0x7A0
-#define EXYNOS_DP_AUX_CH_CTL_2			0x7A4
-
-#define EXYNOS_DP_BUF_DATA_0			0x7C0
-
-#define EXYNOS_DP_SOC_GENERAL_CTL		0x800
-
-/* EXYNOS_DP_TX_SW_RESET */
+#ifndef _ANALOGIX_DP_REG_H
+#define _ANALOGIX_DP_REG_H
+
+#define ANALOGIX_DP_TX_SW_RESET			0x14
+#define ANALOGIX_DP_FUNC_EN_1			0x18
+#define ANALOGIX_DP_FUNC_EN_2			0x1C
+#define ANALOGIX_DP_VIDEO_CTL_1			0x20
+#define ANALOGIX_DP_VIDEO_CTL_2			0x24
+#define ANALOGIX_DP_VIDEO_CTL_3			0x28
+
+#define ANALOGIX_DP_VIDEO_CTL_8			0x3C
+#define ANALOGIX_DP_VIDEO_CTL_10		0x44
+
+#define ANALOGIX_DP_LANE_MAP			0x35C
+
+#define ANALOGIX_DP_ANALOG_CTL_1		0x370
+#define ANALOGIX_DP_ANALOG_CTL_2		0x374
+#define ANALOGIX_DP_ANALOG_CTL_3		0x378
+#define ANALOGIX_DP_PLL_FILTER_CTL_1		0x37C
+#define ANALOGIX_DP_TX_AMP_TUNING_CTL		0x380
+
+#define ANALOGIX_DP_AUX_HW_RETRY_CTL		0x390
+
+#define ANALOGIX_DP_COMMON_INT_STA_1		0x3C4
+#define ANALOGIX_DP_COMMON_INT_STA_2		0x3C8
+#define ANALOGIX_DP_COMMON_INT_STA_3		0x3CC
+#define ANALOGIX_DP_COMMON_INT_STA_4		0x3D0
+#define ANALOGIX_DP_INT_STA			0x3DC
+#define ANALOGIX_DP_COMMON_INT_MASK_1		0x3E0
+#define ANALOGIX_DP_COMMON_INT_MASK_2		0x3E4
+#define ANALOGIX_DP_COMMON_INT_MASK_3		0x3E8
+#define ANALOGIX_DP_COMMON_INT_MASK_4		0x3EC
+#define ANALOGIX_DP_INT_STA_MASK		0x3F8
+#define ANALOGIX_DP_INT_CTL			0x3FC
+
+#define ANALOGIX_DP_SYS_CTL_1			0x600
+#define ANALOGIX_DP_SYS_CTL_2			0x604
+#define ANALOGIX_DP_SYS_CTL_3			0x608
+#define ANALOGIX_DP_SYS_CTL_4			0x60C
+
+#define ANALOGIX_DP_PKT_SEND_CTL		0x640
+#define ANALOGIX_DP_HDCP_CTL			0x648
+
+#define ANALOGIX_DP_LINK_BW_SET			0x680
+#define ANALOGIX_DP_LANE_COUNT_SET		0x684
+#define ANALOGIX_DP_TRAINING_PTN_SET		0x688
+#define ANALOGIX_DP_LN0_LINK_TRAINING_CTL	0x68C
+#define ANALOGIX_DP_LN1_LINK_TRAINING_CTL	0x690
+#define ANALOGIX_DP_LN2_LINK_TRAINING_CTL	0x694
+#define ANALOGIX_DP_LN3_LINK_TRAINING_CTL	0x698
+
+#define ANALOGIX_DP_DEBUG_CTL			0x6C0
+#define ANALOGIX_DP_HPD_DEGLITCH_L		0x6C4
+#define ANALOGIX_DP_HPD_DEGLITCH_H		0x6C8
+#define ANALOGIX_DP_LINK_DEBUG_CTL		0x6E0
+
+#define ANALOGIX_DP_M_VID_0			0x700
+#define ANALOGIX_DP_M_VID_1			0x704
+#define ANALOGIX_DP_M_VID_2			0x708
+#define ANALOGIX_DP_N_VID_0			0x70C
+#define ANALOGIX_DP_N_VID_1			0x710
+#define ANALOGIX_DP_N_VID_2			0x714
+
+#define ANALOGIX_DP_PLL_CTL			0x71C
+#define ANALOGIX_DP_PHY_PD			0x720
+#define ANALOGIX_DP_PHY_TEST			0x724
+
+#define ANALOGIX_DP_VIDEO_FIFO_THRD		0x730
+#define ANALOGIX_DP_AUDIO_MARGIN		0x73C
+
+#define ANALOGIX_DP_M_VID_GEN_FILTER_TH		0x764
+#define ANALOGIX_DP_M_AUD_GEN_FILTER_TH		0x778
+#define ANALOGIX_DP_AUX_CH_STA			0x780
+#define ANALOGIX_DP_AUX_CH_DEFER_CTL		0x788
+#define ANALOGIX_DP_AUX_RX_COMM			0x78C
+#define ANALOGIX_DP_BUFFER_DATA_CTL		0x790
+#define ANALOGIX_DP_AUX_CH_CTL_1		0x794
+#define ANALOGIX_DP_AUX_ADDR_7_0		0x798
+#define ANALOGIX_DP_AUX_ADDR_15_8		0x79C
+#define ANALOGIX_DP_AUX_ADDR_19_16		0x7A0
+#define ANALOGIX_DP_AUX_CH_CTL_2		0x7A4
+
+#define ANALOGIX_DP_BUF_DATA_0			0x7C0
+
+#define ANALOGIX_DP_SOC_GENERAL_CTL		0x800
+
+/* ANALOGIX_DP_TX_SW_RESET */
 #define RESET_DP_TX				(0x1 << 0)
 
-/* EXYNOS_DP_FUNC_EN_1 */
+/* ANALOGIX_DP_FUNC_EN_1 */
 #define MASTER_VID_FUNC_EN_N			(0x1 << 7)
 #define SLAVE_VID_FUNC_EN_N			(0x1 << 5)
 #define AUD_FIFO_FUNC_EN_N			(0x1 << 4)
@@ -107,17 +107,17 @@
 #define CRC_FUNC_EN_N				(0x1 << 1)
 #define SW_FUNC_EN_N				(0x1 << 0)
 
-/* EXYNOS_DP_FUNC_EN_2 */
+/* ANALOGIX_DP_FUNC_EN_2 */
 #define SSC_FUNC_EN_N				(0x1 << 7)
 #define AUX_FUNC_EN_N				(0x1 << 2)
 #define SERDES_FIFO_FUNC_EN_N			(0x1 << 1)
 #define LS_CLK_DOMAIN_FUNC_EN_N			(0x1 << 0)
 
-/* EXYNOS_DP_VIDEO_CTL_1 */
+/* ANALOGIX_DP_VIDEO_CTL_1 */
 #define VIDEO_EN				(0x1 << 7)
 #define HDCP_VIDEO_MUTE				(0x1 << 6)
 
-/* EXYNOS_DP_VIDEO_CTL_1 */
+/* ANALOGIX_DP_VIDEO_CTL_1 */
 #define IN_D_RANGE_MASK				(0x1 << 7)
 #define IN_D_RANGE_SHIFT			(7)
 #define IN_D_RANGE_CEA				(0x1 << 7)
@@ -134,7 +134,7 @@
 #define IN_COLOR_F_YCBCR422			(0x1 << 0)
 #define IN_COLOR_F_RGB				(0x0 << 0)
 
-/* EXYNOS_DP_VIDEO_CTL_3 */
+/* ANALOGIX_DP_VIDEO_CTL_3 */
 #define IN_YC_COEFFI_MASK			(0x1 << 7)
 #define IN_YC_COEFFI_SHIFT			(7)
 #define IN_YC_COEFFI_ITU709			(0x1 << 7)
@@ -144,17 +144,17 @@
 #define VID_CHK_UPDATE_TYPE_1			(0x1 << 4)
 #define VID_CHK_UPDATE_TYPE_0			(0x0 << 4)
 
-/* EXYNOS_DP_VIDEO_CTL_8 */
+/* ANALOGIX_DP_VIDEO_CTL_8 */
 #define VID_HRES_TH(x)				(((x) & 0xf) << 4)
 #define VID_VRES_TH(x)				(((x) & 0xf) << 0)
 
-/* EXYNOS_DP_VIDEO_CTL_10 */
+/* ANALOGIX_DP_VIDEO_CTL_10 */
 #define FORMAT_SEL				(0x1 << 4)
 #define INTERACE_SCAN_CFG			(0x1 << 2)
 #define VSYNC_POLARITY_CFG			(0x1 << 1)
 #define HSYNC_POLARITY_CFG			(0x1 << 0)
 
-/* EXYNOS_DP_LANE_MAP */
+/* ANALOGIX_DP_LANE_MAP */
 #define LANE3_MAP_LOGIC_LANE_0			(0x0 << 6)
 #define LANE3_MAP_LOGIC_LANE_1			(0x1 << 6)
 #define LANE3_MAP_LOGIC_LANE_2			(0x2 << 6)
@@ -172,30 +172,30 @@
 #define LANE0_MAP_LOGIC_LANE_2			(0x2 << 0)
 #define LANE0_MAP_LOGIC_LANE_3			(0x3 << 0)
 
-/* EXYNOS_DP_ANALOG_CTL_1 */
+/* ANALOGIX_DP_ANALOG_CTL_1 */
 #define TX_TERMINAL_CTRL_50_OHM			(0x1 << 4)
 
-/* EXYNOS_DP_ANALOG_CTL_2 */
+/* ANALOGIX_DP_ANALOG_CTL_2 */
 #define SEL_24M					(0x1 << 3)
 #define TX_DVDD_BIT_1_0625V			(0x4 << 0)
 
-/* EXYNOS_DP_ANALOG_CTL_3 */
+/* ANALOGIX_DP_ANALOG_CTL_3 */
 #define DRIVE_DVDD_BIT_1_0625V			(0x4 << 5)
 #define VCO_BIT_600_MICRO			(0x5 << 0)
 
-/* EXYNOS_DP_PLL_FILTER_CTL_1 */
+/* ANALOGIX_DP_PLL_FILTER_CTL_1 */
 #define PD_RING_OSC				(0x1 << 6)
 #define AUX_TERMINAL_CTRL_50_OHM		(0x2 << 4)
 #define TX_CUR1_2X				(0x1 << 2)
 #define TX_CUR_16_MA				(0x3 << 0)
 
-/* EXYNOS_DP_TX_AMP_TUNING_CTL */
+/* ANALOGIX_DP_TX_AMP_TUNING_CTL */
 #define CH3_AMP_400_MV				(0x0 << 24)
 #define CH2_AMP_400_MV				(0x0 << 16)
 #define CH1_AMP_400_MV				(0x0 << 8)
 #define CH0_AMP_400_MV				(0x0 << 0)
 
-/* EXYNOS_DP_AUX_HW_RETRY_CTL */
+/* ANALOGIX_DP_AUX_HW_RETRY_CTL */
 #define AUX_BIT_PERIOD_EXPECTED_DELAY(x)	(((x) & 0x7) << 8)
 #define AUX_HW_RETRY_INTERVAL_MASK		(0x3 << 3)
 #define AUX_HW_RETRY_INTERVAL_600_MICROSECONDS	(0x0 << 3)
@@ -204,7 +204,7 @@
 #define AUX_HW_RETRY_INTERVAL_1800_MICROSECONDS	(0x3 << 3)
 #define AUX_HW_RETRY_COUNT_SEL(x)		(((x) & 0x7) << 0)
 
-/* EXYNOS_DP_COMMON_INT_STA_1 */
+/* ANALOGIX_DP_COMMON_INT_STA_1 */
 #define VSYNC_DET				(0x1 << 7)
 #define PLL_LOCK_CHG				(0x1 << 6)
 #define SPDIF_ERR				(0x1 << 5)
@@ -214,19 +214,19 @@
 #define VID_CLK_CHG				(0x1 << 1)
 #define SW_INT					(0x1 << 0)
 
-/* EXYNOS_DP_COMMON_INT_STA_2 */
+/* ANALOGIX_DP_COMMON_INT_STA_2 */
 #define ENC_EN_CHG				(0x1 << 6)
 #define HW_BKSV_RDY				(0x1 << 3)
 #define HW_SHA_DONE				(0x1 << 2)
 #define HW_AUTH_STATE_CHG			(0x1 << 1)
 #define HW_AUTH_DONE				(0x1 << 0)
 
-/* EXYNOS_DP_COMMON_INT_STA_3 */
+/* ANALOGIX_DP_COMMON_INT_STA_3 */
 #define AFIFO_UNDER				(0x1 << 7)
 #define AFIFO_OVER				(0x1 << 6)
 #define R0_CHK_FLAG				(0x1 << 5)
 
-/* EXYNOS_DP_COMMON_INT_STA_4 */
+/* ANALOGIX_DP_COMMON_INT_STA_4 */
 #define PSR_ACTIVE				(0x1 << 7)
 #define PSR_INACTIVE				(0x1 << 6)
 #define SPDIF_BI_PHASE_ERR			(0x1 << 5)
@@ -234,29 +234,29 @@
 #define HPD_LOST				(0x1 << 1)
 #define PLUG					(0x1 << 0)
 
-/* EXYNOS_DP_INT_STA */
+/* ANALOGIX_DP_INT_STA */
 #define INT_HPD					(0x1 << 6)
 #define HW_TRAINING_FINISH			(0x1 << 5)
 #define RPLY_RECEIV				(0x1 << 1)
 #define AUX_ERR					(0x1 << 0)
 
-/* EXYNOS_DP_INT_CTL */
+/* ANALOGIX_DP_INT_CTL */
 #define SOFT_INT_CTRL				(0x1 << 2)
 #define INT_POL1				(0x1 << 1)
 #define INT_POL0				(0x1 << 0)
 
-/* EXYNOS_DP_SYS_CTL_1 */
+/* ANALOGIX_DP_SYS_CTL_1 */
 #define DET_STA					(0x1 << 2)
 #define FORCE_DET				(0x1 << 1)
 #define DET_CTRL				(0x1 << 0)
 
-/* EXYNOS_DP_SYS_CTL_2 */
+/* ANALOGIX_DP_SYS_CTL_2 */
 #define CHA_CRI(x)				(((x) & 0xf) << 4)
 #define CHA_STA					(0x1 << 2)
 #define FORCE_CHA				(0x1 << 1)
 #define CHA_CTRL				(0x1 << 0)
 
-/* EXYNOS_DP_SYS_CTL_3 */
+/* ANALOGIX_DP_SYS_CTL_3 */
 #define HPD_STATUS				(0x1 << 6)
 #define F_HPD					(0x1 << 5)
 #define HPD_CTRL				(0x1 << 4)
@@ -265,13 +265,13 @@
 #define F_VALID					(0x1 << 1)
 #define VALID_CTRL				(0x1 << 0)
 
-/* EXYNOS_DP_SYS_CTL_4 */
+/* ANALOGIX_DP_SYS_CTL_4 */
 #define FIX_M_AUD				(0x1 << 4)
 #define ENHANCED				(0x1 << 3)
 #define FIX_M_VID				(0x1 << 2)
 #define M_VID_UPDATE_CTRL			(0x3 << 0)
 
-/* EXYNOS_DP_TRAINING_PTN_SET */
+/* ANALOGIX_DP_TRAINING_PTN_SET */
 #define SCRAMBLER_TYPE				(0x1 << 9)
 #define HW_LINK_TRAINING_PATTERN		(0x1 << 8)
 #define SCRAMBLING_DISABLE			(0x1 << 5)
@@ -285,24 +285,24 @@
 #define SW_TRAINING_PATTERN_SET_PTN1		(0x1 << 0)
 #define SW_TRAINING_PATTERN_SET_NORMAL		(0x0 << 0)
 
-/* EXYNOS_DP_LN0_LINK_TRAINING_CTL */
+/* ANALOGIX_DP_LN0_LINK_TRAINING_CTL */
 #define PRE_EMPHASIS_SET_MASK			(0x3 << 3)
 #define PRE_EMPHASIS_SET_SHIFT			(3)
 
-/* EXYNOS_DP_DEBUG_CTL */
+/* ANALOGIX_DP_DEBUG_CTL */
 #define PLL_LOCK				(0x1 << 4)
 #define F_PLL_LOCK				(0x1 << 3)
 #define PLL_LOCK_CTRL				(0x1 << 2)
 #define PN_INV					(0x1 << 0)
 
-/* EXYNOS_DP_PLL_CTL */
+/* ANALOGIX_DP_PLL_CTL */
 #define DP_PLL_PD				(0x1 << 7)
 #define DP_PLL_RESET				(0x1 << 6)
 #define DP_PLL_LOOP_BIT_DEFAULT			(0x1 << 4)
 #define DP_PLL_REF_BIT_1_1250V			(0x5 << 0)
 #define DP_PLL_REF_BIT_1_2500V			(0x7 << 0)
 
-/* EXYNOS_DP_PHY_PD */
+/* ANALOGIX_DP_PHY_PD */
 #define DP_PHY_PD				(0x1 << 5)
 #define AUX_PD					(0x1 << 4)
 #define CH3_PD					(0x1 << 3)
@@ -310,28 +310,28 @@
 #define CH1_PD					(0x1 << 1)
 #define CH0_PD					(0x1 << 0)
 
-/* EXYNOS_DP_PHY_TEST */
+/* ANALOGIX_DP_PHY_TEST */
 #define MACRO_RST				(0x1 << 5)
 #define CH1_TEST				(0x1 << 1)
 #define CH0_TEST				(0x1 << 0)
 
-/* EXYNOS_DP_AUX_CH_STA */
+/* ANALOGIX_DP_AUX_CH_STA */
 #define AUX_BUSY				(0x1 << 4)
 #define AUX_STATUS_MASK				(0xf << 0)
 
-/* EXYNOS_DP_AUX_CH_DEFER_CTL */
+/* ANALOGIX_DP_AUX_CH_DEFER_CTL */
 #define DEFER_CTRL_EN				(0x1 << 7)
 #define DEFER_COUNT(x)				(((x) & 0x7f) << 0)
 
-/* EXYNOS_DP_AUX_RX_COMM */
+/* ANALOGIX_DP_AUX_RX_COMM */
 #define AUX_RX_COMM_I2C_DEFER			(0x2 << 2)
 #define AUX_RX_COMM_AUX_DEFER			(0x2 << 0)
 
-/* EXYNOS_DP_BUFFER_DATA_CTL */
+/* ANALOGIX_DP_BUFFER_DATA_CTL */
 #define BUF_CLR					(0x1 << 7)
 #define BUF_DATA_COUNT(x)			(((x) & 0x1f) << 0)
 
-/* EXYNOS_DP_AUX_CH_CTL_1 */
+/* ANALOGIX_DP_AUX_CH_CTL_1 */
 #define AUX_LENGTH(x)				(((x - 1) & 0xf) << 4)
 #define AUX_TX_COMM_MASK			(0xf << 0)
 #define AUX_TX_COMM_DP_TRANSACTION		(0x1 << 3)
@@ -340,20 +340,20 @@
 #define AUX_TX_COMM_WRITE			(0x0 << 0)
 #define AUX_TX_COMM_READ			(0x1 << 0)
 
-/* EXYNOS_DP_AUX_ADDR_7_0 */
+/* ANALOGIX_DP_AUX_ADDR_7_0 */
 #define AUX_ADDR_7_0(x)				(((x) >> 0) & 0xff)
 
-/* EXYNOS_DP_AUX_ADDR_15_8 */
+/* ANALOGIX_DP_AUX_ADDR_15_8 */
 #define AUX_ADDR_15_8(x)			(((x) >> 8) & 0xff)
 
-/* EXYNOS_DP_AUX_ADDR_19_16 */
+/* ANALOGIX_DP_AUX_ADDR_19_16 */
 #define AUX_ADDR_19_16(x)			(((x) >> 16) & 0x0f)
 
-/* EXYNOS_DP_AUX_CH_CTL_2 */
+/* ANALOGIX_DP_AUX_CH_CTL_2 */
 #define ADDR_ONLY				(0x1 << 1)
 #define AUX_EN					(0x1 << 0)
 
-/* EXYNOS_DP_SOC_GENERAL_CTL */
+/* ANALOGIX_DP_SOC_GENERAL_CTL */
 #define AUDIO_MODE_SPDIF_MODE			(0x1 << 8)
 #define AUDIO_MODE_MASTER_MODE			(0x0 << 8)
 #define MASTER_VIDEO_INTERLACE_EN		(0x1 << 4)
@@ -363,4 +363,4 @@
 #define VIDEO_MODE_SLAVE_MODE			(0x1 << 0)
 #define VIDEO_MODE_MASTER_MODE			(0x0 << 0)
 
-#endif /* _EXYNOS_DP_REG_H */
+#endif /* _ANALOGIX_DP_REG_H */
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
index 83efca9..b3e90bd 100644
--- a/drivers/gpu/drm/exynos/Kconfig
+++ b/drivers/gpu/drm/exynos/Kconfig
@@ -71,8 +71,9 @@ config DRM_EXYNOS_DSI
 	  This enables support for Exynos MIPI-DSI device.
 
 config DRM_EXYNOS_DP
-	bool "Display Port"
+	bool "EXYNOS specific extensions for Analogix DP driver"
 	depends on DRM_EXYNOS_FIMD || DRM_EXYNOS7_DECON
+	select DRM_ANALOGIX_DP
 	default DRM_EXYNOS
 	select DRM_PANEL
 	help
diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile
index 6496532..533151b 100644
--- a/drivers/gpu/drm/exynos/Makefile
+++ b/drivers/gpu/drm/exynos/Makefile
@@ -13,7 +13,7 @@ exynosdrm-$(CONFIG_DRM_EXYNOS5433_DECON)	+= exynos5433_drm_decon.o
 exynosdrm-$(CONFIG_DRM_EXYNOS7_DECON)	+= exynos7_drm_decon.o
 exynosdrm-$(CONFIG_DRM_EXYNOS_DPI)	+= exynos_drm_dpi.o
 exynosdrm-$(CONFIG_DRM_EXYNOS_DSI)	+= exynos_drm_dsi.o
-exynosdrm-$(CONFIG_DRM_EXYNOS_DP)	+= exynos_dp_core.o exynos_dp_reg.o
+exynosdrm-$(CONFIG_DRM_EXYNOS_DP)	+= exynos_dp.o
 exynosdrm-$(CONFIG_DRM_EXYNOS_MIXER)	+= exynos_mixer.o
 exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI)	+= exynos_hdmi.o
 exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI)	+= exynos_drm_vidi.o
diff --git a/drivers/gpu/drm/exynos/exynos_dp.c b/drivers/gpu/drm/exynos/exynos_dp.c
new file mode 100644
index 0000000..942d610
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_dp.c
@@ -0,0 +1,328 @@
+/*
+ * Samsung SoC DP (Display Port) interface driver.
+ *
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ * Author: Jingoo Han <jg1.han at samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/of_graph.h>
+#include <video/of_display_timing.h>
+#include <video/of_videomode.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_panel.h>
+
+#include <drm/bridge/analogix_dp.h>
+#include <drm/exynos_drm.h>
+
+#include "exynos_drm_crtc.h"
+
+#define to_dp(nm)	container_of(nm, struct exynos_dp_device, nm)
+
+struct exynos_dp_device {
+	struct drm_encoder         encoder;
+	struct drm_connector       connector;
+	struct drm_bridge          *ptn_bridge;
+	struct drm_device          *drm_dev;
+	struct device              *dev;
+
+	struct exynos_drm_panel_info priv;
+	struct analogix_dp_plat_data plat_data;
+};
+
+int exynos_dp_crtc_clock_enable(struct analogix_dp_plat_data *plat_data,
+				bool enable)
+{
+	struct exynos_dp_device *dp = to_dp(plat_data);
+	struct drm_encoder *encoder = &dp->encoder;
+	struct exynos_drm_crtc *crtc;
+
+	if (!encoder)
+		return -1;
+
+	crtc = to_exynos_crtc(encoder->crtc);
+	if (crtc && crtc->ops && crtc->ops->clock_enable)
+		crtc->ops->clock_enable(crtc, enable);
+
+	return 0;
+}
+
+static int exynos_dp_poweron(struct analogix_dp_plat_data *plat_data)
+{
+	return exynos_dp_crtc_clock_enable(plat_data, true);
+}
+
+static int exynos_dp_poweroff(struct analogix_dp_plat_data *plat_data)
+{
+	return exynos_dp_crtc_clock_enable(plat_data, false);
+}
+
+static int exynos_dp_get_modes(struct analogix_dp_plat_data *plat_data)
+{
+	struct exynos_dp_device *dp = to_dp(plat_data);
+	struct drm_connector *connector = &dp->connector;
+	struct drm_display_mode *mode;
+	int num_modes = 0;
+
+	if (dp->plat_data.panel)
+		return num_modes;
+
+	mode = drm_mode_create(connector->dev);
+	if (!mode) {
+		DRM_ERROR("failed to create a new display mode.\n");
+		return num_modes;
+	}
+
+	drm_display_mode_from_videomode(&dp->priv.vm, mode);
+	mode->width_mm = dp->priv.width_mm;
+	mode->height_mm = dp->priv.height_mm;
+	connector->display_info.width_mm = mode->width_mm;
+	connector->display_info.height_mm = mode->height_mm;
+
+	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+	drm_mode_set_name(mode);
+	drm_mode_probed_add(connector, mode);
+
+	return num_modes + 1;
+}
+
+static int exynos_dp_bridge_attach(struct analogix_dp_plat_data *plat_data,
+				   struct drm_bridge *bridge,
+				   struct drm_connector *connector)
+{
+	struct exynos_dp_device *dp = to_dp(plat_data);
+	struct drm_encoder *encoder = &dp->encoder;
+	int ret;
+
+	drm_connector_register(connector);
+
+	/* Pre-empt DP connector creation if there's a bridge */
+	if (dp->ptn_bridge) {
+		bridge->next = dp->ptn_bridge;
+		dp->ptn_bridge->encoder = encoder;
+		ret = drm_bridge_attach(encoder->dev, dp->ptn_bridge);
+		if (ret) {
+			DRM_ERROR("Failed to attach bridge to drm\n");
+			bridge->next = NULL;
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static bool exynos_dp_mode_fixup(struct drm_encoder *encoder,
+				 const struct drm_display_mode *mode,
+				 struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static void exynos_dp_mode_set(struct drm_encoder *encoder,
+			       struct drm_display_mode *mode,
+			       struct drm_display_mode *adjusted_mode)
+{
+}
+
+static void exynos_dp_nop(struct drm_encoder *encoder)
+{
+	/* do nothing */
+}
+
+static const struct drm_encoder_helper_funcs exynos_dp_encoder_helper_funcs = {
+	.mode_fixup = exynos_dp_mode_fixup,
+	.mode_set = exynos_dp_mode_set,
+	.enable = exynos_dp_nop,
+	.disable = exynos_dp_nop,
+};
+
+static const struct drm_encoder_funcs exynos_dp_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static int exynos_dp_dt_parse_panel(struct exynos_dp_device *dp)
+{
+	int ret;
+
+	ret = of_get_videomode(dp->dev->of_node, &dp->priv.vm,
+			       OF_USE_NATIVE_MODE);
+	if (ret) {
+		DRM_ERROR("failed: of_get_videomode() : %d\n", ret);
+		return ret;
+	}
+	return 0;
+}
+
+static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
+{
+	struct exynos_dp_device *dp = dev_get_drvdata(dev);
+	struct drm_encoder *encoder = &dp->encoder;
+	struct drm_device *drm_dev = data;
+	int pipe, ret;
+
+	/*
+	 * Just like the probe function said, we don't need the
+	 * device drvrate anymore, we should leave the charge to
+	 * analogix dp driver, set the device drvdata to NULL.
+	 */
+	dev_set_drvdata(dev, NULL);
+
+	dp->dev = dev;
+	dp->drm_dev = drm_dev;
+
+	dp->plat_data.dev_type = EXYNOS_DP;
+	dp->plat_data.power_on = exynos_dp_poweron;
+	dp->plat_data.power_off = exynos_dp_poweroff;
+	dp->plat_data.attach = exynos_dp_bridge_attach;
+	dp->plat_data.get_modes = exynos_dp_get_modes;
+
+	if (!dp->plat_data.panel && !dp->ptn_bridge) {
+		ret = exynos_dp_dt_parse_panel(dp);
+		if (ret)
+			return ret;
+	}
+
+	pipe = exynos_drm_crtc_get_pipe_from_type(drm_dev,
+						  EXYNOS_DISPLAY_TYPE_LCD);
+	if (pipe < 0)
+		return pipe;
+
+	encoder->possible_crtcs = 1 << pipe;
+
+	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
+
+	drm_encoder_init(drm_dev, encoder, &exynos_dp_encoder_funcs,
+			 DRM_MODE_ENCODER_TMDS, NULL);
+
+	drm_encoder_helper_add(encoder, &exynos_dp_encoder_helper_funcs);
+
+	dp->plat_data.encoder = encoder;
+
+	return analogix_dp_bind(dev, dp->drm_dev, &dp->plat_data);
+}
+
+static void exynos_dp_unbind(struct device *dev, struct device *master,
+			     void *data)
+{
+	return analogix_dp_unbind(dev, master, data);
+}
+
+static const struct component_ops exynos_dp_ops = {
+	.bind	= exynos_dp_bind,
+	.unbind	= exynos_dp_unbind,
+};
+
+static int exynos_dp_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *panel_node = NULL, *bridge_node, *endpoint = NULL;
+	struct exynos_dp_device *dp;
+
+	dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device),
+			  GFP_KERNEL);
+	if (!dp)
+		return -ENOMEM;
+
+	/*
+	 * We just use the drvdata until driver run into component
+	 * add function, and then we would set drvdata to null, so
+	 * that analogix dp driver would take charge of the drvdata.
+	 */
+	platform_set_drvdata(pdev, dp);
+
+	panel_node = of_parse_phandle(dev->of_node, "panel", 0);
+	if (panel_node) {
+		dp->plat_data.panel = of_drm_find_panel(panel_node);
+		of_node_put(panel_node);
+		if (!dp->plat_data.panel)
+			return -EPROBE_DEFER;
+	} else {
+		endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
+		if (endpoint) {
+			panel_node = of_graph_get_remote_port_parent(endpoint);
+			if (panel_node) {
+				dp->plat_data.panel = of_drm_find_panel(panel_node);
+				of_node_put(panel_node);
+				if (!dp->plat_data.panel)
+					return -EPROBE_DEFER;
+			} else {
+				DRM_ERROR("no port node for panel device.\n");
+				return -EINVAL;
+			}
+		}
+	}
+
+	if (endpoint)
+		goto out;
+
+	endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
+	if (endpoint) {
+		bridge_node = of_graph_get_remote_port_parent(endpoint);
+		if (bridge_node) {
+			dp->ptn_bridge = of_drm_find_bridge(bridge_node);
+			of_node_put(bridge_node);
+			if (!dp->ptn_bridge)
+				return -EPROBE_DEFER;
+		} else
+			return -EPROBE_DEFER;
+	}
+
+out:
+	return component_add(&pdev->dev, &exynos_dp_ops);
+}
+
+static int exynos_dp_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &exynos_dp_ops);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int exynos_dp_suspend(struct device *dev)
+{
+	return analogix_dp_suspend(dev);
+}
+
+static int exynos_dp_resume(struct device *dev)
+{
+	return analogix_dp_resume(dev);
+}
+#endif
+
+static const struct dev_pm_ops exynos_dp_pm_ops = {
+	SET_RUNTIME_PM_OPS(exynos_dp_suspend, exynos_dp_resume, NULL)
+};
+
+static const struct of_device_id exynos_dp_match[] = {
+	{ .compatible = "samsung,exynos5-dp" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, exynos_dp_match);
+
+struct platform_driver dp_driver = {
+	.probe		= exynos_dp_probe,
+	.remove		= exynos_dp_remove,
+	.driver		= {
+		.name	= "exynos-dp",
+		.owner	= THIS_MODULE,
+		.pm	= &exynos_dp_pm_ops,
+		.of_match_table = exynos_dp_match,
+	},
+};
+
+MODULE_AUTHOR("Jingoo Han <jg1.han at samsung.com>");
+MODULE_DESCRIPTION("Samsung Specific Analogix-DP Driver Extension");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.h b/drivers/gpu/drm/exynos/exynos_dp_core.h
deleted file mode 100644
index 66eec4b..0000000
--- a/drivers/gpu/drm/exynos/exynos_dp_core.h
+++ /dev/null
@@ -1,282 +0,0 @@
-/*
- * Header file for Samsung DP (Display Port) interface driver.
- *
- * Copyright (C) 2012 Samsung Electronics Co., Ltd.
- * Author: Jingoo Han <jg1.han at samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#ifndef _EXYNOS_DP_CORE_H
-#define _EXYNOS_DP_CORE_H
-
-#include <drm/drm_crtc.h>
-#include <drm/drm_dp_helper.h>
-#include <drm/exynos_drm.h>
-
-#include "exynos_drm_drv.h"
-
-#define DP_TIMEOUT_LOOP_COUNT 100
-#define MAX_CR_LOOP 5
-#define MAX_EQ_LOOP 5
-
-enum link_rate_type {
-	LINK_RATE_1_62GBPS = 0x06,
-	LINK_RATE_2_70GBPS = 0x0a
-};
-
-enum link_lane_count_type {
-	LANE_COUNT1 = 1,
-	LANE_COUNT2 = 2,
-	LANE_COUNT4 = 4
-};
-
-enum link_training_state {
-	START,
-	CLOCK_RECOVERY,
-	EQUALIZER_TRAINING,
-	FINISHED,
-	FAILED
-};
-
-enum voltage_swing_level {
-	VOLTAGE_LEVEL_0,
-	VOLTAGE_LEVEL_1,
-	VOLTAGE_LEVEL_2,
-	VOLTAGE_LEVEL_3,
-};
-
-enum pre_emphasis_level {
-	PRE_EMPHASIS_LEVEL_0,
-	PRE_EMPHASIS_LEVEL_1,
-	PRE_EMPHASIS_LEVEL_2,
-	PRE_EMPHASIS_LEVEL_3,
-};
-
-enum pattern_set {
-	PRBS7,
-	D10_2,
-	TRAINING_PTN1,
-	TRAINING_PTN2,
-	DP_NONE
-};
-
-enum color_space {
-	COLOR_RGB,
-	COLOR_YCBCR422,
-	COLOR_YCBCR444
-};
-
-enum color_depth {
-	COLOR_6,
-	COLOR_8,
-	COLOR_10,
-	COLOR_12
-};
-
-enum color_coefficient {
-	COLOR_YCBCR601,
-	COLOR_YCBCR709
-};
-
-enum dynamic_range {
-	VESA,
-	CEA
-};
-
-enum pll_status {
-	PLL_UNLOCKED,
-	PLL_LOCKED
-};
-
-enum clock_recovery_m_value_type {
-	CALCULATED_M,
-	REGISTER_M
-};
-
-enum video_timing_recognition_type {
-	VIDEO_TIMING_FROM_CAPTURE,
-	VIDEO_TIMING_FROM_REGISTER
-};
-
-enum analog_power_block {
-	AUX_BLOCK,
-	CH0_BLOCK,
-	CH1_BLOCK,
-	CH2_BLOCK,
-	CH3_BLOCK,
-	ANALOG_TOTAL,
-	POWER_ALL
-};
-
-enum dp_irq_type {
-	DP_IRQ_TYPE_HP_CABLE_IN,
-	DP_IRQ_TYPE_HP_CABLE_OUT,
-	DP_IRQ_TYPE_HP_CHANGE,
-	DP_IRQ_TYPE_UNKNOWN,
-};
-
-struct video_info {
-	char *name;
-
-	bool h_sync_polarity;
-	bool v_sync_polarity;
-	bool interlaced;
-
-	enum color_space color_space;
-	enum dynamic_range dynamic_range;
-	enum color_coefficient ycbcr_coeff;
-	enum color_depth color_depth;
-
-	enum link_rate_type link_rate;
-	enum link_lane_count_type lane_count;
-};
-
-struct link_train {
-	int eq_loop;
-	int cr_loop[4];
-
-	u8 link_rate;
-	u8 lane_count;
-	u8 training_lane[4];
-
-	enum link_training_state lt_state;
-};
-
-struct exynos_dp_device {
-	struct drm_encoder	encoder;
-	struct device		*dev;
-	struct drm_device	*drm_dev;
-	struct drm_connector	connector;
-	struct drm_panel	*panel;
-	struct drm_bridge	*bridge;
-	struct drm_bridge	*ptn_bridge;
-	struct clk		*clock;
-	unsigned int		irq;
-	void __iomem		*reg_base;
-
-	struct video_info	*video_info;
-	struct link_train	link_train;
-	struct work_struct	hotplug_work;
-	struct phy		*phy;
-	int			dpms_mode;
-	int			hpd_gpio;
-
-	struct exynos_drm_panel_info priv;
-};
-
-/* exynos_dp_reg.c */
-void exynos_dp_enable_video_mute(struct exynos_dp_device *dp, bool enable);
-void exynos_dp_stop_video(struct exynos_dp_device *dp);
-void exynos_dp_lane_swap(struct exynos_dp_device *dp, bool enable);
-void exynos_dp_init_analog_param(struct exynos_dp_device *dp);
-void exynos_dp_init_interrupt(struct exynos_dp_device *dp);
-void exynos_dp_reset(struct exynos_dp_device *dp);
-void exynos_dp_swreset(struct exynos_dp_device *dp);
-void exynos_dp_config_interrupt(struct exynos_dp_device *dp);
-enum pll_status exynos_dp_get_pll_lock_status(struct exynos_dp_device *dp);
-void exynos_dp_set_pll_power_down(struct exynos_dp_device *dp, bool enable);
-void exynos_dp_set_analog_power_down(struct exynos_dp_device *dp,
-				enum analog_power_block block,
-				bool enable);
-void exynos_dp_init_analog_func(struct exynos_dp_device *dp);
-void exynos_dp_init_hpd(struct exynos_dp_device *dp);
-enum dp_irq_type exynos_dp_get_irq_type(struct exynos_dp_device *dp);
-void exynos_dp_clear_hotplug_interrupts(struct exynos_dp_device *dp);
-void exynos_dp_reset_aux(struct exynos_dp_device *dp);
-void exynos_dp_init_aux(struct exynos_dp_device *dp);
-int exynos_dp_get_plug_in_status(struct exynos_dp_device *dp);
-void exynos_dp_enable_sw_function(struct exynos_dp_device *dp);
-int exynos_dp_start_aux_transaction(struct exynos_dp_device *dp);
-int exynos_dp_write_byte_to_dpcd(struct exynos_dp_device *dp,
-				unsigned int reg_addr,
-				unsigned char data);
-int exynos_dp_read_byte_from_dpcd(struct exynos_dp_device *dp,
-				unsigned int reg_addr,
-				unsigned char *data);
-int exynos_dp_write_bytes_to_dpcd(struct exynos_dp_device *dp,
-				unsigned int reg_addr,
-				unsigned int count,
-				unsigned char data[]);
-int exynos_dp_read_bytes_from_dpcd(struct exynos_dp_device *dp,
-				unsigned int reg_addr,
-				unsigned int count,
-				unsigned char data[]);
-int exynos_dp_select_i2c_device(struct exynos_dp_device *dp,
-				unsigned int device_addr,
-				unsigned int reg_addr);
-int exynos_dp_read_byte_from_i2c(struct exynos_dp_device *dp,
-				unsigned int device_addr,
-				unsigned int reg_addr,
-				unsigned int *data);
-int exynos_dp_read_bytes_from_i2c(struct exynos_dp_device *dp,
-				unsigned int device_addr,
-				unsigned int reg_addr,
-				unsigned int count,
-				unsigned char edid[]);
-void exynos_dp_set_link_bandwidth(struct exynos_dp_device *dp, u32 bwtype);
-void exynos_dp_get_link_bandwidth(struct exynos_dp_device *dp, u32 *bwtype);
-void exynos_dp_set_lane_count(struct exynos_dp_device *dp, u32 count);
-void exynos_dp_get_lane_count(struct exynos_dp_device *dp, u32 *count);
-void exynos_dp_enable_enhanced_mode(struct exynos_dp_device *dp, bool enable);
-void exynos_dp_set_training_pattern(struct exynos_dp_device *dp,
-				 enum pattern_set pattern);
-void exynos_dp_set_lane0_pre_emphasis(struct exynos_dp_device *dp, u32 level);
-void exynos_dp_set_lane1_pre_emphasis(struct exynos_dp_device *dp, u32 level);
-void exynos_dp_set_lane2_pre_emphasis(struct exynos_dp_device *dp, u32 level);
-void exynos_dp_set_lane3_pre_emphasis(struct exynos_dp_device *dp, u32 level);
-void exynos_dp_set_lane0_link_training(struct exynos_dp_device *dp,
-				u32 training_lane);
-void exynos_dp_set_lane1_link_training(struct exynos_dp_device *dp,
-				u32 training_lane);
-void exynos_dp_set_lane2_link_training(struct exynos_dp_device *dp,
-				u32 training_lane);
-void exynos_dp_set_lane3_link_training(struct exynos_dp_device *dp,
-				u32 training_lane);
-u32 exynos_dp_get_lane0_link_training(struct exynos_dp_device *dp);
-u32 exynos_dp_get_lane1_link_training(struct exynos_dp_device *dp);
-u32 exynos_dp_get_lane2_link_training(struct exynos_dp_device *dp);
-u32 exynos_dp_get_lane3_link_training(struct exynos_dp_device *dp);
-void exynos_dp_reset_macro(struct exynos_dp_device *dp);
-void exynos_dp_init_video(struct exynos_dp_device *dp);
-
-void exynos_dp_set_video_color_format(struct exynos_dp_device *dp);
-int exynos_dp_is_slave_video_stream_clock_on(struct exynos_dp_device *dp);
-void exynos_dp_set_video_cr_mn(struct exynos_dp_device *dp,
-			enum clock_recovery_m_value_type type,
-			u32 m_value,
-			u32 n_value);
-void exynos_dp_set_video_timing_mode(struct exynos_dp_device *dp, u32 type);
-void exynos_dp_enable_video_master(struct exynos_dp_device *dp, bool enable);
-void exynos_dp_start_video(struct exynos_dp_device *dp);
-int exynos_dp_is_video_stream_on(struct exynos_dp_device *dp);
-void exynos_dp_config_video_slave_mode(struct exynos_dp_device *dp);
-void exynos_dp_enable_scrambling(struct exynos_dp_device *dp);
-void exynos_dp_disable_scrambling(struct exynos_dp_device *dp);
-
-/* I2C EDID Chip ID, Slave Address */
-#define I2C_EDID_DEVICE_ADDR			0x50
-#define I2C_E_EDID_DEVICE_ADDR			0x30
-
-#define EDID_BLOCK_LENGTH			0x80
-#define EDID_HEADER_PATTERN			0x00
-#define EDID_EXTENSION_FLAG			0x7e
-#define EDID_CHECKSUM				0x7f
-
-/* DP_MAX_LANE_COUNT */
-#define DPCD_ENHANCED_FRAME_CAP(x)		(((x) >> 7) & 0x1)
-#define DPCD_MAX_LANE_COUNT(x)			((x) & 0x1f)
-
-/* DP_LANE_COUNT_SET */
-#define DPCD_LANE_COUNT_SET(x)			((x) & 0x1f)
-
-/* DP_TRAINING_LANE0_SET */
-#define DPCD_PRE_EMPHASIS_SET(x)		(((x) & 0x3) << 3)
-#define DPCD_PRE_EMPHASIS_GET(x)		(((x) >> 3) & 0x3)
-#define DPCD_VOLTAGE_SWING_SET(x)		(((x) & 0x3) << 0)
-#define DPCD_VOLTAGE_SWING_GET(x)		(((x) >> 0) & 0x3)
-
-#endif /* _EXYNOS_DP_CORE_H */
diff --git a/drivers/gpu/drm/exynos/exynos_dp_reg.c b/drivers/gpu/drm/exynos/exynos_dp_reg.c
deleted file mode 100644
index c1f87a2..0000000
--- a/drivers/gpu/drm/exynos/exynos_dp_reg.c
+++ /dev/null
@@ -1,1263 +0,0 @@
-/*
- * Samsung DP (Display port) register interface driver.
- *
- * Copyright (C) 2012 Samsung Electronics Co., Ltd.
- * Author: Jingoo Han <jg1.han at samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#include <linux/device.h>
-#include <linux/io.h>
-#include <linux/delay.h>
-#include <linux/gpio.h>
-
-#include "exynos_dp_core.h"
-#include "exynos_dp_reg.h"
-
-#define COMMON_INT_MASK_1	0
-#define COMMON_INT_MASK_2	0
-#define COMMON_INT_MASK_3	0
-#define COMMON_INT_MASK_4	(HOTPLUG_CHG | HPD_LOST | PLUG)
-#define INT_STA_MASK		INT_HPD
-
-void exynos_dp_enable_video_mute(struct exynos_dp_device *dp, bool enable)
-{
-	u32 reg;
-
-	if (enable) {
-		reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_1);
-		reg |= HDCP_VIDEO_MUTE;
-		writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_1);
-	} else {
-		reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_1);
-		reg &= ~HDCP_VIDEO_MUTE;
-		writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_1);
-	}
-}
-
-void exynos_dp_stop_video(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_1);
-	reg &= ~VIDEO_EN;
-	writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_1);
-}
-
-void exynos_dp_lane_swap(struct exynos_dp_device *dp, bool enable)
-{
-	u32 reg;
-
-	if (enable)
-		reg = LANE3_MAP_LOGIC_LANE_0 | LANE2_MAP_LOGIC_LANE_1 |
-			LANE1_MAP_LOGIC_LANE_2 | LANE0_MAP_LOGIC_LANE_3;
-	else
-		reg = LANE3_MAP_LOGIC_LANE_3 | LANE2_MAP_LOGIC_LANE_2 |
-			LANE1_MAP_LOGIC_LANE_1 | LANE0_MAP_LOGIC_LANE_0;
-
-	writel(reg, dp->reg_base + EXYNOS_DP_LANE_MAP);
-}
-
-void exynos_dp_init_analog_param(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = TX_TERMINAL_CTRL_50_OHM;
-	writel(reg, dp->reg_base + EXYNOS_DP_ANALOG_CTL_1);
-
-	reg = SEL_24M | TX_DVDD_BIT_1_0625V;
-	writel(reg, dp->reg_base + EXYNOS_DP_ANALOG_CTL_2);
-
-	reg = DRIVE_DVDD_BIT_1_0625V | VCO_BIT_600_MICRO;
-	writel(reg, dp->reg_base + EXYNOS_DP_ANALOG_CTL_3);
-
-	reg = PD_RING_OSC | AUX_TERMINAL_CTRL_50_OHM |
-		TX_CUR1_2X | TX_CUR_16_MA;
-	writel(reg, dp->reg_base + EXYNOS_DP_PLL_FILTER_CTL_1);
-
-	reg = CH3_AMP_400_MV | CH2_AMP_400_MV |
-		CH1_AMP_400_MV | CH0_AMP_400_MV;
-	writel(reg, dp->reg_base + EXYNOS_DP_TX_AMP_TUNING_CTL);
-}
-
-void exynos_dp_init_interrupt(struct exynos_dp_device *dp)
-{
-	/* Set interrupt pin assertion polarity as high */
-	writel(INT_POL1 | INT_POL0, dp->reg_base + EXYNOS_DP_INT_CTL);
-
-	/* Clear pending regisers */
-	writel(0xff, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_1);
-	writel(0x4f, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_2);
-	writel(0xe0, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_3);
-	writel(0xe7, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4);
-	writel(0x63, dp->reg_base + EXYNOS_DP_INT_STA);
-
-	/* 0:mask,1: unmask */
-	writel(0x00, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_1);
-	writel(0x00, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_2);
-	writel(0x00, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_3);
-	writel(0x00, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_4);
-	writel(0x00, dp->reg_base + EXYNOS_DP_INT_STA_MASK);
-}
-
-void exynos_dp_reset(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	exynos_dp_stop_video(dp);
-	exynos_dp_enable_video_mute(dp, 0);
-
-	reg = MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N |
-		AUD_FIFO_FUNC_EN_N | AUD_FUNC_EN_N |
-		HDCP_FUNC_EN_N | SW_FUNC_EN_N;
-	writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_1);
-
-	reg = SSC_FUNC_EN_N | AUX_FUNC_EN_N |
-		SERDES_FIFO_FUNC_EN_N |
-		LS_CLK_DOMAIN_FUNC_EN_N;
-	writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_2);
-
-	usleep_range(20, 30);
-
-	exynos_dp_lane_swap(dp, 0);
-
-	writel(0x0, dp->reg_base + EXYNOS_DP_SYS_CTL_1);
-	writel(0x40, dp->reg_base + EXYNOS_DP_SYS_CTL_2);
-	writel(0x0, dp->reg_base + EXYNOS_DP_SYS_CTL_3);
-	writel(0x0, dp->reg_base + EXYNOS_DP_SYS_CTL_4);
-
-	writel(0x0, dp->reg_base + EXYNOS_DP_PKT_SEND_CTL);
-	writel(0x0, dp->reg_base + EXYNOS_DP_HDCP_CTL);
-
-	writel(0x5e, dp->reg_base + EXYNOS_DP_HPD_DEGLITCH_L);
-	writel(0x1a, dp->reg_base + EXYNOS_DP_HPD_DEGLITCH_H);
-
-	writel(0x10, dp->reg_base + EXYNOS_DP_LINK_DEBUG_CTL);
-
-	writel(0x0, dp->reg_base + EXYNOS_DP_PHY_TEST);
-
-	writel(0x0, dp->reg_base + EXYNOS_DP_VIDEO_FIFO_THRD);
-	writel(0x20, dp->reg_base + EXYNOS_DP_AUDIO_MARGIN);
-
-	writel(0x4, dp->reg_base + EXYNOS_DP_M_VID_GEN_FILTER_TH);
-	writel(0x2, dp->reg_base + EXYNOS_DP_M_AUD_GEN_FILTER_TH);
-
-	writel(0x00000101, dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL);
-}
-
-void exynos_dp_swreset(struct exynos_dp_device *dp)
-{
-	writel(RESET_DP_TX, dp->reg_base + EXYNOS_DP_TX_SW_RESET);
-}
-
-void exynos_dp_config_interrupt(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	/* 0: mask, 1: unmask */
-	reg = COMMON_INT_MASK_1;
-	writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_1);
-
-	reg = COMMON_INT_MASK_2;
-	writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_2);
-
-	reg = COMMON_INT_MASK_3;
-	writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_3);
-
-	reg = COMMON_INT_MASK_4;
-	writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_4);
-
-	reg = INT_STA_MASK;
-	writel(reg, dp->reg_base + EXYNOS_DP_INT_STA_MASK);
-}
-
-enum pll_status exynos_dp_get_pll_lock_status(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_DEBUG_CTL);
-	if (reg & PLL_LOCK)
-		return PLL_LOCKED;
-	else
-		return PLL_UNLOCKED;
-}
-
-void exynos_dp_set_pll_power_down(struct exynos_dp_device *dp, bool enable)
-{
-	u32 reg;
-
-	if (enable) {
-		reg = readl(dp->reg_base + EXYNOS_DP_PLL_CTL);
-		reg |= DP_PLL_PD;
-		writel(reg, dp->reg_base + EXYNOS_DP_PLL_CTL);
-	} else {
-		reg = readl(dp->reg_base + EXYNOS_DP_PLL_CTL);
-		reg &= ~DP_PLL_PD;
-		writel(reg, dp->reg_base + EXYNOS_DP_PLL_CTL);
-	}
-}
-
-void exynos_dp_set_analog_power_down(struct exynos_dp_device *dp,
-				enum analog_power_block block,
-				bool enable)
-{
-	u32 reg;
-
-	switch (block) {
-	case AUX_BLOCK:
-		if (enable) {
-			reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
-			reg |= AUX_PD;
-			writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
-		} else {
-			reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
-			reg &= ~AUX_PD;
-			writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
-		}
-		break;
-	case CH0_BLOCK:
-		if (enable) {
-			reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
-			reg |= CH0_PD;
-			writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
-		} else {
-			reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
-			reg &= ~CH0_PD;
-			writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
-		}
-		break;
-	case CH1_BLOCK:
-		if (enable) {
-			reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
-			reg |= CH1_PD;
-			writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
-		} else {
-			reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
-			reg &= ~CH1_PD;
-			writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
-		}
-		break;
-	case CH2_BLOCK:
-		if (enable) {
-			reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
-			reg |= CH2_PD;
-			writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
-		} else {
-			reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
-			reg &= ~CH2_PD;
-			writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
-		}
-		break;
-	case CH3_BLOCK:
-		if (enable) {
-			reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
-			reg |= CH3_PD;
-			writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
-		} else {
-			reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
-			reg &= ~CH3_PD;
-			writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
-		}
-		break;
-	case ANALOG_TOTAL:
-		if (enable) {
-			reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
-			reg |= DP_PHY_PD;
-			writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
-		} else {
-			reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
-			reg &= ~DP_PHY_PD;
-			writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
-		}
-		break;
-	case POWER_ALL:
-		if (enable) {
-			reg = DP_PHY_PD | AUX_PD | CH3_PD | CH2_PD |
-				CH1_PD | CH0_PD;
-			writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
-		} else {
-			writel(0x00, dp->reg_base + EXYNOS_DP_PHY_PD);
-		}
-		break;
-	default:
-		break;
-	}
-}
-
-void exynos_dp_init_analog_func(struct exynos_dp_device *dp)
-{
-	u32 reg;
-	int timeout_loop = 0;
-
-	exynos_dp_set_analog_power_down(dp, POWER_ALL, 0);
-
-	reg = PLL_LOCK_CHG;
-	writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_1);
-
-	reg = readl(dp->reg_base + EXYNOS_DP_DEBUG_CTL);
-	reg &= ~(F_PLL_LOCK | PLL_LOCK_CTRL);
-	writel(reg, dp->reg_base + EXYNOS_DP_DEBUG_CTL);
-
-	/* Power up PLL */
-	if (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
-		exynos_dp_set_pll_power_down(dp, 0);
-
-		while (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
-			timeout_loop++;
-			if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) {
-				dev_err(dp->dev, "failed to get pll lock status\n");
-				return;
-			}
-			usleep_range(10, 20);
-		}
-	}
-
-	/* Enable Serdes FIFO function and Link symbol clock domain module */
-	reg = readl(dp->reg_base + EXYNOS_DP_FUNC_EN_2);
-	reg &= ~(SERDES_FIFO_FUNC_EN_N | LS_CLK_DOMAIN_FUNC_EN_N
-		| AUX_FUNC_EN_N);
-	writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_2);
-}
-
-void exynos_dp_clear_hotplug_interrupts(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	if (gpio_is_valid(dp->hpd_gpio))
-		return;
-
-	reg = HOTPLUG_CHG | HPD_LOST | PLUG;
-	writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4);
-
-	reg = INT_HPD;
-	writel(reg, dp->reg_base + EXYNOS_DP_INT_STA);
-}
-
-void exynos_dp_init_hpd(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	if (gpio_is_valid(dp->hpd_gpio))
-		return;
-
-	exynos_dp_clear_hotplug_interrupts(dp);
-
-	reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
-	reg &= ~(F_HPD | HPD_CTRL);
-	writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_3);
-}
-
-enum dp_irq_type exynos_dp_get_irq_type(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	if (gpio_is_valid(dp->hpd_gpio)) {
-		reg = gpio_get_value(dp->hpd_gpio);
-		if (reg)
-			return DP_IRQ_TYPE_HP_CABLE_IN;
-		else
-			return DP_IRQ_TYPE_HP_CABLE_OUT;
-	} else {
-		/* Parse hotplug interrupt status register */
-		reg = readl(dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4);
-
-		if (reg & PLUG)
-			return DP_IRQ_TYPE_HP_CABLE_IN;
-
-		if (reg & HPD_LOST)
-			return DP_IRQ_TYPE_HP_CABLE_OUT;
-
-		if (reg & HOTPLUG_CHG)
-			return DP_IRQ_TYPE_HP_CHANGE;
-
-		return DP_IRQ_TYPE_UNKNOWN;
-	}
-}
-
-void exynos_dp_reset_aux(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	/* Disable AUX channel module */
-	reg = readl(dp->reg_base + EXYNOS_DP_FUNC_EN_2);
-	reg |= AUX_FUNC_EN_N;
-	writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_2);
-}
-
-void exynos_dp_init_aux(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	/* Clear inerrupts related to AUX channel */
-	reg = RPLY_RECEIV | AUX_ERR;
-	writel(reg, dp->reg_base + EXYNOS_DP_INT_STA);
-
-	exynos_dp_reset_aux(dp);
-
-	/* Disable AUX transaction H/W retry */
-	reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3) | AUX_HW_RETRY_COUNT_SEL(0)|
-		AUX_HW_RETRY_INTERVAL_600_MICROSECONDS;
-	writel(reg, dp->reg_base + EXYNOS_DP_AUX_HW_RETRY_CTL);
-
-	/* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */
-	reg = DEFER_CTRL_EN | DEFER_COUNT(1);
-	writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_DEFER_CTL);
-
-	/* Enable AUX channel module */
-	reg = readl(dp->reg_base + EXYNOS_DP_FUNC_EN_2);
-	reg &= ~AUX_FUNC_EN_N;
-	writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_2);
-}
-
-int exynos_dp_get_plug_in_status(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	if (gpio_is_valid(dp->hpd_gpio)) {
-		if (gpio_get_value(dp->hpd_gpio))
-			return 0;
-	} else {
-		reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
-		if (reg & HPD_STATUS)
-			return 0;
-	}
-
-	return -EINVAL;
-}
-
-void exynos_dp_enable_sw_function(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_FUNC_EN_1);
-	reg &= ~SW_FUNC_EN_N;
-	writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_1);
-}
-
-int exynos_dp_start_aux_transaction(struct exynos_dp_device *dp)
-{
-	int reg;
-	int retval = 0;
-	int timeout_loop = 0;
-
-	/* Enable AUX CH operation */
-	reg = readl(dp->reg_base + EXYNOS_DP_AUX_CH_CTL_2);
-	reg |= AUX_EN;
-	writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_2);
-
-	/* Is AUX CH command reply received? */
-	reg = readl(dp->reg_base + EXYNOS_DP_INT_STA);
-	while (!(reg & RPLY_RECEIV)) {
-		timeout_loop++;
-		if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) {
-			dev_err(dp->dev, "AUX CH command reply failed!\n");
-			return -ETIMEDOUT;
-		}
-		reg = readl(dp->reg_base + EXYNOS_DP_INT_STA);
-		usleep_range(10, 11);
-	}
-
-	/* Clear interrupt source for AUX CH command reply */
-	writel(RPLY_RECEIV, dp->reg_base + EXYNOS_DP_INT_STA);
-
-	/* Clear interrupt source for AUX CH access error */
-	reg = readl(dp->reg_base + EXYNOS_DP_INT_STA);
-	if (reg & AUX_ERR) {
-		writel(AUX_ERR, dp->reg_base + EXYNOS_DP_INT_STA);
-		return -EREMOTEIO;
-	}
-
-	/* Check AUX CH error access status */
-	reg = readl(dp->reg_base + EXYNOS_DP_AUX_CH_STA);
-	if ((reg & AUX_STATUS_MASK) != 0) {
-		dev_err(dp->dev, "AUX CH error happens: %d\n\n",
-			reg & AUX_STATUS_MASK);
-		return -EREMOTEIO;
-	}
-
-	return retval;
-}
-
-int exynos_dp_write_byte_to_dpcd(struct exynos_dp_device *dp,
-				unsigned int reg_addr,
-				unsigned char data)
-{
-	u32 reg;
-	int i;
-	int retval;
-
-	for (i = 0; i < 3; i++) {
-		/* Clear AUX CH data buffer */
-		reg = BUF_CLR;
-		writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL);
-
-		/* Select DPCD device address */
-		reg = AUX_ADDR_7_0(reg_addr);
-		writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0);
-		reg = AUX_ADDR_15_8(reg_addr);
-		writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_15_8);
-		reg = AUX_ADDR_19_16(reg_addr);
-		writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_19_16);
-
-		/* Write data buffer */
-		reg = (unsigned int)data;
-		writel(reg, dp->reg_base + EXYNOS_DP_BUF_DATA_0);
-
-		/*
-		 * Set DisplayPort transaction and write 1 byte
-		 * If bit 3 is 1, DisplayPort transaction.
-		 * If Bit 3 is 0, I2C transaction.
-		 */
-		reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE;
-		writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1);
-
-		/* Start AUX transaction */
-		retval = exynos_dp_start_aux_transaction(dp);
-		if (retval == 0)
-			break;
-		else
-			dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
-				__func__);
-	}
-
-	return retval;
-}
-
-int exynos_dp_read_byte_from_dpcd(struct exynos_dp_device *dp,
-				unsigned int reg_addr,
-				unsigned char *data)
-{
-	u32 reg;
-	int i;
-	int retval;
-
-	for (i = 0; i < 3; i++) {
-		/* Clear AUX CH data buffer */
-		reg = BUF_CLR;
-		writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL);
-
-		/* Select DPCD device address */
-		reg = AUX_ADDR_7_0(reg_addr);
-		writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0);
-		reg = AUX_ADDR_15_8(reg_addr);
-		writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_15_8);
-		reg = AUX_ADDR_19_16(reg_addr);
-		writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_19_16);
-
-		/*
-		 * Set DisplayPort transaction and read 1 byte
-		 * If bit 3 is 1, DisplayPort transaction.
-		 * If Bit 3 is 0, I2C transaction.
-		 */
-		reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ;
-		writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1);
-
-		/* Start AUX transaction */
-		retval = exynos_dp_start_aux_transaction(dp);
-		if (retval == 0)
-			break;
-		else
-			dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
-				__func__);
-	}
-
-	/* Read data buffer */
-	reg = readl(dp->reg_base + EXYNOS_DP_BUF_DATA_0);
-	*data = (unsigned char)(reg & 0xff);
-
-	return retval;
-}
-
-int exynos_dp_write_bytes_to_dpcd(struct exynos_dp_device *dp,
-				unsigned int reg_addr,
-				unsigned int count,
-				unsigned char data[])
-{
-	u32 reg;
-	unsigned int start_offset;
-	unsigned int cur_data_count;
-	unsigned int cur_data_idx;
-	int i;
-	int retval = 0;
-
-	/* Clear AUX CH data buffer */
-	reg = BUF_CLR;
-	writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL);
-
-	start_offset = 0;
-	while (start_offset < count) {
-		/* Buffer size of AUX CH is 16 * 4bytes */
-		if ((count - start_offset) > 16)
-			cur_data_count = 16;
-		else
-			cur_data_count = count - start_offset;
-
-		for (i = 0; i < 3; i++) {
-			/* Select DPCD device address */
-			reg = AUX_ADDR_7_0(reg_addr + start_offset);
-			writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0);
-			reg = AUX_ADDR_15_8(reg_addr + start_offset);
-			writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_15_8);
-			reg = AUX_ADDR_19_16(reg_addr + start_offset);
-			writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_19_16);
-
-			for (cur_data_idx = 0; cur_data_idx < cur_data_count;
-			     cur_data_idx++) {
-				reg = data[start_offset + cur_data_idx];
-				writel(reg, dp->reg_base + EXYNOS_DP_BUF_DATA_0
-							  + 4 * cur_data_idx);
-			}
-
-			/*
-			 * Set DisplayPort transaction and write
-			 * If bit 3 is 1, DisplayPort transaction.
-			 * If Bit 3 is 0, I2C transaction.
-			 */
-			reg = AUX_LENGTH(cur_data_count) |
-				AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE;
-			writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1);
-
-			/* Start AUX transaction */
-			retval = exynos_dp_start_aux_transaction(dp);
-			if (retval == 0)
-				break;
-			else
-				dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
-					__func__);
-		}
-
-		start_offset += cur_data_count;
-	}
-
-	return retval;
-}
-
-int exynos_dp_read_bytes_from_dpcd(struct exynos_dp_device *dp,
-				unsigned int reg_addr,
-				unsigned int count,
-				unsigned char data[])
-{
-	u32 reg;
-	unsigned int start_offset;
-	unsigned int cur_data_count;
-	unsigned int cur_data_idx;
-	int i;
-	int retval = 0;
-
-	/* Clear AUX CH data buffer */
-	reg = BUF_CLR;
-	writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL);
-
-	start_offset = 0;
-	while (start_offset < count) {
-		/* Buffer size of AUX CH is 16 * 4bytes */
-		if ((count - start_offset) > 16)
-			cur_data_count = 16;
-		else
-			cur_data_count = count - start_offset;
-
-		/* AUX CH Request Transaction process */
-		for (i = 0; i < 3; i++) {
-			/* Select DPCD device address */
-			reg = AUX_ADDR_7_0(reg_addr + start_offset);
-			writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0);
-			reg = AUX_ADDR_15_8(reg_addr + start_offset);
-			writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_15_8);
-			reg = AUX_ADDR_19_16(reg_addr + start_offset);
-			writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_19_16);
-
-			/*
-			 * Set DisplayPort transaction and read
-			 * If bit 3 is 1, DisplayPort transaction.
-			 * If Bit 3 is 0, I2C transaction.
-			 */
-			reg = AUX_LENGTH(cur_data_count) |
-				AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ;
-			writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1);
-
-			/* Start AUX transaction */
-			retval = exynos_dp_start_aux_transaction(dp);
-			if (retval == 0)
-				break;
-			else
-				dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
-					__func__);
-		}
-
-		for (cur_data_idx = 0; cur_data_idx < cur_data_count;
-		    cur_data_idx++) {
-			reg = readl(dp->reg_base + EXYNOS_DP_BUF_DATA_0
-						 + 4 * cur_data_idx);
-			data[start_offset + cur_data_idx] =
-				(unsigned char)reg;
-		}
-
-		start_offset += cur_data_count;
-	}
-
-	return retval;
-}
-
-int exynos_dp_select_i2c_device(struct exynos_dp_device *dp,
-				unsigned int device_addr,
-				unsigned int reg_addr)
-{
-	u32 reg;
-	int retval;
-
-	/* Set EDID device address */
-	reg = device_addr;
-	writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0);
-	writel(0x0, dp->reg_base + EXYNOS_DP_AUX_ADDR_15_8);
-	writel(0x0, dp->reg_base + EXYNOS_DP_AUX_ADDR_19_16);
-
-	/* Set offset from base address of EDID device */
-	writel(reg_addr, dp->reg_base + EXYNOS_DP_BUF_DATA_0);
-
-	/*
-	 * Set I2C transaction and write address
-	 * If bit 3 is 1, DisplayPort transaction.
-	 * If Bit 3 is 0, I2C transaction.
-	 */
-	reg = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_MOT |
-		AUX_TX_COMM_WRITE;
-	writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1);
-
-	/* Start AUX transaction */
-	retval = exynos_dp_start_aux_transaction(dp);
-	if (retval != 0)
-		dev_dbg(dp->dev, "%s: Aux Transaction fail!\n", __func__);
-
-	return retval;
-}
-
-int exynos_dp_read_byte_from_i2c(struct exynos_dp_device *dp,
-				unsigned int device_addr,
-				unsigned int reg_addr,
-				unsigned int *data)
-{
-	u32 reg;
-	int i;
-	int retval;
-
-	for (i = 0; i < 3; i++) {
-		/* Clear AUX CH data buffer */
-		reg = BUF_CLR;
-		writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL);
-
-		/* Select EDID device */
-		retval = exynos_dp_select_i2c_device(dp, device_addr, reg_addr);
-		if (retval != 0)
-			continue;
-
-		/*
-		 * Set I2C transaction and read data
-		 * If bit 3 is 1, DisplayPort transaction.
-		 * If Bit 3 is 0, I2C transaction.
-		 */
-		reg = AUX_TX_COMM_I2C_TRANSACTION |
-			AUX_TX_COMM_READ;
-		writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1);
-
-		/* Start AUX transaction */
-		retval = exynos_dp_start_aux_transaction(dp);
-		if (retval == 0)
-			break;
-		else
-			dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
-				__func__);
-	}
-
-	/* Read data */
-	if (retval == 0)
-		*data = readl(dp->reg_base + EXYNOS_DP_BUF_DATA_0);
-
-	return retval;
-}
-
-int exynos_dp_read_bytes_from_i2c(struct exynos_dp_device *dp,
-				unsigned int device_addr,
-				unsigned int reg_addr,
-				unsigned int count,
-				unsigned char edid[])
-{
-	u32 reg;
-	unsigned int i, j;
-	unsigned int cur_data_idx;
-	unsigned int defer = 0;
-	int retval = 0;
-
-	for (i = 0; i < count; i += 16) {
-		for (j = 0; j < 3; j++) {
-			/* Clear AUX CH data buffer */
-			reg = BUF_CLR;
-			writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL);
-
-			/* Set normal AUX CH command */
-			reg = readl(dp->reg_base + EXYNOS_DP_AUX_CH_CTL_2);
-			reg &= ~ADDR_ONLY;
-			writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_2);
-
-			/*
-			 * If Rx sends defer, Tx sends only reads
-			 * request without sending address
-			 */
-			if (!defer)
-				retval = exynos_dp_select_i2c_device(dp,
-						device_addr, reg_addr + i);
-			else
-				defer = 0;
-
-			if (retval == 0) {
-				/*
-				 * Set I2C transaction and write data
-				 * If bit 3 is 1, DisplayPort transaction.
-				 * If Bit 3 is 0, I2C transaction.
-				 */
-				reg = AUX_LENGTH(16) |
-					AUX_TX_COMM_I2C_TRANSACTION |
-					AUX_TX_COMM_READ;
-				writel(reg, dp->reg_base +
-					EXYNOS_DP_AUX_CH_CTL_1);
-
-				/* Start AUX transaction */
-				retval = exynos_dp_start_aux_transaction(dp);
-				if (retval == 0)
-					break;
-				else
-					dev_dbg(dp->dev,
-						"%s: Aux Transaction fail!\n",
-						__func__);
-			}
-			/* Check if Rx sends defer */
-			reg = readl(dp->reg_base + EXYNOS_DP_AUX_RX_COMM);
-			if (reg == AUX_RX_COMM_AUX_DEFER ||
-				reg == AUX_RX_COMM_I2C_DEFER) {
-				dev_err(dp->dev, "Defer: %d\n\n", reg);
-				defer = 1;
-			}
-		}
-
-		for (cur_data_idx = 0; cur_data_idx < 16; cur_data_idx++) {
-			reg = readl(dp->reg_base + EXYNOS_DP_BUF_DATA_0
-						 + 4 * cur_data_idx);
-			edid[i + cur_data_idx] = (unsigned char)reg;
-		}
-	}
-
-	return retval;
-}
-
-void exynos_dp_set_link_bandwidth(struct exynos_dp_device *dp, u32 bwtype)
-{
-	u32 reg;
-
-	reg = bwtype;
-	if ((bwtype == LINK_RATE_2_70GBPS) || (bwtype == LINK_RATE_1_62GBPS))
-		writel(reg, dp->reg_base + EXYNOS_DP_LINK_BW_SET);
-}
-
-void exynos_dp_get_link_bandwidth(struct exynos_dp_device *dp, u32 *bwtype)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_LINK_BW_SET);
-	*bwtype = reg;
-}
-
-void exynos_dp_set_lane_count(struct exynos_dp_device *dp, u32 count)
-{
-	u32 reg;
-
-	reg = count;
-	writel(reg, dp->reg_base + EXYNOS_DP_LANE_COUNT_SET);
-}
-
-void exynos_dp_get_lane_count(struct exynos_dp_device *dp, u32 *count)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_LANE_COUNT_SET);
-	*count = reg;
-}
-
-void exynos_dp_enable_enhanced_mode(struct exynos_dp_device *dp, bool enable)
-{
-	u32 reg;
-
-	if (enable) {
-		reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_4);
-		reg |= ENHANCED;
-		writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_4);
-	} else {
-		reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_4);
-		reg &= ~ENHANCED;
-		writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_4);
-	}
-}
-
-void exynos_dp_set_training_pattern(struct exynos_dp_device *dp,
-				 enum pattern_set pattern)
-{
-	u32 reg;
-
-	switch (pattern) {
-	case PRBS7:
-		reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_PRBS7;
-		writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET);
-		break;
-	case D10_2:
-		reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_D10_2;
-		writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET);
-		break;
-	case TRAINING_PTN1:
-		reg = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN1;
-		writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET);
-		break;
-	case TRAINING_PTN2:
-		reg = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN2;
-		writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET);
-		break;
-	case DP_NONE:
-		reg = SCRAMBLING_ENABLE |
-			LINK_QUAL_PATTERN_SET_DISABLE |
-			SW_TRAINING_PATTERN_SET_NORMAL;
-		writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET);
-		break;
-	default:
-		break;
-	}
-}
-
-void exynos_dp_set_lane0_pre_emphasis(struct exynos_dp_device *dp, u32 level)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_LN0_LINK_TRAINING_CTL);
-	reg &= ~PRE_EMPHASIS_SET_MASK;
-	reg |= level << PRE_EMPHASIS_SET_SHIFT;
-	writel(reg, dp->reg_base + EXYNOS_DP_LN0_LINK_TRAINING_CTL);
-}
-
-void exynos_dp_set_lane1_pre_emphasis(struct exynos_dp_device *dp, u32 level)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_LN1_LINK_TRAINING_CTL);
-	reg &= ~PRE_EMPHASIS_SET_MASK;
-	reg |= level << PRE_EMPHASIS_SET_SHIFT;
-	writel(reg, dp->reg_base + EXYNOS_DP_LN1_LINK_TRAINING_CTL);
-}
-
-void exynos_dp_set_lane2_pre_emphasis(struct exynos_dp_device *dp, u32 level)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_LN2_LINK_TRAINING_CTL);
-	reg &= ~PRE_EMPHASIS_SET_MASK;
-	reg |= level << PRE_EMPHASIS_SET_SHIFT;
-	writel(reg, dp->reg_base + EXYNOS_DP_LN2_LINK_TRAINING_CTL);
-}
-
-void exynos_dp_set_lane3_pre_emphasis(struct exynos_dp_device *dp, u32 level)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_LN3_LINK_TRAINING_CTL);
-	reg &= ~PRE_EMPHASIS_SET_MASK;
-	reg |= level << PRE_EMPHASIS_SET_SHIFT;
-	writel(reg, dp->reg_base + EXYNOS_DP_LN3_LINK_TRAINING_CTL);
-}
-
-void exynos_dp_set_lane0_link_training(struct exynos_dp_device *dp,
-					u32 training_lane)
-{
-	u32 reg;
-
-	reg = training_lane;
-	writel(reg, dp->reg_base + EXYNOS_DP_LN0_LINK_TRAINING_CTL);
-}
-
-void exynos_dp_set_lane1_link_training(struct exynos_dp_device *dp,
-					u32 training_lane)
-{
-	u32 reg;
-
-	reg = training_lane;
-	writel(reg, dp->reg_base + EXYNOS_DP_LN1_LINK_TRAINING_CTL);
-}
-
-void exynos_dp_set_lane2_link_training(struct exynos_dp_device *dp,
-					u32 training_lane)
-{
-	u32 reg;
-
-	reg = training_lane;
-	writel(reg, dp->reg_base + EXYNOS_DP_LN2_LINK_TRAINING_CTL);
-}
-
-void exynos_dp_set_lane3_link_training(struct exynos_dp_device *dp,
-					u32 training_lane)
-{
-	u32 reg;
-
-	reg = training_lane;
-	writel(reg, dp->reg_base + EXYNOS_DP_LN3_LINK_TRAINING_CTL);
-}
-
-u32 exynos_dp_get_lane0_link_training(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_LN0_LINK_TRAINING_CTL);
-	return reg;
-}
-
-u32 exynos_dp_get_lane1_link_training(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_LN1_LINK_TRAINING_CTL);
-	return reg;
-}
-
-u32 exynos_dp_get_lane2_link_training(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_LN2_LINK_TRAINING_CTL);
-	return reg;
-}
-
-u32 exynos_dp_get_lane3_link_training(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_LN3_LINK_TRAINING_CTL);
-	return reg;
-}
-
-void exynos_dp_reset_macro(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_PHY_TEST);
-	reg |= MACRO_RST;
-	writel(reg, dp->reg_base + EXYNOS_DP_PHY_TEST);
-
-	/* 10 us is the minimum reset time. */
-	usleep_range(10, 20);
-
-	reg &= ~MACRO_RST;
-	writel(reg, dp->reg_base + EXYNOS_DP_PHY_TEST);
-}
-
-void exynos_dp_init_video(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = VSYNC_DET | VID_FORMAT_CHG | VID_CLK_CHG;
-	writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_1);
-
-	reg = 0x0;
-	writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_1);
-
-	reg = CHA_CRI(4) | CHA_CTRL;
-	writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_2);
-
-	reg = 0x0;
-	writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_3);
-
-	reg = VID_HRES_TH(2) | VID_VRES_TH(0);
-	writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_8);
-}
-
-void exynos_dp_set_video_color_format(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	/* Configure the input color depth, color space, dynamic range */
-	reg = (dp->video_info->dynamic_range << IN_D_RANGE_SHIFT) |
-		(dp->video_info->color_depth << IN_BPC_SHIFT) |
-		(dp->video_info->color_space << IN_COLOR_F_SHIFT);
-	writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_2);
-
-	/* Set Input Color YCbCr Coefficients to ITU601 or ITU709 */
-	reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_3);
-	reg &= ~IN_YC_COEFFI_MASK;
-	if (dp->video_info->ycbcr_coeff)
-		reg |= IN_YC_COEFFI_ITU709;
-	else
-		reg |= IN_YC_COEFFI_ITU601;
-	writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_3);
-}
-
-int exynos_dp_is_slave_video_stream_clock_on(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_1);
-	writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_1);
-
-	reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_1);
-
-	if (!(reg & DET_STA)) {
-		dev_dbg(dp->dev, "Input stream clock not detected.\n");
-		return -EINVAL;
-	}
-
-	reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_2);
-	writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_2);
-
-	reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_2);
-	dev_dbg(dp->dev, "wait SYS_CTL_2.\n");
-
-	if (reg & CHA_STA) {
-		dev_dbg(dp->dev, "Input stream clk is changing\n");
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-void exynos_dp_set_video_cr_mn(struct exynos_dp_device *dp,
-		enum clock_recovery_m_value_type type,
-		u32 m_value,
-		u32 n_value)
-{
-	u32 reg;
-
-	if (type == REGISTER_M) {
-		reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_4);
-		reg |= FIX_M_VID;
-		writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_4);
-		reg = m_value & 0xff;
-		writel(reg, dp->reg_base + EXYNOS_DP_M_VID_0);
-		reg = (m_value >> 8) & 0xff;
-		writel(reg, dp->reg_base + EXYNOS_DP_M_VID_1);
-		reg = (m_value >> 16) & 0xff;
-		writel(reg, dp->reg_base + EXYNOS_DP_M_VID_2);
-
-		reg = n_value & 0xff;
-		writel(reg, dp->reg_base + EXYNOS_DP_N_VID_0);
-		reg = (n_value >> 8) & 0xff;
-		writel(reg, dp->reg_base + EXYNOS_DP_N_VID_1);
-		reg = (n_value >> 16) & 0xff;
-		writel(reg, dp->reg_base + EXYNOS_DP_N_VID_2);
-	} else  {
-		reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_4);
-		reg &= ~FIX_M_VID;
-		writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_4);
-
-		writel(0x00, dp->reg_base + EXYNOS_DP_N_VID_0);
-		writel(0x80, dp->reg_base + EXYNOS_DP_N_VID_1);
-		writel(0x00, dp->reg_base + EXYNOS_DP_N_VID_2);
-	}
-}
-
-void exynos_dp_set_video_timing_mode(struct exynos_dp_device *dp, u32 type)
-{
-	u32 reg;
-
-	if (type == VIDEO_TIMING_FROM_CAPTURE) {
-		reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
-		reg &= ~FORMAT_SEL;
-		writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
-	} else {
-		reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
-		reg |= FORMAT_SEL;
-		writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
-	}
-}
-
-void exynos_dp_enable_video_master(struct exynos_dp_device *dp, bool enable)
-{
-	u32 reg;
-
-	if (enable) {
-		reg = readl(dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL);
-		reg &= ~VIDEO_MODE_MASK;
-		reg |= VIDEO_MASTER_MODE_EN | VIDEO_MODE_MASTER_MODE;
-		writel(reg, dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL);
-	} else {
-		reg = readl(dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL);
-		reg &= ~VIDEO_MODE_MASK;
-		reg |= VIDEO_MODE_SLAVE_MODE;
-		writel(reg, dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL);
-	}
-}
-
-void exynos_dp_start_video(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_1);
-	reg |= VIDEO_EN;
-	writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_1);
-}
-
-int exynos_dp_is_video_stream_on(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
-	writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_3);
-
-	reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
-	if (!(reg & STRM_VALID)) {
-		dev_dbg(dp->dev, "Input video stream is not detected.\n");
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-void exynos_dp_config_video_slave_mode(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_FUNC_EN_1);
-	reg &= ~(MASTER_VID_FUNC_EN_N|SLAVE_VID_FUNC_EN_N);
-	reg |= MASTER_VID_FUNC_EN_N;
-	writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_1);
-
-	reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
-	reg &= ~INTERACE_SCAN_CFG;
-	reg |= (dp->video_info->interlaced << 2);
-	writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
-
-	reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
-	reg &= ~VSYNC_POLARITY_CFG;
-	reg |= (dp->video_info->v_sync_polarity << 1);
-	writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
-
-	reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
-	reg &= ~HSYNC_POLARITY_CFG;
-	reg |= (dp->video_info->h_sync_polarity << 0);
-	writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
-
-	reg = AUDIO_MODE_SPDIF_MODE | VIDEO_MODE_SLAVE_MODE;
-	writel(reg, dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL);
-}
-
-void exynos_dp_enable_scrambling(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET);
-	reg &= ~SCRAMBLING_DISABLE;
-	writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET);
-}
-
-void exynos_dp_disable_scrambling(struct exynos_dp_device *dp)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET);
-	reg |= SCRAMBLING_DISABLE;
-	writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET);
-}
diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h
new file mode 100644
index 0000000..d1188db
--- /dev/null
+++ b/include/drm/bridge/analogix_dp.h
@@ -0,0 +1,40 @@
+/*
+ * Analogix DP (Display Port) Core interface driver.
+ *
+ * Copyright (C) 2015 Rockchip Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+#ifndef _ANALOGIX_DP_H_
+#define _ANALOGIX_DP_H_
+
+#include <drm/drm_crtc.h>
+
+enum analogix_dp_devtype {
+	EXYNOS_DP,
+};
+
+struct analogix_dp_plat_data {
+	enum analogix_dp_devtype dev_type;
+	struct drm_panel *panel;
+	struct drm_encoder *encoder;
+	struct drm_connector *connector;
+
+	int (*power_on)(struct analogix_dp_plat_data *);
+	int (*power_off)(struct analogix_dp_plat_data *);
+	int (*attach)(struct analogix_dp_plat_data *, struct drm_bridge *,
+		      struct drm_connector *);
+	int (*get_modes)(struct analogix_dp_plat_data *);
+};
+
+int analogix_dp_resume(struct device *dev);
+int analogix_dp_suspend(struct device *dev);
+
+int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev,
+		     struct analogix_dp_plat_data *plat_data);
+void analogix_dp_unbind(struct device *dev, struct device *master, void *data);
+
+#endif /* _ANALOGIX_DP_H_ */
-- 
1.9.1




From ykk at rock-chips.com  Thu Jan 21 17:54:58 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 22 Jan 2016 09:54:58 +0800
Subject: [PATCH v13 04/17] drm: bridge: analogix/dp: dynamic parse sync_pol &
 interlace & dynamic_range
In-Reply-To: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
References: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1453427698-23885-1-git-send-email-ykk@rock-chips.com>

Both hsync/vsync polarity and interlace mode can be parsed from
drm display mode, and dynamic_range and ycbcr_coeff can be judge
by the video code.

But presumably Exynos still relies on the DT properties, so take
good use of mode_fixup() in to achieve the compatibility hacks.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Reviewed-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
Tested-by: Javier Martinez Canillas <javier at osg.samsung.com>
---
Changes in v13: None
Changes in v12: None
Changes in v11: None
Changes in v10: None
Changes in v9: None
Changes in v8: None
Changes in v7:
- Back to use the of_property_read_bool() interfacs to provoid backward
  compatibility of "hsync-active-high" "vsync-active-high" "interlaced"
  to avoid -EOVERFLOW error (Krzysztof)

Changes in v6: None
Changes in v5:
- Switch video timing type to "u32", so driver could use "of_property_read_u32"
  to get the backword timing values. Krzysztof suggest me that driver could use
  the "of_property_read_bool" to get backword timing values, but that interfacs
  would modify the original drm_display_mode timing directly (whether those
  properties exists or not).

Changes in v4:
- Provide backword compatibility with samsung. (Krzysztof)

Changes in v3:
- Dynamic parse video timing info from struct drm_display_mode and
  struct drm_display_info. (Thierry)

Changes in v2: None

 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 148 +++++++++++++--------
 drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |   2 +-
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  |  14 +-
 3 files changed, 103 insertions(+), 61 deletions(-)

diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index b948636..26359f4 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -893,8 +893,8 @@ static void analogix_dp_commit(struct analogix_dp_device *dp)
 		return;
 	}
 
-	ret = analogix_dp_set_link_train(dp, dp->video_info->lane_count,
-					 dp->video_info->link_rate);
+	ret = analogix_dp_set_link_train(dp, dp->video_info.lane_count,
+					 dp->video_info.link_rate);
 	if (ret) {
 		dev_err(dp->dev, "unable to do link train\n");
 		return;
@@ -1077,6 +1077,85 @@ static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
 	dp->dpms_mode = DRM_MODE_DPMS_OFF;
 }
 
+static void analogix_dp_bridge_mode_set(struct drm_bridge *bridge,
+					struct drm_display_mode *orig_mode,
+					struct drm_display_mode *mode)
+{
+	struct analogix_dp_device *dp = bridge->driver_private;
+	struct drm_display_info *display_info = &dp->connector.display_info;
+	struct video_info *video = &dp->video_info;
+	struct device_node *dp_node = dp->dev->of_node;
+	int vic;
+
+	/* Input video interlaces & hsync pol & vsync pol */
+	video->interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
+	video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC);
+	video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC);
+
+	/* Input video dynamic_range & colorimetry */
+	vic = drm_match_cea_mode(mode);
+	if ((vic == 6) || (vic == 7) || (vic == 21) || (vic == 22) ||
+	    (vic == 2) || (vic == 3) || (vic == 17) || (vic == 18)) {
+		video->dynamic_range = CEA;
+		video->ycbcr_coeff = COLOR_YCBCR601;
+	} else if (vic) {
+		video->dynamic_range = CEA;
+		video->ycbcr_coeff = COLOR_YCBCR709;
+	} else {
+		video->dynamic_range = VESA;
+		video->ycbcr_coeff = COLOR_YCBCR709;
+	}
+
+	/* Input vide bpc and color_formats */
+	switch (display_info->bpc) {
+	case 12:
+		video->color_depth = COLOR_12;
+		break;
+	case 10:
+		video->color_depth = COLOR_10;
+		break;
+	case 8:
+		video->color_depth = COLOR_8;
+		break;
+	case 6:
+		video->color_depth = COLOR_6;
+		break;
+	default:
+		video->color_depth = COLOR_8;
+		break;
+	}
+	if (display_info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+		video->color_space = COLOR_YCBCR444;
+	else if (display_info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+		video->color_space = COLOR_YCBCR422;
+	else if (display_info->color_formats & DRM_COLOR_FORMAT_RGB444)
+		video->color_space = COLOR_RGB;
+	else
+		video->color_space = COLOR_RGB;
+
+	/*
+	 * NOTE: those property parsing code is used for providing backward
+	 * compatibility for samsung platform.
+	 * Due to we used the "of_property_read_u32" interfaces, when this
+	 * property isn't present, the "video_info" can keep the original
+	 * values and wouldn't be modified.
+	 */
+	of_property_read_u32(dp_node, "samsung,color-space",
+			     &video->color_space);
+	of_property_read_u32(dp_node, "samsung,dynamic-range",
+			     &video->dynamic_range);
+	of_property_read_u32(dp_node, "samsung,ycbcr-coeff",
+			     &video->ycbcr_coeff);
+	of_property_read_u32(dp_node, "samsung,color-depth",
+			     &video->color_depth);
+	if (of_property_read_bool(dp_node, "hsync-active-high"))
+		video->h_sync_polarity = true;
+	if (of_property_read_bool(dp_node, "vsync-active-high"))
+		video->v_sync_polarity = true;
+	if (of_property_read_bool(dp_node, "interlaced"))
+		video->interlaced = true;
+}
+
 static void analogix_dp_bridge_nop(struct drm_bridge *bridge)
 {
 	/* do nothing */
@@ -1087,6 +1166,7 @@ static const struct drm_bridge_funcs analogix_dp_bridge_funcs = {
 	.disable = analogix_dp_bridge_disable,
 	.pre_enable = analogix_dp_bridge_nop,
 	.post_disable = analogix_dp_bridge_nop,
+	.mode_set = analogix_dp_bridge_mode_set,
 	.attach = analogix_dp_bridge_attach,
 };
 
@@ -1118,62 +1198,24 @@ static int analogix_dp_create_bridge(struct drm_device *drm_dev,
 	return 0;
 }
 
-static struct video_info *analogix_dp_dt_parse_pdata(struct device *dev)
+static int analogix_dp_dt_parse_pdata(struct analogix_dp_device *dp)
 {
-	struct device_node *dp_node = dev->of_node;
-	struct video_info *dp_video_config;
-
-	dp_video_config = devm_kzalloc(dev, sizeof(*dp_video_config),
-				       GFP_KERNEL);
-	if (!dp_video_config)
-		return ERR_PTR(-ENOMEM);
-
-	dp_video_config->h_sync_polarity =
-		of_property_read_bool(dp_node, "hsync-active-high");
-
-	dp_video_config->v_sync_polarity =
-		of_property_read_bool(dp_node, "vsync-active-high");
-
-	dp_video_config->interlaced =
-		of_property_read_bool(dp_node, "interlaced");
-
-	if (of_property_read_u32(dp_node, "samsung,color-space",
-				 &dp_video_config->color_space)) {
-		dev_err(dev, "failed to get color-space\n");
-		return ERR_PTR(-EINVAL);
-	}
-
-	if (of_property_read_u32(dp_node, "samsung,dynamic-range",
-				 &dp_video_config->dynamic_range)) {
-		dev_err(dev, "failed to get dynamic-range\n");
-		return ERR_PTR(-EINVAL);
-	}
-
-	if (of_property_read_u32(dp_node, "samsung,ycbcr-coeff",
-				 &dp_video_config->ycbcr_coeff)) {
-		dev_err(dev, "failed to get ycbcr-coeff\n");
-		return ERR_PTR(-EINVAL);
-	}
-
-	if (of_property_read_u32(dp_node, "samsung,color-depth",
-				 &dp_video_config->color_depth)) {
-		dev_err(dev, "failed to get color-depth\n");
-		return ERR_PTR(-EINVAL);
-	}
+	struct device_node *dp_node = dp->dev->of_node;
+	struct video_info *video_info = &dp->video_info;
 
 	if (of_property_read_u32(dp_node, "samsung,link-rate",
-				 &dp_video_config->link_rate)) {
-		dev_err(dev, "failed to get link-rate\n");
-		return ERR_PTR(-EINVAL);
+				 &video_info->link_rate)) {
+		dev_err(dp->dev, "failed to get link-rate\n");
+		return -EINVAL;
 	}
 
 	if (of_property_read_u32(dp_node, "samsung,lane-count",
-				 &dp_video_config->lane_count)) {
-		dev_err(dev, "failed to get lane-count\n");
-		return ERR_PTR(-EINVAL);
+				 &video_info->lane_count)) {
+		dev_err(dp->dev, "failed to get lane-count\n");
+		return -EINVAL;
 	}
 
-	return dp_video_config;
+	return 0;
 }
 
 int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev,
@@ -1206,9 +1248,9 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev,
 	 */
 	dp->plat_data = plat_data;
 
-	dp->video_info = analogix_dp_dt_parse_pdata(&pdev->dev);
-	if (IS_ERR(dp->video_info))
-		return PTR_ERR(dp->video_info);
+	ret = analogix_dp_dt_parse_pdata(dp);
+	if (ret)
+		return ret;
 
 	dp->phy = devm_phy_get(dp->dev, "dp");
 	if (IS_ERR(dp->phy)) {
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
index 911642e..d488dd6 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
@@ -148,7 +148,7 @@ struct analogix_dp_device {
 	unsigned int		irq;
 	void __iomem		*reg_base;
 
-	struct video_info	*video_info;
+	struct video_info	video_info;
 	struct link_train	link_train;
 	struct work_struct	hotplug_work;
 	struct phy		*phy;
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
index eb0b63c..3858df5 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
@@ -1084,15 +1084,15 @@ void analogix_dp_set_video_color_format(struct analogix_dp_device *dp)
 	u32 reg;
 
 	/* Configure the input color depth, color space, dynamic range */
-	reg = (dp->video_info->dynamic_range << IN_D_RANGE_SHIFT) |
-		(dp->video_info->color_depth << IN_BPC_SHIFT) |
-		(dp->video_info->color_space << IN_COLOR_F_SHIFT);
+	reg = (dp->video_info.dynamic_range << IN_D_RANGE_SHIFT) |
+		(dp->video_info.color_depth << IN_BPC_SHIFT) |
+		(dp->video_info.color_space << IN_COLOR_F_SHIFT);
 	writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_2);
 
 	/* Set Input Color YCbCr Coefficients to ITU601 or ITU709 */
 	reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
 	reg &= ~IN_YC_COEFFI_MASK;
-	if (dp->video_info->ycbcr_coeff)
+	if (dp->video_info.ycbcr_coeff)
 		reg |= IN_YC_COEFFI_ITU709;
 	else
 		reg |= IN_YC_COEFFI_ITU601;
@@ -1229,17 +1229,17 @@ void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp)
 
 	reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
 	reg &= ~INTERACE_SCAN_CFG;
-	reg |= (dp->video_info->interlaced << 2);
+	reg |= (dp->video_info.interlaced << 2);
 	writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
 
 	reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
 	reg &= ~VSYNC_POLARITY_CFG;
-	reg |= (dp->video_info->v_sync_polarity << 1);
+	reg |= (dp->video_info.v_sync_polarity << 1);
 	writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
 
 	reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
 	reg &= ~HSYNC_POLARITY_CFG;
-	reg |= (dp->video_info->h_sync_polarity << 0);
+	reg |= (dp->video_info.h_sync_polarity << 0);
 	writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
 
 	reg = AUDIO_MODE_SPDIF_MODE | VIDEO_MODE_SLAVE_MODE;
-- 
1.9.1




From ykk at rock-chips.com  Thu Jan 21 17:55:03 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 22 Jan 2016 09:55:03 +0800
Subject: [PATCH v13 05/17] dt-bindings: add document for analogix display port
 driver
In-Reply-To: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
References: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1453427703-23931-1-git-send-email-ykk@rock-chips.com>

Analogix dp driver is split from exynos dp driver, so we just
make an copy of exynos_dp.txt, and then simplify exynos_dp.txt

Beside update some exynos dtsi file with the latest change
according to the devicetree binding documents.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Acked-by: Rob Herring <robh at kernel.org>
---
Changes in v13: None
Changes in v12: None
Changes in v11: None
Changes in v10:
- Add the ack from Rob Herring

Changes in v9: None
Changes in v8:
- Correct the right document path of display-timing.txt (Heiko)
- Correct the misspell of 'from' to 'frm'. (Heiko)

Changes in v7: None
Changes in v6: None
Changes in v5: None
Changes in v4:
- Split all DTS changes, and provide backward compatibility. Mark old
  properties as deprecated but still support them. (Krzysztof)
- Update "analogix,hpd-gpio" to "hpd-gpios" prop name. (Rob)
- Deprecated some properties which could parsed from Edid/Mode/DPCD. (Thierry)
    "analogix,color-space" & "analogix,color-depth"   &
    "analogix,link-rate"   & "analogix,lane-count"    &
    "analogix,ycbcr-coeff" & "analogix,dynamic-range" &
    "vsync-active-high"    & "hsync-active-high"      & "interlaces"

Changes in v3:
- Add devicetree binding documents. (Heiko)
- Remove sync pol & colorimetry properies from the new analogix dp driver
  devicetree binding. (Thierry)
- Update the exist exynos dtsi file with the latest DP DT properies.

Changes in v2: None

 .../bindings/display/bridge/analogix_dp.txt        | 50 ++++++++++++
 .../bindings/display/exynos/exynos_dp.txt          | 92 ++++++----------------
 2 files changed, 76 insertions(+), 66 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/display/bridge/analogix_dp.txt

diff --git a/Documentation/devicetree/bindings/display/bridge/analogix_dp.txt b/Documentation/devicetree/bindings/display/bridge/analogix_dp.txt
new file mode 100644
index 0000000..7659a7a
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/bridge/analogix_dp.txt
@@ -0,0 +1,50 @@
+Analogix Display Port bridge bindings
+
+Required properties for dp-controller:
+	-compatible:
+		platform specific such as:
+		 * "samsung,exynos5-dp"
+		 * "rockchip,rk3288-dp"
+	-reg:
+		physical base address of the controller and length
+		of memory mapped region.
+	-interrupts:
+		interrupt combiner values.
+	-clocks:
+		from common clock binding: handle to dp clock.
+	-clock-names:
+		from common clock binding: Shall be "dp".
+	-interrupt-parent:
+		phandle to Interrupt combiner node.
+	-phys:
+		from general PHY binding: the phandle for the PHY device.
+	-phy-names:
+		from general PHY binding: Should be "dp".
+
+Optional properties for dp-controller:
+	-hpd-gpios:
+		Hotplug detect GPIO.
+		Indicates which GPIO should be used for hotplug detection
+	-port@[X]: SoC specific port nodes with endpoint definitions as defined
+		in Documentation/devicetree/bindings/media/video-interfaces.txt,
+		please refer to the SoC specific binding document:
+		* Documentation/devicetree/bindings/display/exynos/exynos_dp.txt
+		* Documentation/devicetree/bindings/video/analogix_dp-rockchip.txt
+
+
+[1]: Documentation/devicetree/bindings/media/video-interfaces.txt
+-------------------------------------------------------------------------------
+
+Example:
+
+	dp-controller {
+		compatible = "samsung,exynos5-dp";
+		reg = <0x145b0000 0x10000>;
+		interrupts = <10 3>;
+		interrupt-parent = <&combiner>;
+		clocks = <&clock 342>;
+		clock-names = "dp";
+
+		phys = <&dp_phy>;
+		phy-names = "dp";
+	};
diff --git a/Documentation/devicetree/bindings/display/exynos/exynos_dp.txt b/Documentation/devicetree/bindings/display/exynos/exynos_dp.txt
index fe4a7a2..4fa8aca 100644
--- a/Documentation/devicetree/bindings/display/exynos/exynos_dp.txt
+++ b/Documentation/devicetree/bindings/display/exynos/exynos_dp.txt
@@ -1,20 +1,3 @@
-Device-Tree bindings for Samsung Exynos Embedded DisplayPort Transmitter(eDP)
-
-DisplayPort is industry standard to accommodate the growing board adoption
-of digital display technology within the PC and CE industries.
-It consolidates the internal and external connection methods to reduce device
-complexity and cost. It also supports necessary features for important cross
-industry applications and provides performance scalability to enable the next
-generation of displays that feature higher color depths, refresh rates, and
-display resolutions.
-
-eDP (embedded display port) device is compliant with Embedded DisplayPort
-standard as follows,
-- DisplayPort standard 1.1a for Exynos5250 and Exynos5260.
-- DisplayPort standard 1.3 for Exynos5422s and Exynos5800.
-
-eDP resides between FIMD and panel or FIMD and bridge such as LVDS.
-
 The Exynos display port interface should be configured based on
 the type of panel connected to it.
 
@@ -48,26 +31,6 @@ Required properties for dp-controller:
 		from general PHY binding: the phandle for the PHY device.
 	-phy-names:
 		from general PHY binding: Should be "dp".
-	-samsung,color-space:
-		input video data format.
-			COLOR_RGB = 0, COLOR_YCBCR422 = 1, COLOR_YCBCR444 = 2
-	-samsung,dynamic-range:
-		dynamic range for input video data.
-			VESA = 0, CEA = 1
-	-samsung,ycbcr-coeff:
-		YCbCr co-efficients for input video.
-			COLOR_YCBCR601 = 0, COLOR_YCBCR709 = 1
-	-samsung,color-depth:
-		number of bits per colour component.
-			COLOR_6 = 0, COLOR_8 = 1, COLOR_10 = 2, COLOR_12 = 3
-	-samsung,link-rate:
-		link rate supported by the panel.
-			LINK_RATE_1_62GBPS = 0x6, LINK_RATE_2_70GBPS = 0x0A
-	-samsung,lane-count:
-		number of lanes supported by the panel.
-			LANE_COUNT1 = 1, LANE_COUNT2 = 2, LANE_COUNT4 = 4
-	- display-timings: timings for the connected panel as described by
-		Documentation/devicetree/bindings/display/display-timing.txt
 
 Optional properties for dp-controller:
 	-interlaced:
@@ -83,17 +46,30 @@ Optional properties for dp-controller:
 		Hotplug detect GPIO.
 			Indicates which GPIO should be used for hotplug
 			detection
-Video interfaces:
-  Device node can contain video interface port nodes according to [1].
-  The following are properties specific to those nodes:
-
-  endpoint node connected to bridge or panel node:
-   - remote-endpoint: specifies the endpoint in panel or bridge node.
-		      This node is required in all kinds of exynos dp
-		      to represent the connection between dp and bridge
-		      or dp and panel.
-
-[1]: Documentation/devicetree/bindings/media/video-interfaces.txt
+	-video interfaces: Device node can contain video interface port
+			nodes according to [1].
+	- display-timings: timings for the connected panel as described by
+		Documentation/devicetree/bindings/display/panel/display-timing.txt
+
+For the below properties, please refer to Analogix DP binding document:
+ * Documentation/devicetree/bindings/display/bridge/analogix_dp.txt
+	-phys (required)
+	-phy-names (required)
+	-hpd-gpios (optional)
+
+Deprecated properties for DisplayPort:
+-interlaced:            deprecated prop that can parsed from drm_display_mode.
+-vsync-active-high:     deprecated prop that can parsed from drm_display_mode.
+-hsync-active-high:     deprecated prop that can parsed from drm_display_mode.
+-samsung,ycbcr-coeff:   deprecated prop that can parsed from drm_display_mode.
+-samsung,dynamic-range: deprecated prop that can parsed from drm_display_mode.
+-samsung,color-space:   deprecated prop that can parsed from drm_display_info.
+-samsung,color-depth:   deprecated prop that can parsed from drm_display_info.
+-samsung,link-rate:     deprecated prop that can reading from monitor by dpcd method.
+-samsung,lane-count:    deprecated prop that can reading from monitor by dpcd method.
+-samsung,hpd-gpio:      deprecated name for hpd-gpios.
+
+-------------------------------------------------------------------------------
 
 Example:
 
@@ -112,13 +88,6 @@ SOC specific portion:
 
 Board Specific portion:
 	dp-controller {
-		samsung,color-space = <0>;
-		samsung,dynamic-range = <0>;
-		samsung,ycbcr-coeff = <0>;
-		samsung,color-depth = <1>;
-		samsung,link-rate = <0x0a>;
-		samsung,lane-count = <4>;
-
 		display-timings {
 			native-mode = <&lcd_timing>;
 			lcd_timing: 1366x768 {
@@ -135,18 +104,9 @@ Board Specific portion:
 		};
 
 		ports {
-			port {
+			port at 0 {
 				dp_out: endpoint {
-					remote-endpoint = <&dp_in>;
-				};
-			};
-		};
-
-		panel {
-			...
-			port {
-				dp_in: endpoint {
-					remote-endpoint = <&dp_out>;
+					remote-endpoint = <&bridge_in>;
 				};
 			};
 		};
-- 
1.9.1




From ykk at rock-chips.com  Thu Jan 21 17:55:21 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 22 Jan 2016 09:55:21 +0800
Subject: [PATCH v13 06/17] ARM: dts: exynos/dp: remove some properties that
 deprecated by analogix_dp driver
In-Reply-To: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
References: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1453427721-23974-1-git-send-email-ykk@rock-chips.com>

After exynos_dp have been split the common IP code into analogix_dp driver,
the analogix_dp driver have deprecated some Samsung platform properties which
could be dynamically parsed from EDID/MODE/DPCD message, so this is an update
for Exynos DTS file for dp-controller.

Beside the backward compatibility is fully preserved, so there are no
bisectability break that make this change in a separate patch.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Reviewed-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
Tested-by: Javier Martinez Canillas <javier at osg.samsung.com>
---
Changes in v13: None
Changes in v12: None
Changes in v11: None
Changes in v10: None
Changes in v9: None
Changes in v8: None
Changes in v7: None
Changes in v6:
- Fix Peach Pit hpd property name error:
-       hpd-gpio = <&gpx2 6 0>;
+       hpd-gpios = <&gpx2 6 0>;

Changes in v5:
- Correct the misspell in commit message. (Krzysztof)

Changes in v4:
- Separate all DTS changes to a separate patch. (Krzysztof)

Changes in v3: None
Changes in v2: None

 arch/arm/boot/dts/exynos5250-arndale.dts      | 2 --
 arch/arm/boot/dts/exynos5250-smdk5250.dts     | 2 --
 arch/arm/boot/dts/exynos5250-snow-common.dtsi | 4 +---
 arch/arm/boot/dts/exynos5250-spring.dts       | 4 +---
 arch/arm/boot/dts/exynos5420-peach-pit.dts    | 4 +---
 arch/arm/boot/dts/exynos5420-smdk5420.dts     | 2 --
 arch/arm/boot/dts/exynos5800-peach-pi.dts     | 2 --
 7 files changed, 3 insertions(+), 17 deletions(-)

diff --git a/arch/arm/boot/dts/exynos5250-arndale.dts b/arch/arm/boot/dts/exynos5250-arndale.dts
index c000532..b1790cf 100644
--- a/arch/arm/boot/dts/exynos5250-arndale.dts
+++ b/arch/arm/boot/dts/exynos5250-arndale.dts
@@ -124,8 +124,6 @@
 &dp {
 	status = "okay";
 	samsung,color-space = <0>;
-	samsung,dynamic-range = <0>;
-	samsung,ycbcr-coeff = <0>;
 	samsung,color-depth = <1>;
 	samsung,link-rate = <0x0a>;
 	samsung,lane-count = <4>;
diff --git a/arch/arm/boot/dts/exynos5250-smdk5250.dts b/arch/arm/boot/dts/exynos5250-smdk5250.dts
index 0f5dcd4..f30c2db 100644
--- a/arch/arm/boot/dts/exynos5250-smdk5250.dts
+++ b/arch/arm/boot/dts/exynos5250-smdk5250.dts
@@ -80,8 +80,6 @@
 
 &dp {
 	samsung,color-space = <0>;
-	samsung,dynamic-range = <0>;
-	samsung,ycbcr-coeff = <0>;
 	samsung,color-depth = <1>;
 	samsung,link-rate = <0x0a>;
 	samsung,lane-count = <4>;
diff --git a/arch/arm/boot/dts/exynos5250-snow-common.dtsi b/arch/arm/boot/dts/exynos5250-snow-common.dtsi
index 0a7f408..ee94110 100644
--- a/arch/arm/boot/dts/exynos5250-snow-common.dtsi
+++ b/arch/arm/boot/dts/exynos5250-snow-common.dtsi
@@ -236,12 +236,10 @@
 	pinctrl-names = "default";
 	pinctrl-0 = <&dp_hpd>;
 	samsung,color-space = <0>;
-	samsung,dynamic-range = <0>;
-	samsung,ycbcr-coeff = <0>;
 	samsung,color-depth = <1>;
 	samsung,link-rate = <0x0a>;
 	samsung,lane-count = <2>;
-	samsung,hpd-gpio = <&gpx0 7 GPIO_ACTIVE_HIGH>;
+	hpd-gpios = <&gpx0 7 GPIO_ACTIVE_HIGH>;
 
 	ports {
 		port at 0 {
diff --git a/arch/arm/boot/dts/exynos5250-spring.dts b/arch/arm/boot/dts/exynos5250-spring.dts
index c1edd6d..91881d7 100644
--- a/arch/arm/boot/dts/exynos5250-spring.dts
+++ b/arch/arm/boot/dts/exynos5250-spring.dts
@@ -74,12 +74,10 @@
 	pinctrl-names = "default";
 	pinctrl-0 = <&dp_hpd_gpio>;
 	samsung,color-space = <0>;
-	samsung,dynamic-range = <0>;
-	samsung,ycbcr-coeff = <0>;
 	samsung,color-depth = <1>;
 	samsung,link-rate = <0x0a>;
 	samsung,lane-count = <1>;
-	samsung,hpd-gpio = <&gpc3 0 GPIO_ACTIVE_HIGH>;
+	hpd-gpios = <&gpc3 0 GPIO_ACTIVE_HIGH>;
 };
 
 &ehci {
diff --git a/arch/arm/boot/dts/exynos5420-peach-pit.dts b/arch/arm/boot/dts/exynos5420-peach-pit.dts
index 72ba6f0..8baf40a 100644
--- a/arch/arm/boot/dts/exynos5420-peach-pit.dts
+++ b/arch/arm/boot/dts/exynos5420-peach-pit.dts
@@ -148,12 +148,10 @@
 	pinctrl-names = "default";
 	pinctrl-0 = <&dp_hpd_gpio>;
 	samsung,color-space = <0>;
-	samsung,dynamic-range = <0>;
-	samsung,ycbcr-coeff = <0>;
 	samsung,color-depth = <1>;
 	samsung,link-rate = <0x06>;
 	samsung,lane-count = <2>;
-	samsung,hpd-gpio = <&gpx2 6 GPIO_ACTIVE_HIGH>;
+	hpd-gpios = <&gpx2 6 GPIO_ACTIVE_HIGH>;
 
 	ports {
 		port at 0 {
diff --git a/arch/arm/boot/dts/exynos5420-smdk5420.dts b/arch/arm/boot/dts/exynos5420-smdk5420.dts
index ac35aef..f67344f 100644
--- a/arch/arm/boot/dts/exynos5420-smdk5420.dts
+++ b/arch/arm/boot/dts/exynos5420-smdk5420.dts
@@ -93,8 +93,6 @@
 	pinctrl-names = "default";
 	pinctrl-0 = <&dp_hpd>;
 	samsung,color-space = <0>;
-	samsung,dynamic-range = <0>;
-	samsung,ycbcr-coeff = <0>;
 	samsung,color-depth = <1>;
 	samsung,link-rate = <0x0a>;
 	samsung,lane-count = <4>;
diff --git a/arch/arm/boot/dts/exynos5800-peach-pi.dts b/arch/arm/boot/dts/exynos5800-peach-pi.dts
index 1cc2e95..fc232be 100644
--- a/arch/arm/boot/dts/exynos5800-peach-pi.dts
+++ b/arch/arm/boot/dts/exynos5800-peach-pi.dts
@@ -148,8 +148,6 @@
 	pinctrl-names = "default";
 	pinctrl-0 = <&dp_hpd_gpio>;
 	samsung,color-space = <0>;
-	samsung,dynamic-range = <0>;
-	samsung,ycbcr-coeff = <0>;
 	samsung,color-depth = <1>;
 	samsung,link-rate = <0x0a>;
 	samsung,lane-count = <2>;
-- 
1.9.1




From ykk at rock-chips.com  Thu Jan 21 17:55:26 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 22 Jan 2016 09:55:26 +0800
Subject: [PATCH v13 07/17] drm: rockchip: dp: add rockchip platform dp driver
In-Reply-To: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
References: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1453427726-24030-1-git-send-email-ykk@rock-chips.com>

Rockchip have three clocks for dp controller, we leave pclk_edp
to analogix_dp driver control, and keep the sclk_edp_24m and
sclk_edp in platform driver.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Tested-by: Heiko Stuebner <heiko at sntech.de>
---
Changes in v13:
- Use .enable instead of preprare/commit in encoder_helper_funcs (Heiko)
- Fix the missing parameters with drm_encoder_init() helper function. (Heiko)

Changes in v12: None
Changes in v11: None
Changes in v10:
- Correct the ROCKCHIP_ANALOGIX_DP indentation in Kconfig to tabs here (Heiko)

Changes in v9: None
Changes in v8: None
Changes in v7: None
Changes in v6: None
Changes in v5:
- Remove the empty line at the end of document, and correct the endpoint
  numbers in the example DT node, and remove the regulator iomux setting
  in driver code while using the pinctl in devicetree instead. (Heiko)
- Add device type declared, cause the previous "platform device type
  support (v4 11/16)" already merge into (v5 02/14).
- Implement connector registration code. (Thierry)

Changes in v4:
- Remove some deprecated DT properties in rockchip dp document.

Changes in v3:
- Leave "sclk_edp_24m" to rockchip dp phy driver which name to "24m",
  and leave "sclk_edp" to analogix dp core driver which name to "dp",
  and leave "pclk_edp" to rockchip dp platform driver which name to
  "pclk". (Thierry & Heiko)
- Add devicetree binding document. (Heiko)
- Remove "rockchip,panel" DT property, take use of remote point to get panel
  node. (Heiko)
- Add the new function point dp_platdata->get_modes() init.

Changes in v2:
- Get panel node with remote-endpoint method, and create devicetree binding
  for driver. (Heiko)
- Remove the clock enable/disbale with "sclk_edp" & "sclk_edp_24m",
  leave those clock to rockchip dp phy driver.

 drivers/gpu/drm/rockchip/Kconfig                |   9 +
 drivers/gpu/drm/rockchip/Makefile               |   1 +
 drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 384 ++++++++++++++++++++++++
 include/drm/bridge/analogix_dp.h                |   1 +
 4 files changed, 395 insertions(+)
 create mode 100644 drivers/gpu/drm/rockchip/analogix_dp-rockchip.c

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 8573985..9fe4e32 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -35,3 +35,12 @@ config ROCKCHIP_DW_MIPI_DSI
 	 for the Synopsys DesignWare HDMI driver. If you want to
 	 enable MIPI DSI on RK3288 based SoC, you should selet this
 	 option.
+
+config ROCKCHIP_ANALOGIX_DP
+	tristate "Rockchip specific extensions for Analogix DP driver"
+	depends on DRM_ROCKCHIP
+	select DRM_ANALOGIX_DP
+	help
+	  This selects support for Rockchip SoC specific extensions
+	  for the Analogix Core DP driver. If you want to enable DP
+	  on RK3288 based SoC, you should selet this option.
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index d1dc0f7..40d4c94 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -7,6 +7,7 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
 
 obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
 obj-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
+obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
 
 obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_drm_vop.o \
 				rockchip_vop_reg.o
diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
new file mode 100644
index 0000000..b7af1b7
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
@@ -0,0 +1,384 @@
+/*
+ * Rockchip SoC DP (Display Port) interface driver.
+ *
+ * Copyright (C) Fuzhou Rockchip Electronics Co., Ltd.
+ * Author: Andy Yan <andy.yan at rock-chips.com>
+ *         Yakir Yang <ykk at rock-chips.com>
+ *         Jeff Chen <jeff.chen at rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/component.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of_graph.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/clk.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+
+#include <video/of_videomode.h>
+#include <video/videomode.h>
+
+#include <drm/bridge/analogix_dp.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_vop.h"
+
+#define to_dp(nm)	container_of(nm, struct rockchip_dp_device, nm)
+
+/* dp grf register offset */
+#define GRF_SOC_CON6                            0x025c
+#define GRF_EDP_LCD_SEL_MASK                    BIT(5)
+#define GRF_EDP_SEL_VOP_LIT                     BIT(5)
+#define GRF_EDP_SEL_VOP_BIG                     0
+
+struct rockchip_dp_device {
+	struct drm_device        *drm_dev;
+	struct device            *dev;
+	struct drm_encoder       encoder;
+	struct drm_display_mode  mode;
+
+	struct clk               *pclk;
+	struct regmap            *grf;
+	struct reset_control     *rst;
+
+	struct analogix_dp_plat_data plat_data;
+};
+
+static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
+{
+	reset_control_assert(dp->rst);
+	usleep_range(10, 20);
+	reset_control_deassert(dp->rst);
+
+	return 0;
+}
+
+static int rockchip_dp_poweron(struct analogix_dp_plat_data *plat_data)
+{
+	struct rockchip_dp_device *dp = to_dp(plat_data);
+	int ret;
+
+	ret = clk_prepare_enable(dp->pclk);
+	if (ret < 0) {
+		dev_err(dp->dev, "failed to enable pclk %d\n", ret);
+		return ret;
+	}
+
+	ret = rockchip_dp_pre_init(dp);
+	if (ret < 0) {
+		dev_err(dp->dev, "failed to dp pre init %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int rockchip_dp_powerdown(struct analogix_dp_plat_data *plat_data)
+{
+	struct rockchip_dp_device *dp = to_dp(plat_data);
+
+	clk_disable_unprepare(dp->pclk);
+
+	return 0;
+}
+
+static bool
+rockchip_dp_drm_encoder_mode_fixup(struct drm_encoder *encoder,
+				   const struct drm_display_mode *mode,
+				   struct drm_display_mode *adjusted_mode)
+{
+	/* do nothing */
+	return true;
+}
+
+static void rockchip_dp_drm_encoder_mode_set(struct drm_encoder *encoder,
+					     struct drm_display_mode *mode,
+					     struct drm_display_mode *adjusted)
+{
+	/* do nothing */
+}
+
+static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder)
+{
+	struct rockchip_dp_device *dp = to_dp(encoder);
+	int ret;
+	u32 val;
+
+	/*
+	 * FIXME(Yakir): driver should configure the CRTC output video
+	 * mode with the display information which indicated the monitor
+	 * support colorimetry.
+	 *
+	 * But don't know why the CRTC driver seems could only output the
+	 * RGBaaa rightly. For example, if connect the "innolux,n116bge"
+	 * eDP screen, EDID would indicated that screen only accepted the
+	 * 6bpc mode. But if I configure CRTC to RGB666 output, then eDP
+	 * screen would show a blue picture (RGB888 show a green picture).
+	 * But if I configure CTRC to RGBaaa, and eDP driver still keep
+	 * RGB666 input video mode, then screen would works prefect.
+	 */
+	ret = rockchip_drm_crtc_mode_config(encoder->crtc,
+					    DRM_MODE_CONNECTOR_eDP,
+					    ROCKCHIP_OUT_MODE_AAAA);
+	if (ret < 0) {
+		dev_err(dp->dev, "Could not set crtc mode config (%d)\n", ret);
+		return;
+	}
+
+	ret = rockchip_drm_encoder_get_mux_id(dp->dev->of_node, encoder);
+	if (ret < 0)
+		return;
+
+	if (ret)
+		val = GRF_EDP_SEL_VOP_LIT | (GRF_EDP_LCD_SEL_MASK << 16);
+	else
+		val = GRF_EDP_SEL_VOP_BIG | (GRF_EDP_LCD_SEL_MASK << 16);
+
+	dev_dbg(dp->dev, "vop %s output to dp\n", (ret) ? "LIT" : "BIG");
+
+	ret = regmap_write(dp->grf, GRF_SOC_CON6, val);
+	if (ret != 0) {
+		dev_err(dp->dev, "Could not write to GRF: %d\n", ret);
+		return;
+	}
+}
+
+static void rockchip_dp_drm_encoder_nop(struct drm_encoder *encoder)
+{
+	/* do nothing */
+}
+
+static struct drm_encoder_helper_funcs rockchip_dp_encoder_helper_funcs = {
+	.mode_fixup = rockchip_dp_drm_encoder_mode_fixup,
+	.mode_set = rockchip_dp_drm_encoder_mode_set,
+	.enable = rockchip_dp_drm_encoder_enable,
+	.disable = rockchip_dp_drm_encoder_nop,
+};
+
+static void rockchip_dp_drm_encoder_destroy(struct drm_encoder *encoder)
+{
+	drm_encoder_cleanup(encoder);
+}
+
+static struct drm_encoder_funcs rockchip_dp_encoder_funcs = {
+	.destroy = rockchip_dp_drm_encoder_destroy,
+};
+
+static int rockchip_dp_init(struct rockchip_dp_device *dp)
+{
+	struct device *dev = dp->dev;
+	struct device_node *np = dev->of_node;
+	int ret;
+
+	dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+	if (IS_ERR(dp->grf)) {
+		dev_err(dev, "failed to get rockchip,grf property\n");
+		return PTR_ERR(dp->grf);
+	}
+
+	dp->pclk = devm_clk_get(dev, "pclk");
+	if (IS_ERR(dp->pclk)) {
+		dev_err(dev, "failed to get pclk property\n");
+		return PTR_ERR(dp->pclk);
+	}
+
+	dp->rst = devm_reset_control_get(dev, "dp");
+	if (IS_ERR(dp->rst)) {
+		dev_err(dev, "failed to get dp reset control\n");
+		return PTR_ERR(dp->rst);
+	}
+
+	ret = clk_prepare_enable(dp->pclk);
+	if (ret < 0) {
+		dev_err(dp->dev, "failed to enable pclk %d\n", ret);
+		return ret;
+	}
+
+	ret = rockchip_dp_pre_init(dp);
+	if (ret < 0) {
+		dev_err(dp->dev, "failed to pre init %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int rockchip_dp_drm_create_encoder(struct rockchip_dp_device *dp)
+{
+	struct drm_encoder *encoder = &dp->encoder;
+	struct drm_device *drm_dev = dp->drm_dev;
+	struct device *dev = dp->dev;
+	int ret;
+
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
+							     dev->of_node);
+	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
+
+	ret = drm_encoder_init(drm_dev, encoder, &rockchip_dp_encoder_funcs,
+			       DRM_MODE_ENCODER_TMDS, NULL);
+	if (ret) {
+		DRM_ERROR("failed to initialize encoder with drm\n");
+		return ret;
+	}
+
+	drm_encoder_helper_add(encoder, &rockchip_dp_encoder_helper_funcs);
+
+	return 0;
+}
+
+static int rockchip_dp_bind(struct device *dev, struct device *master,
+			    void *data)
+{
+	struct rockchip_dp_device *dp = dev_get_drvdata(dev);
+	struct drm_device *drm_dev = data;
+	int ret;
+
+	/*
+	 * Just like the probe function said, we don't need the
+	 * device drvrate anymore, we should leave the charge to
+	 * analogix dp driver, set the device drvdata to NULL.
+	 */
+	dev_set_drvdata(dev, NULL);
+
+	ret = rockchip_dp_init(dp);
+	if (ret < 0)
+		return ret;
+
+	dp->drm_dev = drm_dev;
+
+	ret = rockchip_dp_drm_create_encoder(dp);
+	if (ret) {
+		DRM_ERROR("failed to create drm encoder\n");
+		return ret;
+	}
+
+	dp->plat_data.encoder = &dp->encoder;
+
+	dp->plat_data.dev_type = RK3288_DP;
+	dp->plat_data.power_on = rockchip_dp_poweron;
+	dp->plat_data.power_off = rockchip_dp_powerdown;
+
+	return analogix_dp_bind(dev, dp->drm_dev, &dp->plat_data);
+}
+
+static void rockchip_dp_unbind(struct device *dev, struct device *master,
+			       void *data)
+{
+	return analogix_dp_unbind(dev, master, data);
+}
+
+static const struct component_ops rockchip_dp_component_ops = {
+	.bind = rockchip_dp_bind,
+	.unbind = rockchip_dp_unbind,
+};
+
+static int rockchip_dp_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *panel_node, *port, *endpoint;
+	struct rockchip_dp_device *dp;
+	struct drm_panel *panel;
+
+	port = of_graph_get_port_by_id(dev->of_node, 1);
+	if (!port) {
+		dev_err(dev, "can't find output port\n");
+		return -EINVAL;
+	}
+
+	endpoint = of_get_child_by_name(port, "endpoint");
+	of_node_put(port);
+	if (!endpoint) {
+		dev_err(dev, "no output endpoint found\n");
+		return -EINVAL;
+	}
+
+	panel_node = of_graph_get_remote_port_parent(endpoint);
+	of_node_put(endpoint);
+	if (!panel_node) {
+		dev_err(dev, "no output node found\n");
+		return -EINVAL;
+	}
+
+	panel = of_drm_find_panel(panel_node);
+	if (!panel) {
+		DRM_ERROR("failed to find panel\n");
+		of_node_put(panel_node);
+		return -EPROBE_DEFER;
+	}
+
+	of_node_put(panel_node);
+
+	dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
+	if (!dp)
+		return -ENOMEM;
+
+	dp->dev = dev;
+
+	dp->plat_data.panel = panel;
+
+	/*
+	 * We just use the drvdata until driver run into component
+	 * add function, and then we would set drvdata to null, so
+	 * that analogix dp driver could take charge of the drvdata.
+	 */
+	platform_set_drvdata(pdev, dp);
+
+	return component_add(dev, &rockchip_dp_component_ops);
+}
+
+static int rockchip_dp_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &rockchip_dp_component_ops);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int rockchip_dp_suspend(struct device *dev)
+{
+	return analogix_dp_suspend(dev);
+}
+
+static int rockchip_dp_resume(struct device *dev)
+{
+	return analogix_dp_resume(dev);
+}
+#endif
+
+static const struct dev_pm_ops rockchip_dp_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(rockchip_dp_suspend, rockchip_dp_resume)
+};
+
+static const struct of_device_id rockchip_dp_dt_ids[] = {
+	{.compatible = "rockchip,rk3288-dp",},
+	{}
+};
+MODULE_DEVICE_TABLE(of, rockchip_dp_dt_ids);
+
+static struct platform_driver rockchip_dp_driver = {
+	.probe = rockchip_dp_probe,
+	.remove = rockchip_dp_remove,
+	.driver = {
+		   .name = "rockchip-dp",
+		   .owner = THIS_MODULE,
+		   .pm = &rockchip_dp_pm_ops,
+		   .of_match_table = of_match_ptr(rockchip_dp_dt_ids),
+	},
+};
+
+module_platform_driver(rockchip_dp_driver);
+
+MODULE_AUTHOR("Yakir Yang <ykk at rock-chips.com>");
+MODULE_AUTHOR("Jeff chen <jeff.chen at rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip Specific Analogix-DP Driver Extension");
+MODULE_LICENSE("GPL v2");
diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h
index d1188db..25afb31 100644
--- a/include/drm/bridge/analogix_dp.h
+++ b/include/drm/bridge/analogix_dp.h
@@ -15,6 +15,7 @@
 
 enum analogix_dp_devtype {
 	EXYNOS_DP,
+	RK3288_DP,
 };
 
 struct analogix_dp_plat_data {
-- 
1.9.1




From ykk at rock-chips.com  Thu Jan 21 17:55:33 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 22 Jan 2016 09:55:33 +0800
Subject: [PATCH v13 08/17] dt-bindings: add document for rockchip variant of
 analogix_dp
In-Reply-To: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
References: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1453427733-24083-1-git-send-email-ykk@rock-chips.com>

Rockchip DP driver is a helper driver of analogix_dp coder driver,
so most of the DT property should be descriped in analogix_dp document.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Acked-by: Rob Herring <robh at kernel.org>
Reviewed-by: Heiko Stuebner <heiko at sntech.de>
---
Changes in v13: None
Changes in v12: None
Changes in v11: None
Changes in v10:
- Add the ack from Rob Herring

Changes in v9:
- Document more details for 'ports' property.

Changes in v8:
- Modify the commit subject name. (Heiko)

Changes in v7: None
Changes in v6: None
Changes in v5:
- Split binding doc's from driver changes. (Rob)
- Add eDP hotplug pinctrl property. (Heiko)

Changes in v4: None
Changes in v3: None
Changes in v2: None

 .../display/rockchip/analogix_dp-rockchip.txt      | 91 ++++++++++++++++++++++
 1 file changed, 91 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/rockchip/analogix_dp-rockchip.txt

diff --git a/Documentation/devicetree/bindings/display/rockchip/analogix_dp-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/analogix_dp-rockchip.txt
new file mode 100644
index 0000000..04d99e3
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/rockchip/analogix_dp-rockchip.txt
@@ -0,0 +1,91 @@
+Rockchip RK3288 specific extensions to the Analogix Display Port
+================================
+
+Required properties:
+- compatible: "rockchip,rk3288-edp";
+
+- reg: physical base address of the controller and length
+
+- clocks: from common clock binding: handle to dp clock.
+	  of memory mapped region.
+
+- clock-names: from common clock binding:
+	       Required elements: "dp" "pclk"
+
+- resets: Must contain an entry for each entry in reset-names.
+	  See ../reset/reset.txt for details.
+
+- pinctrl-names: Names corresponding to the chip hotplug pinctrl states.
+- pinctrl-0: pin-control mode. should be <&edp_hpd>
+
+- reset-names: Must include the name "dp"
+
+- rockchip,grf: this soc should set GRF regs, so need get grf here.
+
+- ports: there are 2 port nodes with endpoint definitions as defined in
+  Documentation/devicetree/bindings/media/video-interfaces.txt.
+    Port 0: contained 2 endpoints, connecting to the output of vop.
+    Port 1: contained 1 endpoint, connecting to the input of panel.
+
+For the below properties, please refer to Analogix DP binding document:
+ * Documentation/devicetree/bindings/drm/bridge/analogix_dp.txt
+- phys (required)
+- phy-names (required)
+- hpd-gpios (optional)
+-------------------------------------------------------------------------------
+
+Example:
+	dp-controller: dp at ff970000 {
+		compatible = "rockchip,rk3288-dp";
+		reg = <0xff970000 0x4000>;
+		interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru SCLK_EDP>, <&cru PCLK_EDP_CTRL>;
+		clock-names = "dp", "pclk";
+		phys = <&dp_phy>;
+		phy-names = "dp";
+
+		rockchip,grf = <&grf>;
+		resets = <&cru 111>;
+		reset-names = "dp";
+
+		pinctrl-names = "default";
+		pinctrl-0 = <&edp_hpd>;
+
+		status = "disabled";
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			edp_in: port at 0 {
+				reg = <0>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				edp_in_vopb: endpoint at 0 {
+					reg = <0>;
+					remote-endpoint = <&vopb_out_edp>;
+				};
+				edp_in_vopl: endpoint at 1 {
+					reg = <1>;
+					remote-endpoint = <&vopl_out_edp>;
+				};
+			};
+
+			edp_out: port at 1 {
+				reg = <1>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+				edp_out_panel: endpoint {
+					reg = <0>;
+					remote-endpoint = <&panel_in_edp>
+				};
+			};
+		};
+	};
+
+	pinctrl {
+		edp {
+			edp_hpd: edp-hpd {
+				rockchip,pins = <7 11 RK_FUNC_2 &pcfg_pull_none>;
+			};
+		};
+	};
-- 
1.9.1




From ykk at rock-chips.com  Thu Jan 21 17:55:40 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 22 Jan 2016 09:55:40 +0800
Subject: [PATCH v13 09/17] phy: Add driver for rockchip Display Port PHY
In-Reply-To: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
References: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1453427740-24126-1-git-send-email-ykk@rock-chips.com>

Add phy driver for the Rockchip DisplayPort PHY module. This
is required to get DisplayPort working in Rockchip SoCs.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Reviewed-by: Heiko Stuebner <heiko at sntech.de>
---
Changes in v13: None
Changes in v12:
- Re-order the include headers file alphabetically in phy-rockchip-dp.c (Jingoo)

Changes in v11: None
Changes in v10:
- Fix the wrong macro value of GRF_EDP_REF_CLK_SEL_INTER_HIWORD_MASK
    BIT(4) -> BIT(20)

Changes in v9:
- Removed the unused the variable "res" in probe function. (Heiko)
- Removed the unused head file.

Changes in v8:
- Fix the mixed spacers on macro definitions. (Heiko)
- Remove the unnecessary empty line after clk_prepare_enable. (Heiko)

Changes in v7:
- Simply the commit message. (Kishon)
- Symmetrical enable/disbale the phy clock and power. (Kishon)

Changes in v6: None
Changes in v5:
- Remove "reg" DT property, cause driver could poweron/poweroff phy via
  the exist "grf" syscon already. And rename the example DT node from
  "edp_phy: phy at ff770274" to "edp_phy: edp-phy" directly. (Heiko)
- Add deivce_node at the front of driver, update phy_ops type from "static
  struct" to "static const struct". And correct the input paramters of
  devm_phy_create() interfaces. (Heiko)

Changes in v4:
- Add commit message, and remove the redundant rockchip_dp_phy_init()
  function, move those code to probe() method. And remove driver .owner
  number. (Kishon)

Changes in v3:
- Suggest, add rockchip dp phy driver, collect the phy clocks and
  power control. (Heiko)

Changes in v2: None

 drivers/phy/Kconfig           |   7 ++
 drivers/phy/Makefile          |   1 +
 drivers/phy/phy-rockchip-dp.c | 151 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 159 insertions(+)
 create mode 100644 drivers/phy/phy-rockchip-dp.c

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 03cb3ea..cdb1c39 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -320,6 +320,13 @@ config PHY_ROCKCHIP_USB
 	help
 	  Enable this to support the Rockchip USB 2.0 PHY.
 
+config PHY_ROCKCHIP_DP
+	tristate "Rockchip Display Port PHY Driver"
+	depends on ARCH_ROCKCHIP && OF
+	select GENERIC_PHY
+	help
+	  Enable this to support the Rockchip Display Port PHY.
+
 config PHY_ST_SPEAR1310_MIPHY
 	tristate "ST SPEAR1310-MIPHY driver"
 	select GENERIC_PHY
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 075db1a..b1700cd 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -35,6 +35,7 @@ phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2)	+= phy-s5pv210-usb2.o
 obj-$(CONFIG_PHY_EXYNOS5_USBDRD)	+= phy-exynos5-usbdrd.o
 obj-$(CONFIG_PHY_QCOM_APQ8064_SATA)	+= phy-qcom-apq8064-sata.o
 obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
+obj-$(CONFIG_PHY_ROCKCHIP_DP)		+= phy-rockchip-dp.o
 obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA)	+= phy-qcom-ipq806x-sata.o
 obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY)	+= phy-spear1310-miphy.o
 obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)	+= phy-spear1340-miphy.o
diff --git a/drivers/phy/phy-rockchip-dp.c b/drivers/phy/phy-rockchip-dp.c
new file mode 100644
index 0000000..88f09ec
--- /dev/null
+++ b/drivers/phy/phy-rockchip-dp.c
@@ -0,0 +1,151 @@
+/*
+ * Rockchip DP PHY driver
+ *
+ * Copyright (C) 2015 FuZhou Rockchip Co., Ltd.
+ * Author: Yakir Yang <ykk@@rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ */
+
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define GRF_SOC_CON12                           0x0274
+
+#define GRF_EDP_REF_CLK_SEL_INTER_HIWORD_MASK   BIT(20)
+#define GRF_EDP_REF_CLK_SEL_INTER               BIT(4)
+
+#define GRF_EDP_PHY_SIDDQ_HIWORD_MASK           BIT(21)
+#define GRF_EDP_PHY_SIDDQ_ON                    0
+#define GRF_EDP_PHY_SIDDQ_OFF                   BIT(5)
+
+struct rockchip_dp_phy {
+	struct device  *dev;
+	struct regmap  *grf;
+	struct clk     *phy_24m;
+};
+
+static int rockchip_set_phy_state(struct phy *phy, bool enable)
+{
+	struct rockchip_dp_phy *dp = phy_get_drvdata(phy);
+	int ret;
+
+	if (enable) {
+		ret = regmap_write(dp->grf, GRF_SOC_CON12,
+				   GRF_EDP_PHY_SIDDQ_HIWORD_MASK |
+				   GRF_EDP_PHY_SIDDQ_ON);
+		if (ret < 0) {
+			dev_err(dp->dev, "Can't enable PHY power %d\n", ret);
+			return ret;
+		}
+
+		ret = clk_prepare_enable(dp->phy_24m);
+	} else {
+		clk_disable_unprepare(dp->phy_24m);
+
+		ret = regmap_write(dp->grf, GRF_SOC_CON12,
+				   GRF_EDP_PHY_SIDDQ_HIWORD_MASK |
+				   GRF_EDP_PHY_SIDDQ_OFF);
+	}
+
+	return ret;
+}
+
+static int rockchip_dp_phy_power_on(struct phy *phy)
+{
+	return rockchip_set_phy_state(phy, true);
+}
+
+static int rockchip_dp_phy_power_off(struct phy *phy)
+{
+	return rockchip_set_phy_state(phy, false);
+}
+
+static const struct phy_ops rockchip_dp_phy_ops = {
+	.power_on	= rockchip_dp_phy_power_on,
+	.power_off	= rockchip_dp_phy_power_off,
+	.owner		= THIS_MODULE,
+};
+
+static int rockchip_dp_phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct phy_provider *phy_provider;
+	struct rockchip_dp_phy *dp;
+	struct phy *phy;
+	int ret;
+
+	if (!np)
+		return -ENODEV;
+
+	dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
+	if (IS_ERR(dp))
+		return -ENOMEM;
+
+	dp->dev = dev;
+
+	dp->phy_24m = devm_clk_get(dev, "24m");
+	if (IS_ERR(dp->phy_24m)) {
+		dev_err(dev, "cannot get clock 24m\n");
+		return PTR_ERR(dp->phy_24m);
+	}
+
+	ret = clk_set_rate(dp->phy_24m, 24000000);
+	if (ret < 0) {
+		dev_err(dp->dev, "cannot set clock phy_24m %d\n", ret);
+		return ret;
+	}
+
+	dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+	if (IS_ERR(dp->grf)) {
+		dev_err(dev, "rk3288-dp needs rockchip,grf property\n");
+		return PTR_ERR(dp->grf);
+	}
+
+	ret = regmap_write(dp->grf, GRF_SOC_CON12, GRF_EDP_REF_CLK_SEL_INTER |
+			   GRF_EDP_REF_CLK_SEL_INTER_HIWORD_MASK);
+	if (ret != 0) {
+		dev_err(dp->dev, "Could not config GRF edp ref clk: %d\n", ret);
+		return ret;
+	}
+
+	phy = devm_phy_create(dev, np, &rockchip_dp_phy_ops);
+	if (IS_ERR(phy)) {
+		dev_err(dev, "failed to create phy\n");
+		return PTR_ERR(phy);
+	}
+	phy_set_drvdata(phy, dp);
+
+	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+	return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id rockchip_dp_phy_dt_ids[] = {
+	{ .compatible = "rockchip,rk3288-dp-phy" },
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, rockchip_dp_phy_dt_ids);
+
+static struct platform_driver rockchip_dp_phy_driver = {
+	.probe		= rockchip_dp_phy_probe,
+	.driver		= {
+		.name	= "rockchip-dp-phy",
+		.of_match_table = rockchip_dp_phy_dt_ids,
+	},
+};
+
+module_platform_driver(rockchip_dp_phy_driver);
+
+MODULE_AUTHOR("Yakir Yang <ykk at rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip DP PHY driver");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1




From ykk at rock-chips.com  Thu Jan 21 17:55:47 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 22 Jan 2016 09:55:47 +0800
Subject: [PATCH v13 10/17] dt-bindings: add document for rockchip dp phy
In-Reply-To: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
References: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1453427747-24187-1-git-send-email-ykk@rock-chips.com>

Add dt binding documentation for rockchip display port PHY.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Acked-by: Rob Herring <robh at kernel.org>
Reviewed-by: Heiko Stuebner <heiko at sntech.de>
---
Changes in v13: None
Changes in v12: None
Changes in v11:
- Correct the title of this rockchip dp phy document(Rob)
- Add the ack from Rob Herring

Changes in v10: None
Changes in v9: None
Changes in v8:
- Remove the specific address in the example node name. (Heiko)

Changes in v7:
- Simplify the commit message. (Kishon)

Changes in v6: None
Changes in v5:
- Split binding doc's from driver changes. (Rob)
- Update the rockchip,grf explain in document, and correct the clock required
  elemets in document. (Rob & Heiko)

Changes in v4: None
Changes in v3: None
Changes in v2: None

 .../devicetree/bindings/phy/rockchip-dp-phy.txt    | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/rockchip-dp-phy.txt

diff --git a/Documentation/devicetree/bindings/phy/rockchip-dp-phy.txt b/Documentation/devicetree/bindings/phy/rockchip-dp-phy.txt
new file mode 100644
index 0000000..50c4f9b
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/rockchip-dp-phy.txt
@@ -0,0 +1,22 @@
+Rockchip specific extensions to the Analogix Display Port PHY
+------------------------------------
+
+Required properties:
+- compatible : should be one of the following supported values:
+	 - "rockchip.rk3288-dp-phy"
+- clocks: from common clock binding: handle to dp clock.
+	of memory mapped region.
+- clock-names: from common clock binding:
+	Required elements: "24m"
+- rockchip,grf: phandle to the syscon managing the "general register files"
+- #phy-cells : from the generic PHY bindings, must be 0;
+
+Example:
+
+edp_phy: edp-phy {
+	compatible = "rockchip,rk3288-dp-phy";
+	rockchip,grf = <&grf>;
+	clocks = <&cru SCLK_EDP_24M>;
+	clock-names = "24m";
+	#phy-cells = <0>;
+};
-- 
1.9.1




From ykk at rock-chips.com  Thu Jan 21 17:56:06 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 22 Jan 2016 09:56:06 +0800
Subject: [PATCH v13 12/17] drm: bridge: analogix/dp: add max link rate and
 lane count limit for RK3288
In-Reply-To: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
References: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1453427766-24472-1-git-send-email-ykk@rock-chips.com>

There are some IP limit on rk3288 that only support 4 physical lanes
of 2.7/1.6 Gbps/lane, so seprate them out by device_type flag.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Tested-by: Javier Martinez Canillas <javier at osg.samsung.com>
---
Changes in v13: None
Changes in v12: None
Changes in v11: None
Changes in v10:
- Remove the surplus "plat_data" check. (Heiko)
-       switch (dp->plat_data && dp->plat_data->dev_type) {
+       switch (dp->plat_data->dev_type) {

Changes in v9: None
Changes in v8: None
Changes in v7: None
Changes in v6: None
Changes in v5: None
Changes in v4:
- Seprate the link-rate and lane-count limit out with the device_type
  flag. (Thierry)

Changes in v3: None
Changes in v2: None

 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 33 ++++++++++++++--------
 drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  4 +--
 2 files changed, 23 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index 26359f4..a62e8d0 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -893,8 +893,8 @@ static void analogix_dp_commit(struct analogix_dp_device *dp)
 		return;
 	}
 
-	ret = analogix_dp_set_link_train(dp, dp->video_info.lane_count,
-					 dp->video_info.link_rate);
+	ret = analogix_dp_set_link_train(dp, dp->video_info.max_lane_count,
+					 dp->video_info.max_link_rate);
 	if (ret) {
 		dev_err(dp->dev, "unable to do link train\n");
 		return;
@@ -1203,16 +1203,25 @@ static int analogix_dp_dt_parse_pdata(struct analogix_dp_device *dp)
 	struct device_node *dp_node = dp->dev->of_node;
 	struct video_info *video_info = &dp->video_info;
 
-	if (of_property_read_u32(dp_node, "samsung,link-rate",
-				 &video_info->link_rate)) {
-		dev_err(dp->dev, "failed to get link-rate\n");
-		return -EINVAL;
-	}
-
-	if (of_property_read_u32(dp_node, "samsung,lane-count",
-				 &video_info->lane_count)) {
-		dev_err(dp->dev, "failed to get lane-count\n");
-		return -EINVAL;
+	switch (dp->plat_data->dev_type) {
+	case RK3288_DP:
+		/*
+		 * Like Rk3288 DisplayPort TRM indicate that "Main link
+		 * containing 4 physical lanes of 2.7/1.62 Gbps/lane".
+		 */
+		video_info->max_link_rate = 0x0A;
+		video_info->max_lane_count = 0x04;
+		break;
+	case EXYNOS_DP:
+		/*
+		 * NOTE: those property parseing code is used for
+		 * providing backward compatibility for samsung platform.
+		 */
+		of_property_read_u32(dp_node, "samsung,link-rate",
+				     &video_info->max_link_rate);
+		of_property_read_u32(dp_node, "samsung,lane-count",
+				     &video_info->max_lane_count);
+		break;
 	}
 
 	return 0;
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
index d488dd6..6cc53d4 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
@@ -123,8 +123,8 @@ struct video_info {
 	enum color_coefficient ycbcr_coeff;
 	enum color_depth color_depth;
 
-	int link_rate;
-	enum link_lane_count_type lane_count;
+	int max_link_rate;
+	enum link_lane_count_type max_lane_count;
 };
 
 struct link_train {
-- 
1.9.1




From ykk at rock-chips.com  Thu Jan 21 17:56:11 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 22 Jan 2016 09:56:11 +0800
Subject: [PATCH v13 13/17] drm: bridge: analogix/dp: try force hpd after plug
 in lookup failed
In-Reply-To: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
References: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1453427771-24543-1-git-send-email-ykk@rock-chips.com>

Some edp screen do not have hpd signal, so we can't just return
failed when hpd plug in detect failed.

This is an hardware property, so we need add a devicetree property
"analogix,need-force-hpd" to indicate this sutiation.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Acked-by: Rob Herring <robh at kernel.org>
Tested-by: Javier Martinez Canillas <javier at osg.samsung.com>
---
Changes in v13: None
Changes in v12: None
Changes in v11:
- Rename the "analogix,need-force-hpd" to common 'force-hpd' (Rob)
- Add the ack from Rob Herring

Changes in v10: None
Changes in v9: None
Changes in v8: None
Changes in v7: None
Changes in v6: None
Changes in v5: None
Changes in v4: None
Changes in v3:
- Add "analogix,need-force-hpd" to indicate whether driver need foce
  hpd when hpd detect failed.

Changes in v2: None

 .../bindings/display/bridge/analogix_dp.txt        |  4 ++-
 .../bindings/display/exynos/exynos_dp.txt          |  1 +
 .../display/rockchip/analogix_dp-rockchip.txt      |  1 +
 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 35 ++++++++++++++++++----
 drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  2 ++
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  |  9 ++++++
 6 files changed, 46 insertions(+), 6 deletions(-)

diff --git a/Documentation/devicetree/bindings/display/bridge/analogix_dp.txt b/Documentation/devicetree/bindings/display/bridge/analogix_dp.txt
index 7659a7a..4f2ba8c 100644
--- a/Documentation/devicetree/bindings/display/bridge/analogix_dp.txt
+++ b/Documentation/devicetree/bindings/display/bridge/analogix_dp.txt
@@ -22,6 +22,9 @@ Required properties for dp-controller:
 		from general PHY binding: Should be "dp".
 
 Optional properties for dp-controller:
+	-force-hpd:
+		Indicate driver need force hpd when hpd detect failed, this
+		is used for some eDP screen which don't have hpd signal.
 	-hpd-gpios:
 		Hotplug detect GPIO.
 		Indicates which GPIO should be used for hotplug detection
@@ -31,7 +34,6 @@ Optional properties for dp-controller:
 		* Documentation/devicetree/bindings/display/exynos/exynos_dp.txt
 		* Documentation/devicetree/bindings/video/analogix_dp-rockchip.txt
 
-
 [1]: Documentation/devicetree/bindings/media/video-interfaces.txt
 -------------------------------------------------------------------------------
 
diff --git a/Documentation/devicetree/bindings/display/exynos/exynos_dp.txt b/Documentation/devicetree/bindings/display/exynos/exynos_dp.txt
index 4fa8aca..ade5d8e 100644
--- a/Documentation/devicetree/bindings/display/exynos/exynos_dp.txt
+++ b/Documentation/devicetree/bindings/display/exynos/exynos_dp.txt
@@ -56,6 +56,7 @@ For the below properties, please refer to Analogix DP binding document:
 	-phys (required)
 	-phy-names (required)
 	-hpd-gpios (optional)
+	 force-hpd (optional)
 
 Deprecated properties for DisplayPort:
 -interlaced:            deprecated prop that can parsed from drm_display_mode.
diff --git a/Documentation/devicetree/bindings/display/rockchip/analogix_dp-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/analogix_dp-rockchip.txt
index 04d99e3..e832ff9 100644
--- a/Documentation/devicetree/bindings/display/rockchip/analogix_dp-rockchip.txt
+++ b/Documentation/devicetree/bindings/display/rockchip/analogix_dp-rockchip.txt
@@ -32,6 +32,7 @@ For the below properties, please refer to Analogix DP binding document:
 - phys (required)
 - phy-names (required)
 - hpd-gpios (optional)
+- force-hpd (optional)
 -------------------------------------------------------------------------------
 
 Example:
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index a62e8d0..13986b7 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -62,15 +62,38 @@ static int analogix_dp_detect_hpd(struct analogix_dp_device *dp)
 {
 	int timeout_loop = 0;
 
-	while (analogix_dp_get_plug_in_status(dp) != 0) {
+	while (timeout_loop < DP_TIMEOUT_LOOP_COUNT) {
+		if (analogix_dp_get_plug_in_status(dp) == 0)
+			return 0;
+
 		timeout_loop++;
-		if (timeout_loop > DP_TIMEOUT_LOOP_COUNT) {
-			dev_err(dp->dev, "failed to get hpd plug status\n");
-			return -ETIMEDOUT;
-		}
 		usleep_range(10, 11);
 	}
 
+	/*
+	 * Some edp screen do not have hpd signal, so we can't just
+	 * return failed when hpd plug in detect failed, DT property
+	 * "force-hpd" would indicate whether driver need this.
+	 */
+	if (!dp->force_hpd)
+		return -ETIMEDOUT;
+
+	/*
+	 * The eDP TRM indicate that if HPD_STATUS(RO) is 0, AUX CH
+	 * will not work, so we need to give a force hpd action to
+	 * set HPD_STATUS manually.
+	 */
+	dev_dbg(dp->dev, "failed to get hpd plug status, try to force hpd\n");
+
+	analogix_dp_force_hpd(dp);
+
+	if (analogix_dp_get_plug_in_status(dp) != 0) {
+		dev_err(dp->dev, "failed to get hpd plug in status\n");
+		return -EINVAL;
+	}
+
+	dev_dbg(dp->dev, "success to get plug in status after force hpd\n");
+
 	return 0;
 }
 
@@ -1291,6 +1314,8 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev,
 	if (IS_ERR(dp->reg_base))
 		return PTR_ERR(dp->reg_base);
 
+	dp->force_hpd = of_property_read_bool(dev->of_node, "force-hpd");
+
 	dp->hpd_gpio = of_get_named_gpio(dev->of_node, "hpd-gpios", 0);
 	if (!gpio_is_valid(dp->hpd_gpio))
 		dp->hpd_gpio = of_get_named_gpio(dev->of_node,
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
index 6cc53d4..a8cba64 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
@@ -154,6 +154,7 @@ struct analogix_dp_device {
 	struct phy		*phy;
 	int			dpms_mode;
 	int			hpd_gpio;
+	bool                    force_hpd;
 
 	struct analogix_dp_plat_data *plat_data;
 };
@@ -174,6 +175,7 @@ void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp,
 				       bool enable);
 void analogix_dp_init_analog_func(struct analogix_dp_device *dp);
 void analogix_dp_init_hpd(struct analogix_dp_device *dp);
+void analogix_dp_force_hpd(struct analogix_dp_device *dp);
 enum dp_irq_type analogix_dp_get_irq_type(struct analogix_dp_device *dp);
 void analogix_dp_clear_hotplug_interrupts(struct analogix_dp_device *dp);
 void analogix_dp_reset_aux(struct analogix_dp_device *dp);
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
index 1e24b37..cba3ffd 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
@@ -365,6 +365,15 @@ void analogix_dp_init_hpd(struct analogix_dp_device *dp)
 	writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
 }
 
+void analogix_dp_force_hpd(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
+	reg = (F_HPD | HPD_CTRL);
+	writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
+}
+
 enum dp_irq_type analogix_dp_get_irq_type(struct analogix_dp_device *dp)
 {
 	u32 reg;
-- 
1.9.1




From ykk at rock-chips.com  Thu Jan 21 17:55:59 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 22 Jan 2016 09:55:59 +0800
Subject: [PATCH v13 11/17] drm: bridge: analogix/dp: add some rk3288 special
 registers setting
In-Reply-To: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
References: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1453427759-24403-1-git-send-email-ykk@rock-chips.com>

RK3288 need some special registers setting, we can separate
them out by the dev_type of plat_data.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v13: None
Changes in v12: None
Changes in v11: None
Changes in v10: None
Changes in v9: None
Changes in v8: None
Changes in v7: None
Changes in v6: None
Changes in v5: None
Changes in v4: None
Changes in v3: None
Changes in v2:
- Fix compile failed dut to phy_pd_addr variable misspell error

 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c | 76 ++++++++++++++---------
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h | 12 ++++
 2 files changed, 60 insertions(+), 28 deletions(-)

diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
index 3858df5..1e24b37 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
@@ -15,6 +15,8 @@
 #include <linux/delay.h>
 #include <linux/gpio.h>
 
+#include <drm/bridge/analogix_dp.h>
+
 #include "analogix_dp_core.h"
 #include "analogix_dp_reg.h"
 
@@ -72,6 +74,14 @@ void analogix_dp_init_analog_param(struct analogix_dp_device *dp)
 	reg = SEL_24M | TX_DVDD_BIT_1_0625V;
 	writel(reg, dp->reg_base + ANALOGIX_DP_ANALOG_CTL_2);
 
+	if (dp->plat_data && (dp->plat_data->dev_type == RK3288_DP)) {
+		writel(REF_CLK_24M, dp->reg_base + ANALOGIX_DP_PLL_REG_1);
+		writel(0x95, dp->reg_base + ANALOGIX_DP_PLL_REG_2);
+		writel(0x40, dp->reg_base + ANALOGIX_DP_PLL_REG_3);
+		writel(0x58, dp->reg_base + ANALOGIX_DP_PLL_REG_4);
+		writel(0x22, dp->reg_base + ANALOGIX_DP_PLL_REG_5);
+	}
+
 	reg = DRIVE_DVDD_BIT_1_0625V | VCO_BIT_600_MICRO;
 	writel(reg, dp->reg_base + ANALOGIX_DP_ANALOG_CTL_3);
 
@@ -206,81 +216,85 @@ void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp,
 				       bool enable)
 {
 	u32 reg;
+	u32 phy_pd_addr = ANALOGIX_DP_PHY_PD;
+
+	if (dp->plat_data && (dp->plat_data->dev_type == RK3288_DP))
+		phy_pd_addr = ANALOGIX_DP_PD;
 
 	switch (block) {
 	case AUX_BLOCK:
 		if (enable) {
-			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg = readl(dp->reg_base + phy_pd_addr);
 			reg |= AUX_PD;
-			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+			writel(reg, dp->reg_base + phy_pd_addr);
 		} else {
-			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg = readl(dp->reg_base + phy_pd_addr);
 			reg &= ~AUX_PD;
-			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+			writel(reg, dp->reg_base + phy_pd_addr);
 		}
 		break;
 	case CH0_BLOCK:
 		if (enable) {
-			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg = readl(dp->reg_base + phy_pd_addr);
 			reg |= CH0_PD;
-			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+			writel(reg, dp->reg_base + phy_pd_addr);
 		} else {
-			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg = readl(dp->reg_base + phy_pd_addr);
 			reg &= ~CH0_PD;
-			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+			writel(reg, dp->reg_base + phy_pd_addr);
 		}
 		break;
 	case CH1_BLOCK:
 		if (enable) {
-			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg = readl(dp->reg_base + phy_pd_addr);
 			reg |= CH1_PD;
-			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+			writel(reg, dp->reg_base + phy_pd_addr);
 		} else {
-			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg = readl(dp->reg_base + phy_pd_addr);
 			reg &= ~CH1_PD;
-			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+			writel(reg, dp->reg_base + phy_pd_addr);
 		}
 		break;
 	case CH2_BLOCK:
 		if (enable) {
-			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg = readl(dp->reg_base + phy_pd_addr);
 			reg |= CH2_PD;
-			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+			writel(reg, dp->reg_base + phy_pd_addr);
 		} else {
-			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg = readl(dp->reg_base + phy_pd_addr);
 			reg &= ~CH2_PD;
-			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+			writel(reg, dp->reg_base + phy_pd_addr);
 		}
 		break;
 	case CH3_BLOCK:
 		if (enable) {
-			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg = readl(dp->reg_base + phy_pd_addr);
 			reg |= CH3_PD;
-			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+			writel(reg, dp->reg_base + phy_pd_addr);
 		} else {
-			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg = readl(dp->reg_base + phy_pd_addr);
 			reg &= ~CH3_PD;
-			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+			writel(reg, dp->reg_base + phy_pd_addr);
 		}
 		break;
 	case ANALOG_TOTAL:
 		if (enable) {
-			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg = readl(dp->reg_base + phy_pd_addr);
 			reg |= DP_PHY_PD;
-			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+			writel(reg, dp->reg_base + phy_pd_addr);
 		} else {
-			reg = readl(dp->reg_base + ANALOGIX_DP_PHY_PD);
+			reg = readl(dp->reg_base + phy_pd_addr);
 			reg &= ~DP_PHY_PD;
-			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+			writel(reg, dp->reg_base + phy_pd_addr);
 		}
 		break;
 	case POWER_ALL:
 		if (enable) {
 			reg = DP_PHY_PD | AUX_PD | CH3_PD | CH2_PD |
 				CH1_PD | CH0_PD;
-			writel(reg, dp->reg_base + ANALOGIX_DP_PHY_PD);
+			writel(reg, dp->reg_base + phy_pd_addr);
 		} else {
-			writel(0x00, dp->reg_base + ANALOGIX_DP_PHY_PD);
+			writel(0x00, dp->reg_base + phy_pd_addr);
 		}
 		break;
 	default:
@@ -399,8 +413,14 @@ void analogix_dp_init_aux(struct analogix_dp_device *dp)
 	analogix_dp_reset_aux(dp);
 
 	/* Disable AUX transaction H/W retry */
-	reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3) | AUX_HW_RETRY_COUNT_SEL(0) |
-	      AUX_HW_RETRY_INTERVAL_600_MICROSECONDS;
+	if (dp->plat_data && (dp->plat_data->dev_type == RK3288_DP))
+		reg = AUX_BIT_PERIOD_EXPECTED_DELAY(0) |
+		      AUX_HW_RETRY_COUNT_SEL(3) |
+		      AUX_HW_RETRY_INTERVAL_600_MICROSECONDS;
+	else
+		reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3) |
+		      AUX_HW_RETRY_COUNT_SEL(0) |
+		      AUX_HW_RETRY_INTERVAL_600_MICROSECONDS;
 	writel(reg, dp->reg_base + ANALOGIX_DP_AUX_HW_RETRY_CTL);
 
 	/* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
index 738db4c..337912b 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
@@ -22,6 +22,14 @@
 #define ANALOGIX_DP_VIDEO_CTL_8			0x3C
 #define ANALOGIX_DP_VIDEO_CTL_10		0x44
 
+#define ANALOGIX_DP_PLL_REG_1			0xfc
+#define ANALOGIX_DP_PLL_REG_2			0x9e4
+#define ANALOGIX_DP_PLL_REG_3			0x9e8
+#define ANALOGIX_DP_PLL_REG_4			0x9ec
+#define ANALOGIX_DP_PLL_REG_5			0xa00
+
+#define ANALOGIX_DP_PD				0x12c
+
 #define ANALOGIX_DP_LANE_MAP			0x35C
 
 #define ANALOGIX_DP_ANALOG_CTL_1		0x370
@@ -154,6 +162,10 @@
 #define VSYNC_POLARITY_CFG			(0x1 << 1)
 #define HSYNC_POLARITY_CFG			(0x1 << 0)
 
+/* ANALOGIX_DP_PLL_REG_1 */
+#define REF_CLK_24M				(0x1 << 1)
+#define REF_CLK_27M				(0x0 << 1)
+
 /* ANALOGIX_DP_LANE_MAP */
 #define LANE3_MAP_LOGIC_LANE_0			(0x0 << 6)
 #define LANE3_MAP_LOGIC_LANE_1			(0x1 << 6)
-- 
1.9.1




From ykk at rock-chips.com  Thu Jan 21 17:56:17 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 22 Jan 2016 09:56:17 +0800
Subject: [PATCH v13 14/17] drm: bridge: analogix/dp: move hpd detect to
 connector detect function
In-Reply-To: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
References: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1453427777-24610-1-git-send-email-ykk@rock-chips.com>

This change just make a little clean to make code more like
drm core expect, move hdp detect code from bridge->enable(),
and place them into connector->detect().

Note: Gustavo Padovan try to remove the controller and phy
power on function in bind time at bellow commit:
	drm/exynos: do not start enabling DP at bind() phase

But for now the connector status don't hardcode to connected,
need to operate dp phy in .detect function, so we need to revert
parts if Gustavo Padovan's changes, add phy poweron
function in bind time.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Tested-by: Javier Martinez Canillas <javier at osg.samsung.com>
---
Changes in v13: None
Changes in v12: None
Changes in v11:
- Revert parts of Gustavo Padovan's changes in commit:
	drm/exynos: do not start enabling DP at bind() phase
  Add dp phy poweron function in bind time.
- Move the panel prepare from get_modes time to bind time, and move
  the panel unprepare from bridge->disable to unbind time. (Heiko)

Changes in v10: None
Changes in v9: None
Changes in v8: None
Changes in v7: None
Changes in v6: None
Changes in v5: None
Changes in v4:
- Take Jingoo suggest, add commit messages.

Changes in v3:
- move dp hpd detect to connector detect function.

Changes in v2: None

 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 38 ++++++++++++----------
 1 file changed, 20 insertions(+), 18 deletions(-)

diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index 13986b7..cfdf695 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -904,12 +904,6 @@ static void analogix_dp_commit(struct analogix_dp_device *dp)
 			DRM_ERROR("failed to disable the panel\n");
 	}
 
-	ret = analogix_dp_detect_hpd(dp);
-	if (ret) {
-		/* Cable has been disconnected, we're done */
-		return;
-	}
-
 	ret = analogix_dp_handle_edid(dp);
 	if (ret) {
 		dev_err(dp->dev, "unable to handle edid\n");
@@ -972,6 +966,11 @@ static const struct drm_connector_helper_funcs analogix_dp_connector_helper_func
 enum drm_connector_status
 analogix_dp_detect(struct drm_connector *connector, bool force)
 {
+	struct analogix_dp_device *dp = to_dp(connector);
+
+	if (analogix_dp_detect_hpd(dp))
+		return connector_status_disconnected;
+
 	return connector_status_connected;
 }
 
@@ -1051,13 +1050,6 @@ static void analogix_dp_bridge_enable(struct drm_bridge *bridge)
 
 	pm_runtime_get_sync(dp->dev);
 
-	if (dp->plat_data->panel) {
-		if (drm_panel_prepare(dp->plat_data->panel)) {
-			DRM_ERROR("failed to setup the panel\n");
-			return;
-		}
-	}
-
 	if (dp->plat_data->power_on)
 		dp->plat_data->power_on(dp->plat_data);
 
@@ -1090,11 +1082,6 @@ static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
 	if (dp->plat_data->power_off)
 		dp->plat_data->power_off(dp->plat_data);
 
-	if (dp->plat_data->panel) {
-		if (drm_panel_unprepare(dp->plat_data->panel))
-			DRM_ERROR("failed to turnoff the panel\n");
-	}
-
 	pm_runtime_put_sync(dp->dev);
 
 	dp->dpms_mode = DRM_MODE_DPMS_OFF;
@@ -1352,6 +1339,15 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev,
 
 	pm_runtime_enable(dev);
 
+	phy_power_on(dp->phy);
+
+	if (dp->plat_data->panel) {
+		if (drm_panel_prepare(dp->plat_data->panel)) {
+			DRM_ERROR("failed to setup the panel\n");
+			return -EBUSY;
+		}
+	}
+
 	ret = devm_request_irq(&pdev->dev, dp->irq, analogix_dp_irq_handler,
 			       irq_flags, "analogix-dp", dp);
 	if (ret) {
@@ -1385,6 +1381,12 @@ void analogix_dp_unbind(struct device *dev, struct device *master,
 	struct analogix_dp_device *dp = dev_get_drvdata(dev);
 
 	analogix_dp_bridge_disable(dp->bridge);
+
+	if (dp->plat_data->panel) {
+		if (drm_panel_unprepare(dp->plat_data->panel))
+			DRM_ERROR("failed to turnoff the panel\n");
+	}
+
 	pm_runtime_disable(dev);
 }
 EXPORT_SYMBOL_GPL(analogix_dp_unbind);
-- 
1.9.1




From ykk at rock-chips.com  Thu Jan 21 17:56:26 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 22 Jan 2016 09:56:26 +0800
Subject: [PATCH v13 15/17] drm: bridge: analogix/dp: add edid modes parse in
 get_modes method
In-Reply-To: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
References: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1453427786-24655-1-git-send-email-ykk@rock-chips.com>

Display Port monitor could support kinds of mode which indicate
in monitor edid, not just one single display resolution which
defined in panel or devivetree property display timing.

Note: Gustavo Padovan try to remove the controller and phy
power on function in bind time at bellow commit:
	drm/exynos: do not start enabling DP at bind() phase

But for now driver need to read edid message in .get_modes()
function, so controller must be inited in bind time, so we
need to add controller init back.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Tested-by: Javier Martinez Canillas <javier at osg.samsung.com>
---
Changes in v13: None
Changes in v12: None
Changes in v11: None
Changes in v10: None
Changes in v9: None
Changes in v8: None
Changes in v7: None
Changes in v6: None
Changes in v5: None
Changes in v4:
- Call drm_panel_prepare() in .get_modes function, ensure panel should
  power on before driver try to read edid message.

Changes in v3:
- Add edid modes parse support

Changes in v2: None

 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 17 ++++----
 drivers/gpu/drm/bridge/analogix/analogix_dp_core.h | 46 +++++++++++-----------
 2 files changed, 33 insertions(+), 30 deletions(-)

diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index cfdf695..bc59e8d 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -110,7 +110,7 @@ static unsigned char analogix_dp_calc_edid_check_sum(unsigned char *edid_data)
 
 static int analogix_dp_read_edid(struct analogix_dp_device *dp)
 {
-	unsigned char edid[EDID_BLOCK_LENGTH * 2];
+	unsigned char *edid = dp->edid;
 	unsigned int extend_block = 0;
 	unsigned char sum;
 	unsigned char test_vector;
@@ -904,12 +904,6 @@ static void analogix_dp_commit(struct analogix_dp_device *dp)
 			DRM_ERROR("failed to disable the panel\n");
 	}
 
-	ret = analogix_dp_handle_edid(dp);
-	if (ret) {
-		dev_err(dp->dev, "unable to handle edid\n");
-		return;
-	}
-
 	ret = analogix_dp_set_link_train(dp, dp->video_info.max_lane_count,
 					 dp->video_info.max_link_rate);
 	if (ret) {
@@ -939,8 +933,14 @@ static void analogix_dp_commit(struct analogix_dp_device *dp)
 int analogix_dp_get_modes(struct drm_connector *connector)
 {
 	struct analogix_dp_device *dp = to_dp(connector);
+	struct edid *edid = (struct edid *)dp->edid;
 	int num_modes = 0;
 
+	if (analogix_dp_handle_edid(dp) == 0) {
+		drm_mode_connector_update_edid_property(&dp->connector, edid);
+		num_modes += drm_add_edid_modes(&dp->connector, edid);
+	}
+
 	if (dp->plat_data->panel)
 		num_modes += drm_panel_get_modes(dp->plat_data->panel);
 
@@ -978,6 +978,7 @@ static void analogix_dp_connector_destroy(struct drm_connector *connector)
 {
 	drm_connector_unregister(connector);
 	drm_connector_cleanup(connector);
+
 }
 
 static const struct drm_connector_funcs analogix_dp_connector_funcs = {
@@ -1348,6 +1349,8 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev,
 		}
 	}
 
+	analogix_dp_init_dp(dp);
+
 	ret = devm_request_irq(&pdev->dev, dp->irq, analogix_dp_irq_handler,
 			       irq_flags, "analogix-dp", dp);
 	if (ret) {
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
index a8cba64..40a0759 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
@@ -20,6 +20,28 @@
 #define MAX_CR_LOOP			5
 #define MAX_EQ_LOOP			5
 
+/* I2C EDID Chip ID, Slave Address */
+#define I2C_EDID_DEVICE_ADDR			0x50
+#define I2C_E_EDID_DEVICE_ADDR			0x30
+
+#define EDID_BLOCK_LENGTH			0x80
+#define EDID_HEADER_PATTERN			0x00
+#define EDID_EXTENSION_FLAG			0x7e
+#define EDID_CHECKSUM				0x7f
+
+/* DP_MAX_LANE_COUNT */
+#define DPCD_ENHANCED_FRAME_CAP(x)		(((x) >> 7) & 0x1)
+#define DPCD_MAX_LANE_COUNT(x)			((x) & 0x1f)
+
+/* DP_LANE_COUNT_SET */
+#define DPCD_LANE_COUNT_SET(x)			((x) & 0x1f)
+
+/* DP_TRAINING_LANE0_SET */
+#define DPCD_PRE_EMPHASIS_SET(x)		(((x) & 0x3) << 3)
+#define DPCD_PRE_EMPHASIS_GET(x)		(((x) >> 3) & 0x3)
+#define DPCD_VOLTAGE_SWING_SET(x)		(((x) & 0x3) << 0)
+#define DPCD_VOLTAGE_SWING_GET(x)		(((x) >> 0) & 0x3)
+
 enum link_lane_count_type {
 	LANE_COUNT1 = 1,
 	LANE_COUNT2 = 2,
@@ -155,6 +177,7 @@ struct analogix_dp_device {
 	int			dpms_mode;
 	int			hpd_gpio;
 	bool                    force_hpd;
+	unsigned char           edid[EDID_BLOCK_LENGTH * 2];
 
 	struct analogix_dp_plat_data *plat_data;
 };
@@ -254,27 +277,4 @@ int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp);
 void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp);
 void analogix_dp_enable_scrambling(struct analogix_dp_device *dp);
 void analogix_dp_disable_scrambling(struct analogix_dp_device *dp);
-
-/* I2C EDID Chip ID, Slave Address */
-#define I2C_EDID_DEVICE_ADDR			0x50
-#define I2C_E_EDID_DEVICE_ADDR			0x30
-
-#define EDID_BLOCK_LENGTH			0x80
-#define EDID_HEADER_PATTERN			0x00
-#define EDID_EXTENSION_FLAG			0x7e
-#define EDID_CHECKSUM				0x7f
-
-/* DP_MAX_LANE_COUNT */
-#define DPCD_ENHANCED_FRAME_CAP(x)		(((x) >> 7) & 0x1)
-#define DPCD_MAX_LANE_COUNT(x)			((x) & 0x1f)
-
-/* DP_LANE_COUNT_SET */
-#define DPCD_LANE_COUNT_SET(x)			((x) & 0x1f)
-
-/* DP_TRAINING_LANE0_SET */
-#define DPCD_PRE_EMPHASIS_SET(x)		(((x) & 0x3) << 3)
-#define DPCD_PRE_EMPHASIS_GET(x)		(((x) >> 3) & 0x3)
-#define DPCD_VOLTAGE_SWING_SET(x)		(((x) & 0x3) << 0)
-#define DPCD_VOLTAGE_SWING_GET(x)		(((x) >> 0) & 0x3)
-
 #endif /* _ANALOGIX_DP_CORE_H */
-- 
1.9.1




From ykk at rock-chips.com  Thu Jan 21 17:56:31 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 22 Jan 2016 09:56:31 +0800
Subject: [PATCH v13 16/17] drm: bridge: analogix/dp: add panel
 prepare/unprepare in suspend/resume time
In-Reply-To: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
References: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1453427791-24698-1-git-send-email-ykk@rock-chips.com>

Turn off the panel power in suspend time would help to reduce
power waste.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v13: None
Changes in v12: None
Changes in v11: None
Changes in v10: None
Changes in v9: None
Changes in v8: None
Changes in v7: None
Changes in v6: None
Changes in v5: None
Changes in v4: None
Changes in v3: None
Changes in v2: None

 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index bc59e8d..5292b28 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -1400,6 +1400,12 @@ int analogix_dp_suspend(struct device *dev)
 	struct analogix_dp_device *dp = dev_get_drvdata(dev);
 
 	clk_disable_unprepare(dp->clock);
+
+	if (dp->plat_data->panel) {
+		if (drm_panel_unprepare(dp->plat_data->panel))
+			DRM_ERROR("failed to turnoff the panel\n");
+	}
+
 	return 0;
 }
 EXPORT_SYMBOL_GPL(analogix_dp_suspend);
@@ -1415,6 +1421,13 @@ int analogix_dp_resume(struct device *dev)
 		return ret;
 	}
 
+	if (dp->plat_data->panel) {
+		if (drm_panel_prepare(dp->plat_data->panel)) {
+			DRM_ERROR("failed to setup the panel\n");
+			return -EBUSY;
+		}
+	}
+
 	return 0;
 }
 EXPORT_SYMBOL_GPL(analogix_dp_resume);
-- 
1.9.1




From ykk at rock-chips.com  Thu Jan 21 17:56:42 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 22 Jan 2016 09:56:42 +0800
Subject: [PATCH v13 17/17] drm: bridge: analogix/dp: Fix the possible dead
 lock in bridge disable time
In-Reply-To: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
References: <1453427614-23053-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1453427802-24787-1-git-send-email-ykk@rock-chips.com>

It may caused a dead lock if we flush the hpd work in bridge disable time.

The normal flow would like:
  IN --> DRM IOCTL
        1. Acquire crtc_ww_class_mutex (DRM IOCTL)
  IN --> analogix_dp_bridge
        2. Acquire hpd work lock (Flush hpd work)
        3. HPD work already in idle, no need to run the work function.
  OUT <-- analogix_dp_bridge
  OUT <-- DRM IOCTL

The dead lock flow would like:
  IN --> DRM IOCTL
        1. Acquire crtc_ww_class_mutex (DRM IOCTL)
  IN --> analogix_dp_bridge
        2. Acquire hpd work lock (Flush hpd work)
  IN --> analogix_dp_hotplug
  IN --> drm_helper_hpd_irq_event
        3. Acquire mode_config lock (This lock already have been acquired in previous step 1)
** Dead Lock Now **

It's wrong to flush the hpd work in bridge->disable time, I guess the
original code just want to ensure the delay work must be finish before
encoder disabled.

The flush work in bridge disable time is try to ensure the HPD event
won't be missed before display card disabled, actually we can take a
fast respond way(interrupt thread) to update DRM HPD event to fix the
delay update and possible dead lock.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v13: None
Changes in v12: None
Changes in v11: None
Changes in v10: None
Changes in v9: None
Changes in v8: None
Changes in v7: None
Changes in v6: None
Changes in v5: None
Changes in v4: None
Changes in v3: None
Changes in v2: None

 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 62 ++++++++++------------
 drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  3 +-
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  | 26 +++++++++
 3 files changed, 55 insertions(+), 36 deletions(-)

diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index 5292b28..7699597 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -851,47 +851,40 @@ static void analogix_dp_enable_scramble(struct analogix_dp_device *dp,
 	}
 }
 
-static irqreturn_t analogix_dp_irq_handler(int irq, void *arg)
+static irqreturn_t analogix_dp_hardirq(int irq, void *arg)
 {
 	struct analogix_dp_device *dp = arg;
-
+	irqreturn_t ret = IRQ_NONE;
 	enum dp_irq_type irq_type;
 
 	irq_type = analogix_dp_get_irq_type(dp);
-	switch (irq_type) {
-	case DP_IRQ_TYPE_HP_CABLE_IN:
-		dev_dbg(dp->dev, "Received irq - cable in\n");
-		schedule_work(&dp->hotplug_work);
-		analogix_dp_clear_hotplug_interrupts(dp);
-		break;
-	case DP_IRQ_TYPE_HP_CABLE_OUT:
-		dev_dbg(dp->dev, "Received irq - cable out\n");
-		analogix_dp_clear_hotplug_interrupts(dp);
-		break;
-	case DP_IRQ_TYPE_HP_CHANGE:
-		/*
-		 * We get these change notifications once in a while, but there
-		 * is nothing we can do with them. Just ignore it for now and
-		 * only handle cable changes.
-		 */
-		dev_dbg(dp->dev, "Received irq - hotplug change; ignoring.\n");
-		analogix_dp_clear_hotplug_interrupts(dp);
-		break;
-	default:
-		dev_err(dp->dev, "Received irq - unknown type!\n");
-		break;
+	if (irq_type != DP_IRQ_TYPE_UNKNOWN) {
+		analogix_dp_mute_hpd_interrupt(dp);
+		ret = IRQ_WAKE_THREAD;
 	}
-	return IRQ_HANDLED;
+
+	return ret;
 }
 
-static void analogix_dp_hotplug(struct work_struct *work)
+static irqreturn_t analogix_dp_irq_thread(int irq, void *arg)
 {
-	struct analogix_dp_device *dp;
+	struct analogix_dp_device *dp = arg;
+	enum dp_irq_type irq_type;
+
+	irq_type = analogix_dp_get_irq_type(dp);
+	if (irq_type & DP_IRQ_TYPE_HP_CABLE_IN ||
+	    irq_type & DP_IRQ_TYPE_HP_CABLE_OUT) {
+		dev_dbg(dp->dev, "Detected cable status changed!\n");
+		if (dp->drm_dev)
+			drm_helper_hpd_irq_event(dp->drm_dev);
+	}
 
-	dp = container_of(work, struct analogix_dp_device, hotplug_work);
+	if (irq_type != DP_IRQ_TYPE_UNKNOWN) {
+		analogix_dp_clear_hotplug_interrupts(dp);
+		analogix_dp_unmute_hpd_interrupt(dp);
+	}
 
-	if (dp->drm_dev)
-		drm_helper_hpd_irq_event(dp->drm_dev);
+	return IRQ_HANDLED;
 }
 
 static void analogix_dp_commit(struct analogix_dp_device *dp)
@@ -1077,7 +1070,6 @@ static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
 	}
 
 	disable_irq(dp->irq);
-	flush_work(&dp->hotplug_work);
 	phy_power_off(dp->phy);
 
 	if (dp->plat_data->power_off)
@@ -1336,8 +1328,6 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev,
 		return -ENODEV;
 	}
 
-	INIT_WORK(&dp->hotplug_work, analogix_dp_hotplug);
-
 	pm_runtime_enable(dev);
 
 	phy_power_on(dp->phy);
@@ -1351,8 +1341,10 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev,
 
 	analogix_dp_init_dp(dp);
 
-	ret = devm_request_irq(&pdev->dev, dp->irq, analogix_dp_irq_handler,
-			       irq_flags, "analogix-dp", dp);
+	ret = devm_request_threaded_irq(&pdev->dev, dp->irq,
+					analogix_dp_hardirq,
+					analogix_dp_irq_thread,
+					irq_flags, "analogix-dp", dp);
 	if (ret) {
 		dev_err(&pdev->dev, "failed to request irq\n");
 		goto err_disable_pm_runtime;
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
index 40a0759..a4e1330 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
@@ -172,7 +172,6 @@ struct analogix_dp_device {
 
 	struct video_info	video_info;
 	struct link_train	link_train;
-	struct work_struct	hotplug_work;
 	struct phy		*phy;
 	int			dpms_mode;
 	int			hpd_gpio;
@@ -191,6 +190,8 @@ void analogix_dp_init_interrupt(struct analogix_dp_device *dp);
 void analogix_dp_reset(struct analogix_dp_device *dp);
 void analogix_dp_swreset(struct analogix_dp_device *dp);
 void analogix_dp_config_interrupt(struct analogix_dp_device *dp);
+void analogix_dp_mute_hpd_interrupt(struct analogix_dp_device *dp);
+void analogix_dp_unmute_hpd_interrupt(struct analogix_dp_device *dp);
 enum pll_status analogix_dp_get_pll_lock_status(struct analogix_dp_device *dp);
 void analogix_dp_set_pll_power_down(struct analogix_dp_device *dp, bool enable);
 void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp,
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
index cba3ffd..49205ef 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
@@ -185,6 +185,32 @@ void analogix_dp_config_interrupt(struct analogix_dp_device *dp)
 	writel(reg, dp->reg_base + ANALOGIX_DP_INT_STA_MASK);
 }
 
+void analogix_dp_mute_hpd_interrupt(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	/* 0: mask, 1: unmask */
+	reg = readl(dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_4);
+	reg &= ~COMMON_INT_MASK_4;
+	writel(reg, dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_4);
+
+	reg = readl(dp->reg_base + ANALOGIX_DP_INT_STA_MASK);
+	reg &= ~INT_STA_MASK;
+	writel(reg, dp->reg_base + ANALOGIX_DP_INT_STA_MASK);
+}
+
+void analogix_dp_unmute_hpd_interrupt(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	/* 0: mask, 1: unmask */
+	reg = COMMON_INT_MASK_4;
+	writel(reg, dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_4);
+
+	reg = INT_STA_MASK;
+	writel(reg, dp->reg_base + ANALOGIX_DP_INT_STA_MASK);
+}
+
 enum pll_status analogix_dp_get_pll_lock_status(struct analogix_dp_device *dp)
 {
 	u32 reg;
-- 
1.9.1




From sjg at chromium.org  Thu Jan 21 19:13:34 2016
From: sjg at chromium.org (Simon Glass)
Date: Thu, 21 Jan 2016 20:13:34 -0700
Subject: [U-Boot] [PATCH v4 5/6] rockchip: kylin: Enable boot with android
 boot image
In-Reply-To: <CAPnjgZ1SUJDVPFVXgJjEYgom7tEdaORUAjQ8QfQ8jCpw=NtcJQ@mail.gmail.com>
References: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
 <1452675200-15941-6-git-send-email-jeffy.chen@rock-chips.com>
 <20160113152811.GC3359@bill-the-cat> <56970886.2040009@rock-chips.com>
 <20160114162227.GY3359@bill-the-cat> <569842F2.4080808@rock-chips.com>
 <20160115005950.GC3359@bill-the-cat> <5698577B.8040209@rock-chips.com>
 <20160115144224.GR3359@bill-the-cat> <1452872541.3905.21.camel@gmail.com>
 <CAPnjgZ1SUJDVPFVXgJjEYgom7tEdaORUAjQ8QfQ8jCpw=NtcJQ@mail.gmail.com>
Message-ID: <CAPnjgZ1umWviK0uCW9B3HwpYdWdL_H=+-83ip-iA3shH3X+MMQ@mail.gmail.com>

Hi,

On 15 January 2016 at 18:18, Simon Glass <sjg at chromium.org> wrote:
> Hi,
>
> On 15 January 2016 at 08:42, Daniel Schwierzeck
> <daniel.schwierzeck at gmail.com> wrote:
>> Am Freitag, den 15.01.2016, 09:42 -0500 schrieb Tom Rini:
>>> On Fri, Jan 15, 2016 at 10:20:43AM +0800, Jeffy Chen wrote:
>>> > Hi Tom,
>>> >
>>> > On 2016-1-15 8:59, Tom Rini wrote:
>>> > > On Fri, Jan 15, 2016 at 08:53:06AM +0800, Jeffy Chen wrote:
>>> > > > Hi Tom,
>>> > > >
>>> > > > On 2016-1-15 0:22, Tom Rini wrote:
>>> > > > > On Thu, Jan 14, 2016 at 10:31:34AM +0800, Jeffy Chen wrote:
>>> > > > > > Hi Tom,
>>> > > > > >
>>> > > > > > On 2016-1-13 23:28, Tom Rini wrote:
>>> > > > > > > On Wed, Jan 13, 2016 at 04:53:19PM +0800, Jeffy Chen
>>> > > > > > > wrote:
>>> > > > > > >
>>> > > > > > > > The android kernel is using appended dtb by default,
>>> > > > > > > > and store
>>> > > > > > > > ramdisk right after kernel & dtb.
>>> > > > > > > > So we needs to relocate ramdisk, and use atags to pass
>>> > > > > > > > params.
>>> > > > > > > >
>>> > > > > > > > Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
>>> > > > > > > > Acked-by: Simon Glass <sjg at chromium.org>
>>> > > > > > > > ---
>>> > > > > > > >
>>> > > > > > > > Changes in v4: None
>>> > > > > > > > Changes in v3: None
>>> > > > > > > > Changes in v2: None
>>> > > > > > > >
>>> > > > > > > >  include/configs/kylin_rk3036.h | 23
>>> > > > > > > > +++++++++++++++++++++++
>>> > > > > > > >  1 file changed, 23 insertions(+)
>>> > > > > > > >
>>> > > > > > > > diff --git a/include/configs/kylin_rk3036.h
>>> > > > > > > > b/include/configs/kylin_rk3036.h
>>> > > > > > > > index b750b26..49997ec 100644
>>> > > > > > > > --- a/include/configs/kylin_rk3036.h
>>> > > > > > > > +++ b/include/configs/kylin_rk3036.h
>>> > > > > > > > @@ -35,6 +35,29 @@
>>> > > > > > > >  #undef CONFIG_EXTRA_ENV_SETTINGS
>>> > > > > > > >  #define CONFIG_EXTRA_ENV_SETTINGS \
>>> > > > > > > >         "partitions=" PARTS_DEFAULT \
>>> > > > > > > > +       "mmcdev=0\0" \
>>> > > > > > > > +       "mmcpart=5\0" \
>>> > > > > > > > +       "loadaddr=" __stringify(CONFIG_SYS_LOAD_ADDR)
>>> > > > > > > > "\0" \
>>> > > > > > > > +
>>> > > > > > > > +#define CONFIG_ANDROID_BOOT_IMAGE
>>> > > > > > > > +#define CONFIG_SYS_BOOT_RAMDISK_HIGH
>>> > > > > > > This should already be set.
>>> > > > > > Right, i'll remove it...
>>> > > > > > > > +#define CONFIG_SYS_HUSH_PARSER
>>> > > > > > > > +
>>> > > > > > > > +#undef CONFIG_BOOTCOMMAND
>>> > > > > > > > +#define CONFIG_BOOTCOMMAND \
>>> > > > > > > > +       "mmc dev ${mmcdev}; if mmc rescan; then " \
>>> > > > > > > > +               "part start mmc ${mmcdev} ${mmcpart}
>>> > > > > > > > boot_start;" \
>>> > > > > > > > +               "part size mmc ${mmcdev} ${mmcpart}
>>> > > > > > > > boot_size;" \
>>> > > > > > > > +               "mmc read ${loadaddr} ${boot_start}
>>> > > > > > > > ${boot_size};" \
>>> > > > > > > > +               "bootm start ${loadaddr}; bootm
>>> > > > > > > > ramdisk;" \
>>> > > > > > > > +               "bootm prep; bootm go;" \
>>> > > > > > > > +       "fi;" \
>>> > > > > > > > +
>>> > > > > > > > +/* Enable atags */
>>> > > > > > > > +#define CONFIG_SYS_BOOTPARAMS_LEN      (64*1024)
>>> > > > > > > > +#define CONFIG_INITRD_TAG
>>> > > > > > > > +#define CONFIG_SETUP_MEMORY_TAGS
>>> > > > > > > > +#define CONFIG_CMDLINE_TAG
>>> > > > > > > But I'm confused as to what exactly is going on here.
>>> > > > > > >  Appended dtb is
>>> > > > > > > not the same as ATAGS.  And you shouldn't need to split
>>> > > > > > > up bootm like
>>> > > > > > > that.  Can you please explain a bit more?  Thanks!
>>> > > > > > The u-boot will pass atags to kernel, and kernel will merge
>>> > > > > > those
>>> > > > > > atags into the appended dtb(fdt).
>>> > > > > >
>>> > > > > > The default bootm flow would not pass ramdisk state, but we
>>> > > > > > need it,
>>> > > > > > so we should add this state into default flow, or just use
>>> > > > > > split
>>> > > > > > bootm cmds :)
>>> > > > > That seems very strange.  Is the ramdisk concatenated with
>>> > > > > the kernel
>>> > > > > and dtb as well (and that's why bootm ramdisk somehow finds
>>> > > > > it but
>>> > > > > normal bootm doesn't as you aren't passing in a ramdisk
>>> > > > > address) ?
>>> > > > Yes, the ramdisk concatenated with the kernel and dtb as
>>> > > > well(u-boot/include/android_image.h: struct andr_img_hdr).
>>> > > >
>>> > > > And the normal bootm cmd would find it by parsing andr_img_hdr
>>> > > > struct.
>>> > > > But we still need bootm ramdisk state, because it will call
>>> > > > boot_ramdisk_high to relocate ramdisk area :)
>>> > > >
>>> > > > I found if not relocate it to somewhere else, it would be
>>> > > > corrupted
>>> > > > after kernel's decompressing(during update fdt area).
>>> > > So 'bootm $loadaddr' of an Android image sees, but does not
>>> > > relocate the
>>> > > ramdisk that is included in the image, but bootm ramdisk does?
>>> > >  That
>>> > > sounds like a bug in the regular bootm handling.
>>> > Yep, the default bootm flow would not contain ramdisk relocate
>>> > state:
>>> >
>>> > vi common/cmd_bootm.c
>>> >         return do_bootm_states(cmdtp, flag, argc, argv,
>>> > BOOTM_STATE_START |
>>> >                 BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER |
>>> >                 BOOTM_STATE_LOADOS |
>>> > #if defined(CONFIG_PPC) || defined(CONFIG_MIPS)
>>> >                 BOOTM_STATE_OS_CMDLINE |
>>> > #endif
>>> >                 BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |
>>> >                 BOOTM_STATE_OS_GO, &images, 1);
>>> >
>>> > But i'm not sure if it's ok to add it here...
>>>
>>> Actually, maybe so.  This made me do some digging again back into
>>> Simon's series that refactored the bootm code.  In the long long ago,
>>> nearly every architecture would call bootm_ramdisk_high to relocate
>>> the
>>> ramdisk and set the environment (and poke the DT).  But it wasn't
>>> always
>>> clear when / why it would do this.  And it got consolidated.  And
>>> here's
>>> where it's tricky.  It looks correct today, still, to make sure that
>>> we
>>> relocate the ramdisk.  image_setup_linux() checks
>>> IMAGE_ENABLE_RAMDISK_HIGH which is toggled by
>>> CONFIG_SYS_BOOT_RAMDISK_HIGH.  But for example, MIPS whacked _back_
>>> in a
>>> local call to make sure the ramdisk was being relocated because it
>>> wasn't back in 2014 into boot_prep_linux.  It may even be related to
>>> some of the initrd rated things Masahiro has found recently.  So
>>> something is wrong down in this core code.
>>
>> one problem is the different behaviour of bootm between legacy uImages
>> and FIT uImages. In case of legacy uImages, the core code automatically
>> relocates initramfs and DTB. In case of FIT uImages, the arch-specific
>> do_bootm_linux() still has to do that. This was only somehow addressed
>> by adding the helper function image_setup_linux() and calling it from
>> do_bootm_linux(). But you can't use that function with legacy uImages,
>> because initramfs and DTB would be relocated twice because the states
>> BOOTM_STATE_RAMDISK and BOOTM_STATE_FDT are not checked. Because of
>> that image_setup_linux() also leads to a different behaviour when
>> calling bootm as a single command and calling bootm with sub-steps.
>>
>> So the current bootm implementation on MIPS is the only way for me to
>> make bootm working with all possible combinations of legacy/FIT uImage,
>> with/without initramfs, with/without DTB and single/multiple bootm
>> calls. I suspect that the current implementation on ARM does not work
>> with all possible combinations.
>>
>>> Jeffy, can you try and debug
>>> this a bit since you have a failing case in front of you?  Otherwise
>>> I'll put it on my TODO list.  Thanks!
>>>
>>
>> Some ideas for a possible refactoring of the bootm core code:
>> - remove arch-specific do_bootm_linux() and image_setup_linux()
>> - rename functions like boot_prep_linux() and boot_jump_linux() to
>> arch_boot_prep_linux() and arch_boot_jump_linux() and declare them as
>> __weak and add a default implementation, the arch code can overwrite
>> them
>> - refactor the core code to call those functions at the appropriate
>> locations
>
> Perhaps we need a more formal API between bootm and the arch-specific
> implementation of the various pieces.
>
> Also it would be great to add more tests for bootm. I made a start
> based on what I could figure out about the behaviour
> (tests/image/test-fit.py), but we should do more.
>
> Regards,
> Simon

I'm going to hold off on this patch as there seems to be some other root cause.

Regards,
Simon


From zhengxing at rock-chips.com  Thu Jan 21 23:49:08 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Fri, 22 Jan 2016 15:49:08 +0800
Subject: [PATCH] clk: rockchip: rk3036: Add apll as the critical clock
In-Reply-To: <7538210.fX6mmzZ75v@phil>
References: <1453279037-23971-1-git-send-email-zhengxing@rock-chips.com>
 <7538210.fX6mmzZ75v@phil>
Message-ID: <56A1DEF4.2070409@rock-chips.com>

Hi Heiko,

On 2016?01?21? 17:21, Heiko Stuebner wrote:
> Hi Xing,
>
> Am Mittwoch, 20. Januar 2016, 16:37:17 schrieb Xing Zheng:
>> The apll may be closed if there are some child clock nodes below
>> it when the device startup. Therefore, the apll should be keep
>> critical.
>>
>> The apll tree like this:
>>      pll_apll
>>         apll
>>            armclk
>>               pclk_dbg
>>               aclk_core_pre
>>            aclk_hvec
>>            uart_pll_clk
>>               uart2_src
>>                  uart2_frac
>>               uart1_src
>>                  uart1_frac
>>               uart0_src
>>                  uart0_frac
> can you find out which of those clocks does cause your hang?
> Because things like the uart-clocks for example should be handled by their
> driver already, at the time the clk_disable_unused runs(). So I'd really
> like the critical clock to be the actually needed clock.
>
> Thanks
> Heiko

It looks like that we call the rockchip_rk3036_pll_disable cause the 
apll is diabled.
I think the diabled tracing like this:
1. All of uart0_frac~uart2_frac are branch_fraction_divider type, they 
have CLK_SET_RATE_UNGATE flag,
2. I enable cpufreq configs on the rk3036_defconfig, the default cpu 
freq is 600MHz durning loader, when startup it is 816MHz with default DTS.
Therefore, cpu freq will be change rate 600MHz to 816MHz then call 
clk_change_rate.
3. With the flag CLK_SET_RATE_UNGATE, triggering call clk_core_disable. 
In here, it will recursively close all of uart gates, finally, to call 
the root
parent diable callback that is rockchip_rk3036_pll_disable.

The disble log:
[ 1.074186] clk_change_rate -- CLK_SET_RATE_UNGATE name: uart2_frac, 
parent: uart2_src, core->flags = 0x00000424
[ 1.105722] clk_gate_endisable -- name: uart2_frac, parent: uart2_src, 
enable = 0
[ 1.110125] clk_gate_endisable -- name: uart2_src, parent: uart_pll_clk, 
enable = 0
[ 2.604445] rockchip_rk3036_pll_disable -- name: pll_apll, parent: xin24m

Therefore, I am considering uart_pll_clk hang onto gpll, or add it into 
the critical clock replace using apll...

If there are some mistake, please correct me. :-)

Thanks.

>
>
>> Signed-off-by: Xing Zheng<zhengxing at rock-chips.com>
>> ---
>>
>>   drivers/clk/rockchip/clk-rk3036.c |    1 +
>>   1 file changed, 1 insertion(+)
>>
>> diff --git a/drivers/clk/rockchip/clk-rk3036.c
>> b/drivers/clk/rockchip/clk-rk3036.c index ebce980..483913b 100644
>> --- a/drivers/clk/rockchip/clk-rk3036.c
>> +++ b/drivers/clk/rockchip/clk-rk3036.c
>> @@ -425,6 +425,7 @@ static struct rockchip_clk_branch
>> rk3036_clk_branches[] __initdata = { };
>>
>>   static const char *const rk3036_critical_clocks[] __initconst = {
>> +	"apll",
>>   	"aclk_cpu",
>>   	"aclk_peri",
>>   	"hclk_peri",
>
>
>




From wxt at rock-chips.com  Fri Jan 22 03:06:43 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Fri, 22 Jan 2016 19:06:43 +0800
Subject: [PATCH v7 0/9] Fix broken DMAFLUSHP on Rockchips platform
Message-ID: <1453460812-8498-1-git-send-email-wxt@rock-chips.com>

This series patches are apply on linus master branch.
Okay, this series patches verified on my github.
(https://github.com/Caesar-github/rockchip/commits/for-dma-tests)

Linux version 4.4.0+ (wxt at ubuntu)  (Ubuntu/Linaro 4.7.3-12ubuntu1) ) #14
SMP Fri Jan 22 18:13:12 CST 2016

The perious version 6 from Shawn.
(http://www.spinics.net/lists/dmaengine/msg06606.html)
I pick them up to go on upstream since Shawn hasn't much time to track them.
----

The purpose of the DMAFLUSHP instruction:
- Tell the peripheral to clear its status and control registers.
- Send a message to the peripheral to resend its level status.

There are 3 timings described in PL330 Technical Reference Manual:
- Timing 1: Burst request, can work well without DMAFLUSHP.
- Timing 2: Single and burst request, DMAC will ignore the single
            transfer request. This timing happens if there are single
            and burst request.
- Timing 3: Single transfers for a burst request, DMAC should signals
            datype to request the peripheral to flush the contents of
            any control registers. This timing happens if there is
            not enough MFIFO to places the burst data.

A peripheral may signal a DMA request during the execution of
DMAFLUSHP instruction, that cause DMA request being ignored by DMAC.

But DMAC and all peripherals on RK3X SoCs DO NOT support DMAFLUSHP.
It can't send a message to the peripheral to resend DMA request,
and the peripheral can't acknowledge a flush request from DMAC.
So all DMA requests should NOT be ignored by DMAC, and DMAC will not
notify the peripheral to flush.

To fix this problem, we need:
- Do NOT execute DMAFLUSHP instruction.
- Timing 2 and timing 3 should not happen.

Because on RK3X SoCs, there are 6 or below  channels and 32 MFIFO depth
for DMAC_BUS, and 8 channels and 64 MFIFO depth for DMAC_PERI, it is
impossible to hit the timing 3 if burst length is equal or less than 4.

Since the request type signal by the peripheral can only be set by
software. We can set Rockchip Soc's GRF_PERIDMAC_CON0[2:1] to select single
or burst request, if it is set b01,  all of the peripharals will signal a brust
request. So the timing 2 will not happen, too.

So DMAC on RK3X can support single or burst transfer, but can't support
mixed transfer.

Because burst transfer is more efficient than single transfer, this is
confirmed by our ASIC team, who strongly suggest to use burst transfer.
And this is confirmed by Addy's test on RK3288-Pink2 board, the speed of
spi flash burst transfer will increase about two times than single transfer.
Also, I have tested dw_mmc with pl330 on RK3188 platform to double confirm
the result. That means burst transfer is reansonable.

So we need a quirk not to execute DMAFLUSHP instruction and to use burst
transfer.

Note:
- The Rockchip Soc default value of GRF_PERIDMAC_CON0[2:1] is b01. To
  support brust transfer, these bits should not be changed in bootloader.

Todo:
remove the patches releated to i2s, that will send them at later.
- https://patchwork.kernel.org/patch/7399581/
- https://patchwork.kernel.org/patch/7399671/


Changes in v7:
- As vinod comments, fix the subsystem name.
- modify the subject to be better match.
- fix the subsystem.
- fix the subject.
- fix the subject.

Changes in v6:
- remove expose quirk and add dma max_burst caps for clients
- remove expose quirks and add max_burst for dmaengine
- remove quirks and get dma caps in order to limit burst

Changes in v5:
- add Mark's tag for spi changes
- remove unnecessary whitespace change
- use switch statement for i2s quirk

Changes in v4:
- remove spi & i2s dts changes and query quirk from dmaengine API
  suggeseted by Mark.
- fix typo
- Add dmaengine_get_quirk hook and implement it for pl330

Changes in v3:
- add Sunny's tag
- add more rockchip drivers' changes in this patchset
- add Reviewed-by: Sonny Rao <sonnyrao at chromium.org>
- add Reviewed-by: Sonny Rao <sonnyrao at chromium.org>
- add Reviewed-by: Sonny Rao <sonnyrao at chromium.org>

Changes in v2:
- amend the author
- reorder the patches suggested by Doug
- add Reviewed-by: Doug Anderson <dianders at chromium.org> for
  rk3288.dtsi patch and arm-pl330.txt patch
- add Reviewed-by: Doug Anderson <dianders at chromium.org>
- amend the author
- fix Olof's mail address
- amend the author
- add Reviewed-by: Doug Anderson <dianders at chromium.org>
- amend Olof's mail address

Changes in v1:
- rename broken-no-flushp to "arm,pl330-broken-no-flushp" suggested
  by Krzysztof.
- add From original author.
- remove Sunny's tag
- rename broken-no-flushp to "arm,pl330-broken-no-flushp" suggested
  by Krzysztof.
- rename broken-no-flushp to "arm,pl330-broken-no-flushp" suggested
  by Krzysztof.
- remove Sunny's tag
- rename broken-no-flushp to "arm,pl330-broken-no-flushp" suggested
  by Krzysztof.
  - remove Sunny's tag
- rename broken-no-flushp to "arm,pl330-broken-no-flushp" suggested
  by Krzysztof.

Addy Ke (3):
  dmaengine: pl330: add quirk for broken no flushp
  ARM: dts: Add arm, pl330-broken-no-flushp quirk for rk3288 platform
  spi: rockchip: modify DMA max burst to 1

Boojin Kim (1):
  dmaengine: pl330: support burst mode for dev-to-mem and mem-to-dev
    transmit

Caesar Wang (1):
  ARM: dts: add pl330-broken-no-flushp quirk for rk3036 SoCs

Shawn Lin (4):
  dt/bindings: arm-pl330: add description of arm, pl330-broken-no-flushp
  ARM: dts: Add arm, pl330-broken-no-flushp quirk for rk3xxx platform
  dmaengine: core: expose max burst capability to clients
  dmaengine: pl330: add max burst for dmaengine

 .../devicetree/bindings/dma/arm-pl330.txt          |   1 +
 arch/arm/boot/dts/rk3036.dtsi                      |   1 +
 arch/arm/boot/dts/rk3288.dtsi                      |   3 +
 arch/arm/boot/dts/rk3xxx.dtsi                      |   3 +
 drivers/dma/dmaengine.c                            |   1 +
 drivers/dma/pl330.c                                | 106 +++++++++++++++------
 drivers/spi/spi-rockchip.c                         |  12 ++-
 include/linux/dmaengine.h                          |   4 +
 8 files changed, 99 insertions(+), 32 deletions(-)

-- 
1.9.1



From wxt at rock-chips.com  Fri Jan 22 03:06:44 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Fri, 22 Jan 2016 19:06:44 +0800
Subject: [PATCH v7 1/9] dmaengine: pl330: support burst mode for dev-to-mem
 and mem-to-dev transmit
In-Reply-To: <1453460812-8498-1-git-send-email-wxt@rock-chips.com>
References: <1453460812-8498-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1453460812-8498-2-git-send-email-wxt@rock-chips.com>

From: Boojin Kim <boojin.kim at samsung.com>

This patch adds to support burst mode for dev-to-mem and
mem-to-dev transmit.

Signed-off-by: Boojin Kim <boojin.kim at samsung.com>
Signed-off-by: Addy Ke <addy.ke at rock-chips.com>
Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
cc: Heiko Stuebner <heiko at sntech.de>
cc: Doug Anderson <dianders at chromium.org>
cc: Olof Johansson <olof at lixom.net>

Reviewed-by: Sonny Rao <sonnyrao at chromium.org>
Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v7:
- As vinod comments, fix the subsystem name.

Changes in v6: None
Changes in v5:
- add Mark's tag for spi changes
- remove unnecessary whitespace change
- use switch statement for i2s quirk

Changes in v4:
- remove spi & i2s dts changes and query quirk from dmaengine API
  suggeseted by Mark.
- fix typo
- Add dmaengine_get_quirk hook and implement it for pl330

Changes in v3:
- add Sunny's tag
- add more rockchip drivers' changes in this patchset

Changes in v2:
- amend the author
- reorder the patches suggested by Doug
- add Reviewed-by: Doug Anderson <dianders at chromium.org> for
  rk3288.dtsi patch and arm-pl330.txt patch

Changes in v1:
- rename broken-no-flushp to "arm,pl330-broken-no-flushp" suggested
  by Krzysztof.
- add From original author.
- remove Sunny's tag

 drivers/dma/pl330.c | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 17ee758..7cdf8d4 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -1141,10 +1141,13 @@ static inline int _ldst_devtomem(unsigned dry_run, u8 buf[],
 		const struct _xfer_spec *pxs, int cyc)
 {
 	int off = 0;
+	enum pl330_cond cond;
+
+	cond = (pxs->desc->rqcfg.brst_len == 1) ? SINGLE : BURST;
 
 	while (cyc--) {
-		off += _emit_WFP(dry_run, &buf[off], SINGLE, pxs->desc->peri);
-		off += _emit_LDP(dry_run, &buf[off], SINGLE, pxs->desc->peri);
+		off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri);
+		off += _emit_LDP(dry_run, &buf[off], cond, pxs->desc->peri);
 		off += _emit_ST(dry_run, &buf[off], ALWAYS);
 		off += _emit_FLUSHP(dry_run, &buf[off], pxs->desc->peri);
 	}
@@ -1156,11 +1159,14 @@ static inline int _ldst_memtodev(unsigned dry_run, u8 buf[],
 		const struct _xfer_spec *pxs, int cyc)
 {
 	int off = 0;
+	enum pl330_cond cond;
+
+	cond = (pxs->desc->rqcfg.brst_len == 1) ? SINGLE : BURST;
 
 	while (cyc--) {
-		off += _emit_WFP(dry_run, &buf[off], SINGLE, pxs->desc->peri);
+		off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri);
 		off += _emit_LD(dry_run, &buf[off], ALWAYS);
-		off += _emit_STP(dry_run, &buf[off], SINGLE, pxs->desc->peri);
+		off += _emit_STP(dry_run, &buf[off], cond, pxs->desc->peri);
 		off += _emit_FLUSHP(dry_run, &buf[off], pxs->desc->peri);
 	}
 
@@ -1199,7 +1205,7 @@ static inline int _loop(unsigned dry_run, u8 buf[],
 	struct _arg_LPEND lpend;
 
 	if (*bursts == 1)
-		return _bursts(dry_run, buf, pxs, 1);
+		return _bursts(pl330, dry_run, buf, pxs, 1);
 
 	/* Max iterations possible in DMALP is 256 */
 	if (*bursts >= 256*256) {
@@ -2560,7 +2566,7 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic(
 
 		desc->rqtype = direction;
 		desc->rqcfg.brst_size = pch->burst_sz;
-		desc->rqcfg.brst_len = 1;
+		desc->rqcfg.brst_len = pch->burst_len;
 		desc->bytes_requested = period_len;
 		fill_px(&desc->px, dst, src, period_len);
 
@@ -2705,7 +2711,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
 		}
 
 		desc->rqcfg.brst_size = pch->burst_sz;
-		desc->rqcfg.brst_len = 1;
+		desc->rqcfg.brst_len = pch->burst_len;
 		desc->rqtype = direction;
 		desc->bytes_requested = sg_dma_len(sg);
 	}
-- 
1.9.1



From wxt at rock-chips.com  Fri Jan 22 03:06:45 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Fri, 22 Jan 2016 19:06:45 +0800
Subject: [PATCH v7 2/9] dt/bindings: arm-pl330: add description of arm,
 pl330-broken-no-flushp
In-Reply-To: <1453460812-8498-1-git-send-email-wxt@rock-chips.com>
References: <1453460812-8498-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1453460812-8498-3-git-send-email-wxt@rock-chips.com>

From: Shawn Lin <shawn.lin at rock-chips.com>

This patch adds the "arm, pl330-broken-no-flushp" for arm-pl330.

Reviewed-by: Doug Anderson <dianders at chromium.org>
Reviewed-by: Sonny Rao <sonnyrao at chromium.org>
Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v7:
- modify the subject to be better match.

Changes in v6: None
Changes in v5: None
Changes in v4: None
Changes in v3:
- add Reviewed-by: Sonny Rao <sonnyrao at chromium.org>

Changes in v2:
- add Reviewed-by: Doug Anderson <dianders at chromium.org>

Changes in v1:
- rename broken-no-flushp to "arm,pl330-broken-no-flushp" suggested
  by Krzysztof.

 Documentation/devicetree/bindings/dma/arm-pl330.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/dma/arm-pl330.txt b/Documentation/devicetree/bindings/dma/arm-pl330.txt
index 2675658..db7e226 100644
--- a/Documentation/devicetree/bindings/dma/arm-pl330.txt
+++ b/Documentation/devicetree/bindings/dma/arm-pl330.txt
@@ -15,6 +15,7 @@ Optional properties:
     cells in the dmas property of client device.
   - dma-channels: contains the total number of DMA channels supported by the DMAC
   - dma-requests: contains the total number of DMA requests supported by the DMAC
+  - arm,pl330-broken-no-flushp: quirk for avoiding to execute DMAFLUSHP
 
 Example:
 
-- 
1.9.1



From wxt at rock-chips.com  Fri Jan 22 03:06:46 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Fri, 22 Jan 2016 19:06:46 +0800
Subject: [PATCH v7 3/9] dmaengine: pl330: add quirk for broken no flushp
In-Reply-To: <1453460812-8498-1-git-send-email-wxt@rock-chips.com>
References: <1453460812-8498-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1453460812-8498-4-git-send-email-wxt@rock-chips.com>

From: Addy Ke <addy.ke at rock-chips.com>

This patch add "arm,pl330-broken-no-flushp" quirk to avoid execute
DMAFLUSHP if Soc doesn't support it.

Signed-off-by: Addy Ke <addy.ke at rock-chips.com>
Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
cc: Doug Anderson <dianders at chromium.org>
cc: Heiko Stuebner <heiko at sntech.de>
cc: Olof Johansson <olof at lixom.net>

Reviewed-by: Sonny Rao <sonnyrao at chromium.org>
Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v7:
- fix the subsystem.

Changes in v6: None
Changes in v5: None
Changes in v4: None
Changes in v3:
- add Reviewed-by: Sonny Rao <sonnyrao at chromium.org>

Changes in v2:
- amend the author
- fix Olof's mail address

Changes in v1:
- rename broken-no-flushp to "arm,pl330-broken-no-flushp" suggested
  by Krzysztof.
- remove Sunny's tag

 drivers/dma/pl330.c | 87 ++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 62 insertions(+), 25 deletions(-)

diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 7cdf8d4..008408d 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -34,6 +34,8 @@
 #define PL330_MAX_IRQS		32
 #define PL330_MAX_PERI		32
 
+#define PL330_QUIRK_BROKEN_NO_FLUSHP BIT(0)
+
 enum pl330_cachectrl {
 	CCTRL0,		/* Noncacheable and nonbufferable */
 	CCTRL1,		/* Bufferable only */
@@ -488,6 +490,17 @@ struct pl330_dmac {
 	/* Peripheral channels connected to this DMAC */
 	unsigned int num_peripherals;
 	struct dma_pl330_chan *peripherals; /* keep at end */
+	int quirks;
+};
+
+static struct pl330_of_quirks {
+	char *quirk;
+	int id;
+} of_quirks[] = {
+	{
+		.quirk = "arm,pl330-broken-no-flushp",
+		.id = PL330_QUIRK_BROKEN_NO_FLUSHP,
+	}
 };
 
 struct dma_pl330_desc {
@@ -1137,53 +1150,68 @@ static inline int _ldst_memtomem(unsigned dry_run, u8 buf[],
 	return off;
 }
 
-static inline int _ldst_devtomem(unsigned dry_run, u8 buf[],
-		const struct _xfer_spec *pxs, int cyc)
+static inline int _ldst_devtomem(struct pl330_dmac *pl330, unsigned dry_run,
+				 u8 buf[], const struct _xfer_spec *pxs,
+				 int cyc)
 {
 	int off = 0;
 	enum pl330_cond cond;
 
-	cond = (pxs->desc->rqcfg.brst_len == 1) ? SINGLE : BURST;
+	if (pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP)
+		cond = BURST;
+	else
+		cond = (pxs->desc->rqcfg.brst_len == 1) ? SINGLE : BURST;
 
 	while (cyc--) {
 		off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri);
 		off += _emit_LDP(dry_run, &buf[off], cond, pxs->desc->peri);
 		off += _emit_ST(dry_run, &buf[off], ALWAYS);
-		off += _emit_FLUSHP(dry_run, &buf[off], pxs->desc->peri);
+
+		if (!(pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP))
+			off += _emit_FLUSHP(dry_run, &buf[off],
+					    pxs->desc->peri);
 	}
 
 	return off;
 }
 
-static inline int _ldst_memtodev(unsigned dry_run, u8 buf[],
-		const struct _xfer_spec *pxs, int cyc)
+static inline int _ldst_memtodev(struct pl330_dmac *pl330,
+				 unsigned dry_run, u8 buf[],
+				 const struct _xfer_spec *pxs, int cyc)
 {
 	int off = 0;
 	enum pl330_cond cond;
 
-	cond = (pxs->desc->rqcfg.brst_len == 1) ? SINGLE : BURST;
+	if (pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP)
+		cond = BURST;
+	else
+		cond = (pxs->desc->rqcfg.brst_len == 1) ? SINGLE : BURST;
+
 
 	while (cyc--) {
 		off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri);
 		off += _emit_LD(dry_run, &buf[off], ALWAYS);
 		off += _emit_STP(dry_run, &buf[off], cond, pxs->desc->peri);
-		off += _emit_FLUSHP(dry_run, &buf[off], pxs->desc->peri);
+
+		if (!(pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP))
+			off += _emit_FLUSHP(dry_run, &buf[off],
+					    pxs->desc->peri);
 	}
 
 	return off;
 }
 
-static int _bursts(unsigned dry_run, u8 buf[],
+static int _bursts(struct pl330_dmac *pl330, unsigned dry_run, u8 buf[],
 		const struct _xfer_spec *pxs, int cyc)
 {
 	int off = 0;
 
 	switch (pxs->desc->rqtype) {
 	case DMA_MEM_TO_DEV:
-		off += _ldst_memtodev(dry_run, &buf[off], pxs, cyc);
+		off += _ldst_memtodev(pl330, dry_run, &buf[off], pxs, cyc);
 		break;
 	case DMA_DEV_TO_MEM:
-		off += _ldst_devtomem(dry_run, &buf[off], pxs, cyc);
+		off += _ldst_devtomem(pl330, dry_run, &buf[off], pxs, cyc);
 		break;
 	case DMA_MEM_TO_MEM:
 		off += _ldst_memtomem(dry_run, &buf[off], pxs, cyc);
@@ -1197,7 +1225,7 @@ static int _bursts(unsigned dry_run, u8 buf[],
 }
 
 /* Returns bytes consumed and updates bursts */
-static inline int _loop(unsigned dry_run, u8 buf[],
+static inline int _loop(struct pl330_dmac *pl330, unsigned dry_run, u8 buf[],
 		unsigned long *bursts, const struct _xfer_spec *pxs)
 {
 	int cyc, cycmax, szlp, szlpend, szbrst, off;
@@ -1223,7 +1251,7 @@ static inline int _loop(unsigned dry_run, u8 buf[],
 	}
 
 	szlp = _emit_LP(1, buf, 0, 0);
-	szbrst = _bursts(1, buf, pxs, 1);
+	szbrst = _bursts(pl330, 1, buf, pxs, 1);
 
 	lpend.cond = ALWAYS;
 	lpend.forever = false;
@@ -1255,7 +1283,7 @@ static inline int _loop(unsigned dry_run, u8 buf[],
 	off += _emit_LP(dry_run, &buf[off], 1, lcnt1);
 	ljmp1 = off;
 
-	off += _bursts(dry_run, &buf[off], pxs, cyc);
+	off += _bursts(pl330, dry_run, &buf[off], pxs, cyc);
 
 	lpend.cond = ALWAYS;
 	lpend.forever = false;
@@ -1278,8 +1306,9 @@ static inline int _loop(unsigned dry_run, u8 buf[],
 	return off;
 }
 
-static inline int _setup_loops(unsigned dry_run, u8 buf[],
-		const struct _xfer_spec *pxs)
+static inline int _setup_loops(struct pl330_dmac *pl330,
+			       unsigned dry_run, u8 buf[],
+			       const struct _xfer_spec *pxs)
 {
 	struct pl330_xfer *x = &pxs->desc->px;
 	u32 ccr = pxs->ccr;
@@ -1288,15 +1317,16 @@ static inline int _setup_loops(unsigned dry_run, u8 buf[],
 
 	while (bursts) {
 		c = bursts;
-		off += _loop(dry_run, &buf[off], &c, pxs);
+		off += _loop(pl330, dry_run, &buf[off], &c, pxs);
 		bursts -= c;
 	}
 
 	return off;
 }
 
-static inline int _setup_xfer(unsigned dry_run, u8 buf[],
-		const struct _xfer_spec *pxs)
+static inline int _setup_xfer(struct pl330_dmac *pl330,
+			      unsigned dry_run, u8 buf[],
+			      const struct _xfer_spec *pxs)
 {
 	struct pl330_xfer *x = &pxs->desc->px;
 	int off = 0;
@@ -1307,7 +1337,7 @@ static inline int _setup_xfer(unsigned dry_run, u8 buf[],
 	off += _emit_MOV(dry_run, &buf[off], DAR, x->dst_addr);
 
 	/* Setup Loop(s) */
-	off += _setup_loops(dry_run, &buf[off], pxs);
+	off += _setup_loops(pl330, dry_run, &buf[off], pxs);
 
 	return off;
 }
@@ -1316,8 +1346,9 @@ static inline int _setup_xfer(unsigned dry_run, u8 buf[],
  * A req is a sequence of one or more xfer units.
  * Returns the number of bytes taken to setup the MC for the req.
  */
-static int _setup_req(unsigned dry_run, struct pl330_thread *thrd,
-		unsigned index, struct _xfer_spec *pxs)
+static int _setup_req(struct pl330_dmac *pl330, unsigned dry_run,
+		      struct pl330_thread *thrd, unsigned index,
+		      struct _xfer_spec *pxs)
 {
 	struct _pl330_req *req = &thrd->req[index];
 	struct pl330_xfer *x;
@@ -1334,7 +1365,7 @@ static int _setup_req(unsigned dry_run, struct pl330_thread *thrd,
 	if (x->bytes % (BRST_SIZE(pxs->ccr) * BRST_LEN(pxs->ccr)))
 		return -EINVAL;
 
-	off += _setup_xfer(dry_run, &buf[off], pxs);
+	off += _setup_xfer(pl330, dry_run, &buf[off], pxs);
 
 	/* DMASEV peripheral/event */
 	off += _emit_SEV(dry_run, &buf[off], thrd->ev);
@@ -1428,7 +1459,7 @@ static int pl330_submit_req(struct pl330_thread *thrd,
 	xs.desc = desc;
 
 	/* First dry run to check if req is acceptable */
-	ret = _setup_req(1, thrd, idx, &xs);
+	ret = _setup_req(pl330, 1, thrd, idx, &xs);
 	if (ret < 0)
 		goto xfer_exit;
 
@@ -1442,7 +1473,7 @@ static int pl330_submit_req(struct pl330_thread *thrd,
 	/* Hook the request */
 	thrd->lstenq = idx;
 	thrd->req[idx].desc = desc;
-	_setup_req(0, thrd, idx, &xs);
+	_setup_req(pl330, 0, thrd, idx, &xs);
 
 	ret = 0;
 
@@ -2787,6 +2818,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
 	struct resource *res;
 	int i, ret, irq;
 	int num_chan;
+	struct device_node *np = adev->dev.of_node;
 
 	pdat = dev_get_platdata(&adev->dev);
 
@@ -2806,6 +2838,11 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
 
 	pl330->mcbufsz = pdat ? pdat->mcbuf_sz : 0;
 
+	/* get quirk */
+	for (i = 0; i < ARRAY_SIZE(of_quirks); i++)
+		if (of_property_read_bool(np, of_quirks[i].quirk))
+			pl330->quirks |= of_quirks[i].id;
+
 	res = &adev->res;
 	pl330->base = devm_ioremap_resource(&adev->dev, res);
 	if (IS_ERR(pl330->base))
-- 
1.9.1



From wxt at rock-chips.com  Fri Jan 22 03:06:47 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Fri, 22 Jan 2016 19:06:47 +0800
Subject: [PATCH v7 4/9] ARM: dts: Add arm,
 pl330-broken-no-flushp quirk for rk3288 platform
In-Reply-To: <1453460812-8498-1-git-send-email-wxt@rock-chips.com>
References: <1453460812-8498-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1453460812-8498-5-git-send-email-wxt@rock-chips.com>

From: Addy Ke <addy.ke at rock-chips.com>

Pl330 integrated in rk3288 platform doesn't support
DMAFLUSHP function. So we add arm,pl330-broken-no-flushp quirk
for it.

Signed-off-by: Addy Ke <addy.ke at rock-chips.com>
Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
cc: Heiko Stuebner <heiko at sntech.de>
cc: Olof Johansson <olof at lixom.net>
cc: Sonny Rao <sonnyrao at chromium.org>

Reviewed-by: Doug Anderson <dianders at chromium.org>
Reviewed-by: Sonny Rao <sonnyrao at chromium.org>
Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v7: None
Changes in v6: None
Changes in v5: None
Changes in v4: None
Changes in v3:
- add Reviewed-by: Sonny Rao <sonnyrao at chromium.org>

Changes in v2:
- amend the author
- add Reviewed-by: Doug Anderson <dianders at chromium.org>
- amend Olof's mail address

Changes in v1:
- rename broken-no-flushp to "arm,pl330-broken-no-flushp" suggested
  by Krzysztof.
  - remove Sunny's tag

 arch/arm/boot/dts/rk3288.dtsi | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
index f0a0919..eba40dc 100644
--- a/arch/arm/boot/dts/rk3288.dtsi
+++ b/arch/arm/boot/dts/rk3288.dtsi
@@ -147,6 +147,7 @@
 			#dma-cells = <1>;
 			clocks = <&cru ACLK_DMAC2>;
 			clock-names = "apb_pclk";
+			arm,pl330-broken-no-flushp;
 		};
 
 		dmac_bus_ns: dma-controller at ff600000 {
@@ -158,6 +159,7 @@
 			clocks = <&cru ACLK_DMAC1>;
 			clock-names = "apb_pclk";
 			status = "disabled";
+			arm,pl330-broken-no-flushp;
 		};
 
 		dmac_bus_s: dma-controller at ffb20000 {
@@ -168,6 +170,7 @@
 			#dma-cells = <1>;
 			clocks = <&cru ACLK_DMAC1>;
 			clock-names = "apb_pclk";
+			arm,pl330-broken-no-flushp;
 		};
 	};
 
-- 
1.9.1



From wxt at rock-chips.com  Fri Jan 22 03:06:48 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Fri, 22 Jan 2016 19:06:48 +0800
Subject: [PATCH v7 5/9] ARM: dts: Add arm,
 pl330-broken-no-flushp quirk for rk3xxx platform
In-Reply-To: <1453460812-8498-1-git-send-email-wxt@rock-chips.com>
References: <1453460812-8498-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1453460812-8498-6-git-send-email-wxt@rock-chips.com>

From: Shawn Lin <shawn.lin at rock-chips.com>

Pl330 integrated in rk3xxx platform doesn't support
DMAFLUSHP function. So we add arm,pl330-broken-no-flushp quirk
for it.

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
cc: Heiko Stuebner <heiko at sntech.de>
cc: Doug Anderson <dianders at chromium.org>
cc: Olof Johansson <olof at lixom.net>
Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v7: None
Changes in v6: None
Changes in v5: None
Changes in v4: None
Changes in v3: None
Changes in v2: None
Changes in v1:
- rename broken-no-flushp to "arm,pl330-broken-no-flushp" suggested
  by Krzysztof.

 arch/arm/boot/dts/rk3xxx.dtsi | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/arch/arm/boot/dts/rk3xxx.dtsi b/arch/arm/boot/dts/rk3xxx.dtsi
index 99eeea7..0012049 100644
--- a/arch/arm/boot/dts/rk3xxx.dtsi
+++ b/arch/arm/boot/dts/rk3xxx.dtsi
@@ -80,6 +80,7 @@
 			#dma-cells = <1>;
 			clocks = <&cru ACLK_DMA1>;
 			clock-names = "apb_pclk";
+			arm,pl330-broken-no-flushp;
 		};
 
 		dmac1_ns: dma-controller at 2001c000 {
@@ -90,6 +91,7 @@
 			#dma-cells = <1>;
 			clocks = <&cru ACLK_DMA1>;
 			clock-names = "apb_pclk";
+			arm,pl330-broken-no-flushp;
 			status = "disabled";
 		};
 
@@ -101,6 +103,7 @@
 			#dma-cells = <1>;
 			clocks = <&cru ACLK_DMA2>;
 			clock-names = "apb_pclk";
+			arm,pl330-broken-no-flushp;
 		};
 	};
 
-- 
1.9.1



From wxt at rock-chips.com  Fri Jan 22 03:06:50 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Fri, 22 Jan 2016 19:06:50 +0800
Subject: [PATCH v7 7/9] dmaengine: core: expose max burst capability to clients
In-Reply-To: <1453460812-8498-1-git-send-email-wxt@rock-chips.com>
References: <1453460812-8498-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1453460812-8498-8-git-send-email-wxt@rock-chips.com>

From: Shawn Lin <shawn.lin at rock-chips.com>

This patch add max_burst to dma_get_slave_caps for clients
to get the burst capability of slave dma controller.

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v7:
- fix the subject.

Changes in v6:
- remove expose quirk and add dma max_burst caps for clients

Changes in v5: None
Changes in v4: None
Changes in v3: None
Changes in v2: None
Changes in v1: None

 drivers/dma/dmaengine.c   | 1 +
 include/linux/dmaengine.h | 4 ++++
 2 files changed, 5 insertions(+)

diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index c50a247..0cb259c 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -496,6 +496,7 @@ int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps)
 	caps->src_addr_widths = device->src_addr_widths;
 	caps->dst_addr_widths = device->dst_addr_widths;
 	caps->directions = device->directions;
+	caps->max_burst = device->max_burst;
 	caps->residue_granularity = device->residue_granularity;
 	caps->descriptor_reuse = device->descriptor_reuse;
 
diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
index 16a1cad..0a9a0ba 100644
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -401,6 +401,7 @@ enum dma_residue_granularity {
  * 	since the enum dma_transfer_direction is not defined as bits for each
  * 	type of direction, the dma controller should fill (1 << <TYPE>) and same
  * 	should be checked by controller as well
+ * @max_burst: max burst capability per-transfer
  * @cmd_pause: true, if pause and thereby resume is supported
  * @cmd_terminate: true, if terminate cmd is supported
  * @residue_granularity: granularity of the reported transfer residue
@@ -411,6 +412,7 @@ struct dma_slave_caps {
 	u32 src_addr_widths;
 	u32 dst_addr_widths;
 	u32 directions;
+	u32 max_burst;
 	bool cmd_pause;
 	bool cmd_terminate;
 	enum dma_residue_granularity residue_granularity;
@@ -654,6 +656,7 @@ struct dma_filter {
  * 	the enum dma_transfer_direction is not defined as bits for
  * 	each type of direction, the dma controller should fill (1 <<
  * 	<TYPE>) and same should be checked by controller as well
+ * @max_burst: max burst capability per-transfer
  * @residue_granularity: granularity of the transfer residue reported
  *	by tx_status
  * @device_alloc_chan_resources: allocate resources and return the
@@ -712,6 +715,7 @@ struct dma_device {
 	u32 src_addr_widths;
 	u32 dst_addr_widths;
 	u32 directions;
+	u32 max_burst;
 	bool descriptor_reuse;
 	enum dma_residue_granularity residue_granularity;
 
-- 
1.9.1



From wxt at rock-chips.com  Fri Jan 22 03:06:49 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Fri, 22 Jan 2016 19:06:49 +0800
Subject: [PATCH v7 6/9] ARM: dts: add pl330-broken-no-flushp quirk for rk3036
 SoCs
In-Reply-To: <1453460812-8498-1-git-send-email-wxt@rock-chips.com>
References: <1453460812-8498-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1453460812-8498-7-git-send-email-wxt@rock-chips.com>

Pl330 integrated in rk3036 platform that doesn't support
DMAFLUSHP function. So we add 'arm,pl330-broken-no-flushp' quirk
for rk3036.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
---

Changes in v7: None
Changes in v6: None
Changes in v5: None
Changes in v4: None
Changes in v3: None
Changes in v2: None
Changes in v1: None

 arch/arm/boot/dts/rk3036.dtsi | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
index ee457a2..242cafc 100644
--- a/arch/arm/boot/dts/rk3036.dtsi
+++ b/arch/arm/boot/dts/rk3036.dtsi
@@ -102,6 +102,7 @@
 		pdma: pdma at 20078000 {
 			compatible = "arm,pl330", "arm,primecell";
 			reg = <0x20078000 0x4000>;
+			arm,pl330-broken-no-flushp;
 			interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>,
 				     <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;
 			#dma-cells = <1>;
-- 
1.9.1



From wxt at rock-chips.com  Fri Jan 22 03:06:51 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Fri, 22 Jan 2016 19:06:51 +0800
Subject: [PATCH v7 8/9] dmaengine: pl330: add max burst for dmaengine
In-Reply-To: <1453460812-8498-1-git-send-email-wxt@rock-chips.com>
References: <1453460812-8498-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1453460812-8498-9-git-send-email-wxt@rock-chips.com>

From: Shawn Lin <shawn.lin at rock-chips.com>

This patch add max burst capability for dmaengine and
limit burst capability to one for PL330_QUIRK_BROKEN_NO_FLUSHP

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v7:
- fix the subject.

Changes in v6:
- remove expose quirks and add max_burst for dmaengine

Changes in v5: None
Changes in v4: None
Changes in v3: None
Changes in v2: None
Changes in v1: None

 drivers/dma/pl330.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 008408d..1b0453b 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -33,6 +33,7 @@
 #define PL330_MAX_CHAN		8
 #define PL330_MAX_IRQS		32
 #define PL330_MAX_PERI		32
+#define PL330_MAX_BURST         16
 
 #define PL330_QUIRK_BROKEN_NO_FLUSHP BIT(0)
 
@@ -2938,6 +2939,8 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
 	pd->dst_addr_widths = PL330_DMA_BUSWIDTHS;
 	pd->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
 	pd->residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT;
+	pd->max_burst = ((pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP) ?
+			 1 : PL330_MAX_BURST);
 
 	ret = dma_async_device_register(pd);
 	if (ret) {
-- 
1.9.1



From wxt at rock-chips.com  Fri Jan 22 03:06:52 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Fri, 22 Jan 2016 19:06:52 +0800
Subject: [PATCH v7 9/9] spi: rockchip: modify DMA max burst to 1
In-Reply-To: <1453460812-8498-1-git-send-email-wxt@rock-chips.com>
References: <1453460812-8498-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1453460812-8498-10-git-send-email-wxt@rock-chips.com>

From: Addy Ke <addy.ke at rock-chips.com>

Generic dma controller on Rockchips' platform cannot support
DMAFLUSHP instruction which make dma to flush the req of non-aligned
or non-multiple of what we need. That will cause an unrecoverable
dma bus error. The saftest way is to set dma max burst to 1.

Signed-off-by: Addy ke <addy.ke at rock-chips.com>
Fixes: 64e36824b32b06 ("spi/rockchip: add driver for Rockchip...")
Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
cc: Heiko Stuebner <heiko at sntech.de>
cc: Olof Johansson <olof at lixom.net>
cc: Doug Anderson <dianders at chromium.org>
cc: Sonny Rao <sonnyrao at chromium.org>

Acked-by: Mark Brown <broonie at kernel.org>
Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v7: None
Changes in v6:
- remove quirks and get dma caps in order to limit burst

Changes in v5: None
Changes in v4: None
Changes in v3: None
Changes in v2: None
Changes in v1: None

 drivers/spi/spi-rockchip.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c
index 79a8bc4..aa9561f 100644
--- a/drivers/spi/spi-rockchip.c
+++ b/drivers/spi/spi-rockchip.c
@@ -199,6 +199,7 @@ struct rockchip_spi {
 	struct sg_table rx_sg;
 	struct rockchip_spi_dma_data dma_rx;
 	struct rockchip_spi_dma_data dma_tx;
+	struct dma_slave_caps dma_caps;
 };
 
 static inline void spi_enable_chip(struct rockchip_spi *rs, int enable)
@@ -449,7 +450,10 @@ static void rockchip_spi_prepare_dma(struct rockchip_spi *rs)
 		rxconf.direction = rs->dma_rx.direction;
 		rxconf.src_addr = rs->dma_rx.addr;
 		rxconf.src_addr_width = rs->n_bytes;
-		rxconf.src_maxburst = rs->n_bytes;
+		if (rs->dma_caps.max_burst > 4)
+			rxconf.src_maxburst = 4;
+		else
+			rxconf.src_maxburst = 1;
 		dmaengine_slave_config(rs->dma_rx.ch, &rxconf);
 
 		rxdesc = dmaengine_prep_slave_sg(
@@ -466,7 +470,10 @@ static void rockchip_spi_prepare_dma(struct rockchip_spi *rs)
 		txconf.direction = rs->dma_tx.direction;
 		txconf.dst_addr = rs->dma_tx.addr;
 		txconf.dst_addr_width = rs->n_bytes;
-		txconf.dst_maxburst = rs->n_bytes;
+		if (rs->dma_caps.max_burst > 4)
+			txconf.dst_maxburst = 4;
+		else
+			txconf.dst_maxburst = 1;
 		dmaengine_slave_config(rs->dma_tx.ch, &txconf);
 
 		txdesc = dmaengine_prep_slave_sg(
@@ -730,6 +737,7 @@ static int rockchip_spi_probe(struct platform_device *pdev)
 	}
 
 	if (rs->dma_tx.ch && rs->dma_rx.ch) {
+		dma_get_slave_caps(rs->dma_rx.ch, &(rs->dma_caps));
 		rs->dma_tx.addr = (dma_addr_t)(mem->start + ROCKCHIP_SPI_TXDR);
 		rs->dma_rx.addr = (dma_addr_t)(mem->start + ROCKCHIP_SPI_RXDR);
 		rs->dma_tx.direction = DMA_MEM_TO_DEV;
-- 
1.9.1



From heiko at sntech.de  Fri Jan 22 05:20:05 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Fri, 22 Jan 2016 14:20:05 +0100
Subject: [PATCH] Documentation: rockchip-dw-mshc: add RK3368 dw-mshc
 description
In-Reply-To: <1453379529-5148-1-git-send-email-shawn.lin@rock-chips.com>
References: <1453379529-5148-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <1671570.Z9uqteZdW0@phil>

Am Donnerstag, 21. Januar 2016, 20:32:09 schrieb Shawn Lin:
> rk3368 dtsi file add dw-mshc compatible "rockchip,rk3368-dw-mshc"
> but didn't add it into rockchip-dw-mshc.txt.
> 
> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>

applied to my dts32 branch for 4.6 with Rob's ack


Thanks
Heiko


From heiko at sntech.de  Fri Jan 22 05:33:13 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Fri, 22 Jan 2016 14:33:13 +0100
Subject: [PATCH] ARM: dts: rockchip: Bump sd card pin drive strength up on
 firefly boards
In-Reply-To: <1453381033-3479-1-git-send-email-w.egorov@phytec.de>
References: <1453381033-3479-1-git-send-email-w.egorov@phytec.de>
Message-ID: <1922254.O59ZZAW3Pv@phil>

Am Donnerstag, 21. Januar 2016, 13:57:13 schrieb Wadim Egorov:
> It seems some firefly boards need 12mA drive strength for sdmmc.
> Using 4mA/8mA drive strength will cause the kernel to fail to recognize
> the sd card correctly.
> Increased the sdmmc lines drive strength from 4mA to 12mA.
> 
> Signed-off-by: Wadim Egorov <w.egorov at phytec.de>

applied to my dts32 branch for 4.6.

As I said somewhere on IRC before, it might be worthwhile to bump the 
default drive-strength at some point, but so far it seems the minimal needed 
values seem to diverge (8mA on Veyron, 12ma [4mA on mine] on firefly and 
seemingly 4mA working fine on the Rock2). So I guess we'll postpone that a 
bit more until we have more datapoints ;-)


Heiko


From tomeu at tomeuvizoso.net  Fri Jan 22 06:00:27 2016
From: tomeu at tomeuvizoso.net (Tomeu Vizoso)
Date: Fri, 22 Jan 2016 15:00:27 +0100
Subject: [PATCH 0/5] clk: rockchip: add full support for HDMI clock on
 rk3288
In-Reply-To: <CAD=FV=VB93wYigRJ8wa8YL6P+Q+-fRokPiCB-Fo1pC_zAdh2oA@mail.gmail.com>
References: <1415087559-19444-1-git-send-email-kever.yang@rock-chips.com>
 <48806799.thBSifdUp8@diego> <54647142.4080800@rock-chips.com>
 <CAD=FV=Vzec3Qm-kc6zZ4VxSREoZbzVkWbzp-noHYdcEVrLYYSw@mail.gmail.com>
 <CAAObsKChYN5nmcm5OWnAzNRGDi_QGSoKvN+0FizhjeT-3mmeXQ@mail.gmail.com>
 <CAD=FV=Wgu=xQ9PcVqfzcedXVU3Rh-HKbBVxHuqgV9OZJEYhYvg@mail.gmail.com>
 <CAAObsKC1v--mZwkiG-c7P2Bwi2-_txbeqaW4Vd5h-noAVNbhjA@mail.gmail.com>
 <CAD=FV=VB93wYigRJ8wa8YL6P+Q+-fRokPiCB-Fo1pC_zAdh2oA@mail.gmail.com>
Message-ID: <CAAObsKCMh3Bk_oNJdLbU2F9b7jrnFZOp2uFR_oPqUoJ3W1gfaQ@mail.gmail.com>

On 21 January 2016 at 21:11, Doug Anderson <dianders at chromium.org> wrote:
> Hi,
>
> On Thu, Jan 21, 2016 at 1:03 AM, Tomeu Vizoso <tomeu at tomeuvizoso.net> wrote:
>> So we have a mechanism for detecting a conflict in the clock
>> hierarchy, and a mechanism to solve it, but we are missing a way for
>> userspace to communicate policy regarding which clocks should be given
>> priority when solving such a conflict?
>
> Hrmmm, I guess it could be userspace that makes the decision.  It does
> seem a little odd to force it to userspace in all cases, though.  For
> a particular laptop that is designed with a specific panel connected
> up eDP it seems less than ideal to push this into userspace.  If the
> kernel could just work in the expected sane way (or at least work that
> way by default) it would be ideal.

Ah, I was wrongly assuming that the kernel didn't have enough
information to make an informed decision in this case, sorry.

Guess the per-user rate limits don't help here because the consumer
with higher priority could work with frequencies other than the ideal.

And we cannot have a consumer listening for PRE_RATE_CHANGE and
aborting unwanted changes or rerouting the ancestors of the clocks of
other consumers because that would be a massive violation of
separation of concerns.

If we were to rearrange the clock topology from within the CCF, then
consumers need to have a way to communicate to the core that they are
more important than other consumers. clk_set_important(clk, true)
could be enough in this case, but would be insufficient in more
complex cases where more than two clocks could use the same PLL.

> If the kernel doesn't try to do anything sane by default then you're
> creating a requirement for everyone's userspace to somehow figure this
> out.  Do you expect there to be UI here, or that this would be
> something that would be figured out by the Linux distribution?
> Certainly exposing UI on something like a laptop with a builtin panel
> wouldn't make any sense to me, but it might make sense if you had an
> eval board with different display connectors on it.  If there's no UI,
> would the Linux distribution need to somehow identify which board we
> were on and then have a big lookup table about how to configure
> things?

If we don't actually need input from userspace for this use case, I
wouldn't go this way right now, because it seems to me like it could
be a really big timesink for little gain.

Once someone comes with a situation in which feedback from userspace
is really needed, that person can propose such an interface ;)

Regards,

Tomeu


From heiko at sntech.de  Fri Jan 22 06:51:42 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Fri, 22 Jan 2016 15:51:42 +0100
Subject: [PATCH] ARM: dts: rockchip: make more compatible for rk3036 SoCs
In-Reply-To: <1452733721-28110-1-git-send-email-wxt@rock-chips.com>
References: <1452733721-28110-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1489399.Xf0pP9CdpZ@phil>

Am Donnerstag, 14. Januar 2016, 09:08:41 schrieb Caesar Wang:
> This patch will be better to support and compatible for rk3036.
> Although some other SoCs drivers also can work for rk3036, that will
> prevent some mutatin factors for drivers.
> 
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>

applied to my dts32 branch for 4.6


Thanks
Heiko


From dianders at chromium.org  Fri Jan 22 09:07:50 2016
From: dianders at chromium.org (Doug Anderson)
Date: Fri, 22 Jan 2016 09:07:50 -0800
Subject: [PATCH 0/5] clk: rockchip: add full support for HDMI clock on
 rk3288
In-Reply-To: <CAAObsKCMh3Bk_oNJdLbU2F9b7jrnFZOp2uFR_oPqUoJ3W1gfaQ@mail.gmail.com>
References: <1415087559-19444-1-git-send-email-kever.yang@rock-chips.com>
 <48806799.thBSifdUp8@diego> <54647142.4080800@rock-chips.com>
 <CAD=FV=Vzec3Qm-kc6zZ4VxSREoZbzVkWbzp-noHYdcEVrLYYSw@mail.gmail.com>
 <CAAObsKChYN5nmcm5OWnAzNRGDi_QGSoKvN+0FizhjeT-3mmeXQ@mail.gmail.com>
 <CAD=FV=Wgu=xQ9PcVqfzcedXVU3Rh-HKbBVxHuqgV9OZJEYhYvg@mail.gmail.com>
 <CAAObsKC1v--mZwkiG-c7P2Bwi2-_txbeqaW4Vd5h-noAVNbhjA@mail.gmail.com>
 <CAD=FV=VB93wYigRJ8wa8YL6P+Q+-fRokPiCB-Fo1pC_zAdh2oA@mail.gmail.com>
 <CAAObsKCMh3Bk_oNJdLbU2F9b7jrnFZOp2uFR_oPqUoJ3W1gfaQ@mail.gmail.com>
Message-ID: <CAD=FV=W=LCJk4m049hBnm7BOYudnVe=oa3jdcSrrZZUU9rFtog@mail.gmail.com>

Tomeu,

On Fri, Jan 22, 2016 at 6:00 AM, Tomeu Vizoso <tomeu at tomeuvizoso.net> wrote:
> On 21 January 2016 at 21:11, Doug Anderson <dianders at chromium.org> wrote:
>> Hi,
>>
>> On Thu, Jan 21, 2016 at 1:03 AM, Tomeu Vizoso <tomeu at tomeuvizoso.net> wrote:
>>> So we have a mechanism for detecting a conflict in the clock
>>> hierarchy, and a mechanism to solve it, but we are missing a way for
>>> userspace to communicate policy regarding which clocks should be given
>>> priority when solving such a conflict?
>>
>> Hrmmm, I guess it could be userspace that makes the decision.  It does
>> seem a little odd to force it to userspace in all cases, though.  For
>> a particular laptop that is designed with a specific panel connected
>> up eDP it seems less than ideal to push this into userspace.  If the
>> kernel could just work in the expected sane way (or at least work that
>> way by default) it would be ideal.
>
> Ah, I was wrongly assuming that the kernel didn't have enough
> information to make an informed decision in this case, sorry.
>
> Guess the per-user rate limits don't help here because the consumer
> with higher priority could work with frequencies other than the ideal.
>
> And we cannot have a consumer listening for PRE_RATE_CHANGE and
> aborting unwanted changes or rerouting the ancestors of the clocks of
> other consumers because that would be a massive violation of
> separation of concerns.
>
> If we were to rearrange the clock topology from within the CCF, then
> consumers need to have a way to communicate to the core that they are
> more important than other consumers. clk_set_important(clk, true)
> could be enough in this case, but would be insufficient in more
> complex cases where more than two clocks could use the same PLL.

With something like the above I'd still expect some complexity
depending on the probe order.  If a less important device grabs the
clock first, it might be forced to re-think its clocks later.  That
might be disconcerting.

OK, so I was just involved in a change recently that made me realize
that maybe our original problems were tied to the fact that our
builtin panels were trying to specify a clock that was impossible to
achieve with CPLL / GPLL.  It was a known problem that the request
would be denied and the CCF would just pick the closest rate it could.
Probably the right thing is to solve _that_ problem first.  If using
simple panel you could do a change like
<https://chromium-review.googlesource.com/#/c/323211/> (though
presumably you'd have to handle people using the same panel in other
laptops).  You might also be able to do funny things to fixup the mode
like dbehr tried to do in
<https://chromium-review.googlesource.com/#/c/270017/>.  By doing this
and making sure that

With that being said, how about this as a solution:

1. Figure some way (presumably some type of device tree property?) to
make sure that the builtin panel chose a clock that was achievable by
dividing CPLL / GPLL on rk3288.  If you have eDP but no builtin panel
you wouldn't specify this and you'd just let the system pick whatever
it wanted.

2. We use "assigned-clocks" to (by default) make NPLL something silly
and unattractive.  This will keep the common clock framework from
picking it to use as the source if the clock could be made just as
well from CPLL / GPLL (or find some other way to make NPLL a lower
priority muxing option).

3. We use the "dibs" rule in that the first display driver to claim
NPLL blocks anyone else from changing things.


In the above on a laptop with a builtin panel and an HDMI port you'd
get the panel using CPLL / GPLL and HDMI getting CPLL, GPLL or NPLL
(and it would be able to change NPLL if needed).


In the above on a dev board with a lot of ports then the first device
probed would get the most flexibility.  Future devices would be
limited in their choices.

-Doug


From broonie at kernel.org  Fri Jan 22 09:18:15 2016
From: broonie at kernel.org (Mark Brown)
Date: Fri, 22 Jan 2016 17:18:15 +0000
Subject: [PATCH v3 3/9] ASoC: rockchip: i2s: add support for grabbing
 output clock to codec
In-Reply-To: <CAPz6YkWt-iXS6P4QBBoq3v3i7ZAGGu6=_9AHw4qgevEwDwbdTA@mail.gmail.com>
References: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
 <1452865796-23527-4-git-send-email-wxt@rock-chips.com>
 <20160115174623.GZ6588@sirena.org.uk>
 <CAPz6YkWt-iXS6P4QBBoq3v3i7ZAGGu6=_9AHw4qgevEwDwbdTA@mail.gmail.com>
Message-ID: <20160122171815.GD6588@sirena.org.uk>

On Fri, Jan 15, 2016 at 01:48:04PM -0800, Sonny Rao wrote:
> On Fri, Jan 15, 2016 at 9:46 AM, Mark Brown <broonie at kernel.org> wrote:

> > If the I2S block is providing a clock to the CODEC then that's what the
> > software should do so that the CODEC can gate and ungate the clock as
> > required.  This patch has the I2S block using a clock, not providing
> > one.

> From my read of the clock diagram for RK3288  there is a single clock
> signal (labeled "clk_i2s0") that comes out of a fractional divider,
> and it is split such that one path gets sent to the I2S block and the
> second path is sent to a mux after which that signal is sent to an
> external pin that goes to the codec.

> There are separate clock gates for the two paths: one for the I2S
> block and one after that mux before the external pin.

> I'm not sure if it's being modeled that way in the Linux code or not,
> but at least physically I don't think this clock signal actually goes
> through the I2S block before being sent to the codec.

That's not really the issue here, the issue is that it's not the I2S
controller that is consuming the clock so it should not be the I2S
controller driver that ensures that the clock is enabled.  The driver
that manages the clock should be the one that uses it, like I say this
means you should add the code to enable the clock to the CODEC driver if
the CODEC driver needs the clock enabled.

> Does that help clarify?

The problem here isn't a lack of clarity in the situation.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160122/4510a8e8/attachment.sig>

From dianders at chromium.org  Fri Jan 22 10:18:36 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Fri, 22 Jan 2016 10:18:36 -0800
Subject: [PATCH v5 01/21] usb: dwc2: rockchip: Make the max_transfer_size
 automatic
In-Reply-To: <1453486736-15358-1-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
Message-ID: <1453486736-15358-2-git-send-email-dianders@chromium.org>

Previously we needed to set the max_transfer_size to explicitly be 65535
because the old driver would detect that our hardware could support much
bigger transfers and then would try to do them.  This wouldn't work
since the DMA alignment code couldn't support it.

Later in commit e8f8c14d9da7 ("usb: dwc2: clip max_transfer_size to
65535") upstream added support for clipping this automatically.  Since
that commit it has been OK to just use "-1" (default), but nobody
bothered to change it.

Let's change it to default now for two reasons:
- It's nice to use autodetected params.
- If we can remove the 65535 limit, we can transfer more!

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Acked-by: John Youn <johnyoun at synopsys.com>
Tested-by: Heiko Stuebner <heiko at sntech.de>
---
Changes in v5: None
Changes in v4:
- Add John's Acks from <https://patchwork.kernel.org/patch/7631551>

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/platform.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
index 510f787434b3..5008a467ce06 100644
--- a/drivers/usb/dwc2/platform.c
+++ b/drivers/usb/dwc2/platform.c
@@ -129,7 +129,7 @@ static const struct dwc2_core_params params_rk3066 = {
 	.host_rx_fifo_size		= 520,	/* 520 DWORDs */
 	.host_nperio_tx_fifo_size	= 128,	/* 128 DWORDs */
 	.host_perio_tx_fifo_size	= 256,	/* 256 DWORDs */
-	.max_transfer_size		= 65535,
+	.max_transfer_size		= -1,
 	.max_packet_count		= -1,
 	.host_channels			= -1,
 	.phy_type			= -1,
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Fri Jan 22 10:18:35 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Fri, 22 Jan 2016 10:18:35 -0800
Subject: [PATCH v5 0/21] usb: dwc2: host: Fix and speed up all the stuff,
 especially with splits
Message-ID: <1453486736-15358-1-git-send-email-dianders@chromium.org>

This is a bit of catchall series for all the bug fix and performance
patches I've been working on over the last few months.  Note that for
dwc2 we need to do LOTS in software and need super low interrupt
latency, so most performance improvements actually fix real bugs.

Patches are structured to start with no-brainer stuff that could be
applied ASAP, especially things I've already gotten Acks for.  Things
get slightly more RFC / RFT like as we get farther down the series.
Anything that can be landed sooner rather than later (especially those
Acked long ago) would help in re-posts (I'm not biased, of course).

It's been a few months since my last post of this series.  In the
meantime I've added a bunch of small bugfixes to the start of it and
also TOTALLY REWROTE the microframe scheduler.  I'll say up front: I
know nothing about USB.  I haven't read the whole spec.  I'm not
terribly familiar with the OHCI, EHCI, and XHCI drivers in the kernel.
...and I'm pretty clueless overall.  Nevertheless, I've attempted to
write up a fancy scheduler based on the portion of the spec talking
about microframe scheduling requirements.  This rewritten scheduler does
seem to help when I start jamming lots of USB things into a hub, so
presumably the code is a reasonably starting point.  Given my current
understanding of USB the old code was fairly insane, so presumably even
if my new patch isn't perfect it's better than what we had.

Anyway, on to the patches:

1. usb: dwc2: rockchip: Make the max_transfer_size automatic

   No brainer.  Can land any time.

2. usb: dwc2: host: Get aligned DMA in a more supported way

   Although this touches a lot of code, it's mostly just deleting
   stuff.  The way this is working is nearly the same as tegra.  Biggest
   objection I expect is that it has too much duplication with tegra and
   musb.  I'd personally prefer to land it now and remove duplication
   later, but up to others.  Speeding up interrupt handler helps with
   SOF scheduling, so this is not just a dumb optimization.

3. usb: dwc2: host: Set host_rx_fifo_size to 528 for rk3066
4. usb: dwc2: host: Set host_perio_tx_fifo_size to 304 for rk3066

   Seems like a good idea and small impact, but if someone hates it or
   it breaks on some Rockchip SoC, just drop it.  I've only tested on
   rk3288 so it would be nice if someone with access to more Rockchip
   SoCs can give a tested by.

5. usb: dwc2: host: Avoid use of chan->qh after qh freed

   Simple bugfix.  Unrelated to the series but thrown in here.

6. usb: dwc2: host: Always add to the tail of queues

   Big functionality improvement.  Small patch.  Suggest applying ASAP.

7. usb: dwc2: hcd: fix split transfer schedule sequence

   Unless I'm misunderstanding, this should be a no-brainer to fix.
   Could be some bikeshedding on how to fix this.  Let me know if/how
   you want me to spin.  Otherwise I'd say land it and it will fix a
   bunch of stuff.

8. usb: dwc2: host: Add scheduler tracing

   Shouldn't hurt anything.  If you have bikesheds, let me know.

9.  usb: dwc2: host: Add a delay before releasing periodic bandwidth
10. usb: dwc2: host: Giveback URB in tasklet context

   I think we should take these.  They improve things a bunch and I have
   found no regressions due to them.  Additional testing appreciated, of
   course.

11. usb: dwc2: host: Use periodic interrupt even with DMA

   Just came up with this one recently so it's had slightly less
   testing.  ...but it certainly fixed a bunch of stuff.  Could probably
   be moved around in the series to be pretty much anywhere.  I don't
   think this has a huge impact until we fix the scheduler (below) but
   at the same time I'm pretty sure it's something that's been wrong for
   a long time.

12. usb: dwc2: host: Rename some fields in struct dwc2_qh
13. usb: dwc2: host: Reorder things in hcd_queue.c
14. usb: dwc2: host: Split code out to make dwc2_do_reserve()

   Cleanups to make future patches easier to understand.  Bikeshed away.
   All no-op changes.

15. usb: dwc2: host: Add scheduler logging for missed SOFs

   I found this to be quite helpful.  If you hate it, drop it from the
   series.

16. usb: dwc2: host: Manage frame nums better in scheduler

   Doesn't totally make sense on its own, but a good halfway point to
   the microframe scheduler.  ...and shouldn't regress anything.  Allows
   us to do the "Properly set even/odd frame" patch below which
   definitely improves things.

17. usb: dwc2: host: Schedule periodic right away if it's time

   Yet another small change to make scheduling tighter.

18. usb: dwc2: host: Add dwc2_hcd_get_future_frame_number() call

   Prep for ("usb: dwc2: host: Properly set even/odd frame")

19. usb: dwc2: host: Properly set even/odd frame

   Helps quite a bit.  Helps even more after the redone microframe
   scheduler.  Feel free to tidy up if you see easy ways to do this.
   Maybe someone has a better way to estimate time on the wire?

20. usb: dwc2: host: Totally redo the microframe scheduler

   Eyeballs please!  I think I've stared at this too much and now my
   eyes are glazing over.  This definitely helps but also probably needs
   a few more spins?  Of course, if nobody wants to review it, IMHO
   checking it in as-is is WAAAAY better than what we had before.

21. usb: dwc2: host: If using uframe scheduler, end splits better

   Low confidence in this one.  Worry that it will end something too
   soon, but haven't seen it yet.

===

Below is discussion of some of the speedup stuff (mostly relevant to the
first few patches).

===

The dwc2 interrupt handler is quite slow.  On rk3288 with a few things
plugged into the ports and with cpufreq locked at 696MHz (to simulate
real world idle system), I can easily observe dwc2_handle_hcd_intr()
taking > 120 us, sometimes > 150 us.  Note that SOF interrupts come
every 125 us with high speed USB, so taking > 120 us in the interrupt
handler is a big deal.

The patches here will speed up the interrupt controller significantly.
After this series, I have a hard time seeing the interrupt controller
taking > 20 us and I don't ever see it taking > 30 us ever in my tests
unless I bring the cpufreq back down.  With the cpufreq at 126 MHz I can
still see the interrupt handler take > 50 us, so I'm sure we could
improve this further.  ...but hey, it's a start.

This series also shows big speed improvements when testing with a USB
Gigabit Ethernet adapter.  Previously the tested adapter would top out
at about 15MB/s.  After these changes it gets about 23MB/s.

In addition to the speedup, this series also has the advantage of
simplifying dwc2 and making it more like everyone else (introducing the
possibility of future simplifications).  Picking this series up will
help your diffstat and likely win you friends.  ;)

===

Steps for gathering data with ftrace (for some reason I have to run
twice):

cd /sys/devices/system/cpu/cpu0/cpufreq/
echo userspace > scaling_governor
echo 696000 > scaling_setspeed

cd /sys/kernel/debug/tracing
echo 0 > tracing_on
echo "" > trace
echo nop > current_tracer
echo function_graph > current_tracer
echo dwc2_handle_hcd_intr > set_graph_function
echo dwc2_handle_common_intr >> set_graph_function
echo dwc2_handle_hcd_intr > set_ftrace_filter
echo dwc2_handle_common_intr >> set_ftrace_filter
echo funcgraph-abstime > trace_options
echo 70 > tracing_thresh
echo 1 > /sys/kernel/debug/tracing/tracing_on

sleep 2
cat trace

Changes in v5:
- Move list maintenance to hcd.c to avoid gadget-only compile error
- Moved defines outside of ifdef to avoid gadget-only compile error.

Changes in v4:
- Add John's Acks from <https://patchwork.kernel.org/patch/7631551>
- Set host_rx_fifo_size to 528 for rk3066 new for v4.
- Set host_perio_tx_fifo_size to 304 for rk3066 new for v4.
- Avoid use of chan->qh after qh freed new for v4.
- Always add to the tail of queues new for v4.
- fix split transfer schedule sequence new for v4.
- Retooled scheduler tracing a bit, so left off John's Ack from v3.
- Moved periodic bandwidth release delay patch earlier again.
- A bit earlier in the list of patches than in v3.
- Use periodic interrupt even with DMA new for v4.
- Rename some fields in struct dwc2_qh new for v4.
- Reorder things in hcd_queue.c new for v4.
- Split code out to make dwc2_do_reserve() new for v4.
- Add scheduler logging for missed SOFs new for v4.
- Manage frame nums better in scheduler new for v4.
- Schedule periodic right away if it's time new for v4.
- Add dwc2_hcd_get_future_frame_number() call new for v4.
- Properly set even/odd frame new for v4.
- Figured out what the microframe scheduler was supposed to do.
- Microframe rewrite is totally different from v3, hopefully more right.
- Microframe rewrite is later in the series now.
- If using uframe scheduler, end splits better new for v4.

Changes in v3:
- Moved periodic bandwidth release delay patch later in the series.
- The uframe scheduler patch is folded into optimization series.
- Optimize uframe scheduler "single uframe" case a little.
- uframe scheduler now atop logging patches.
- uframe scheduler now before delayed bandwidth release patches.
- Add defines like EARLY_FRAME_USEC
- Reorder dwc2_deschedule_periodic() in prep for future patches.
- uframe scheduler now shows real usefulness w/ future patches!
- Assuming single_tt is new for v3; not terribly well tested (yet).
- Keep track and use our uframe new for v3.

Changes in v2:
- Add a warn if setup_dma is not aligned (Julius Werner).
- Periodic bandwidth release delay new for V2
- Commit message now says that URB giveback change needs delay change.
- Totally rewrote uframe scheduler again after writing test code.
- uframe scheduler atop delayed bandwidth release patches.

Douglas Anderson (21):
  usb: dwc2: rockchip: Make the max_transfer_size automatic
  usb: dwc2: host: Get aligned DMA in a more supported way
  usb: dwc2: host: Set host_rx_fifo_size to 528 for rk3066
  usb: dwc2: host: Set host_perio_tx_fifo_size to 304 for rk3066
  usb: dwc2: host: Avoid use of chan->qh after qh freed
  usb: dwc2: host: Always add to the tail of queues
  usb: dwc2: hcd: fix split transfer schedule sequence
  usb: dwc2: host: Add scheduler tracing
  usb: dwc2: host: Add a delay before releasing periodic bandwidth
  usb: dwc2: host: Giveback URB in tasklet context
  usb: dwc2: host: Use periodic interrupt even with DMA
  usb: dwc2: host: Rename some fields in struct dwc2_qh
  usb: dwc2: host: Reorder things in hcd_queue.c
  usb: dwc2: host: Split code out to make dwc2_do_reserve()
  usb: dwc2: host: Add scheduler logging for missed SOFs
  usb: dwc2: host: Manage frame nums better in scheduler
  usb: dwc2: host: Schedule periodic right away if it's time
  usb: dwc2: host: Add dwc2_hcd_get_future_frame_number() call
  usb: dwc2: host: Properly set even/odd frame
  usb: dwc2: host: Totally redo the microframe scheduler
  usb: dwc2: host: If using uframe scheduler, end splits better

 drivers/usb/dwc2/core.c      |  115 ++-
 drivers/usb/dwc2/core.h      |  114 ++-
 drivers/usb/dwc2/hcd.c       |  390 ++++++---
 drivers/usb/dwc2/hcd.h       |  126 ++-
 drivers/usb/dwc2/hcd_ddma.c  |   41 +-
 drivers/usb/dwc2/hcd_intr.c  |  164 ++--
 drivers/usb/dwc2/hcd_queue.c | 1995 ++++++++++++++++++++++++++++++++++--------
 drivers/usb/dwc2/platform.c  |    6 +-
 8 files changed, 2279 insertions(+), 672 deletions(-)

-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Fri Jan 22 10:18:38 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Fri, 22 Jan 2016 10:18:38 -0800
Subject: [PATCH v5 03/21] usb: dwc2: host: Set host_rx_fifo_size to 528 for
 rk3066
In-Reply-To: <1453486736-15358-1-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
Message-ID: <1453486736-15358-4-git-send-email-dianders@chromium.org>

As documented in dwc2_calculate_dynamic_fifo(), host_rx_fifo_size should
really be:
 2 * ((Largest Packet size / 4) + 1 + 1) + n
 with n = number of host channel.

We have 9 host channels, so
 2 * ((1024/4) + 2) + 9 = 516 + 9 = 525

We've got 960 / 972 total_fifo_size on rk3288 (and presumably on
rk3066) and 525 + 128 + 256 = 909 so we're still under on both ports
even when we increment by 5.

Since we have space, Kever Yang suggests bumping by 8.  He says this
will meet INCR16 access and next fifo type can start with a aligned
address.

...so let's bump up by 8.  In the future, it would be nice if
dwc2_calculate_dynamic_fifo() could handle the "too small" FIFO case and
come up with something more dynamically.  When we do that we can figure
out how to allocate the extra 48 / 60 bytes of FIFO that we're currently
wasting.

NOTE: no known bugs are fixed by this patch, but it seems like a simple
fix and ought to fix someone.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v5: None
Changes in v4:
- Set host_rx_fifo_size to 528 for rk3066 new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/platform.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
index 5008a467ce06..b6d7666e715c 100644
--- a/drivers/usb/dwc2/platform.c
+++ b/drivers/usb/dwc2/platform.c
@@ -126,7 +126,7 @@ static const struct dwc2_core_params params_rk3066 = {
 	.speed				= -1,
 	.enable_dynamic_fifo		= 1,
 	.en_multiple_tx_fifo		= -1,
-	.host_rx_fifo_size		= 520,	/* 520 DWORDs */
+	.host_rx_fifo_size		= 528,	/* 528 DWORDs */
 	.host_nperio_tx_fifo_size	= 128,	/* 128 DWORDs */
 	.host_perio_tx_fifo_size	= 256,	/* 256 DWORDs */
 	.max_transfer_size		= -1,
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Fri Jan 22 10:18:37 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Fri, 22 Jan 2016 10:18:37 -0800
Subject: [PATCH v5 02/21] usb: dwc2: host: Get aligned DMA in a more supported
 way
In-Reply-To: <1453486736-15358-1-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
Message-ID: <1453486736-15358-3-git-send-email-dianders@chromium.org>

All other host controllers who want aligned buffers for DMA do it a
certain way.  Let's do that too instead of working behind the USB core's
back.  This makes our interrupt handler not take forever and also rips
out a lot of code, simplifying things a bunch.

This also has the side effect of removing the 65535 max transfer size
limit.

NOTE: The actual code to allocate the aligned buffers is ripped almost
completely from the tegra EHCI driver.  At some point in the future we
may want to add this functionality to the USB core to share more code
everywhere.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Acked-by: John Youn <johnyoun at synopsys.com>
Tested-by: Heiko Stuebner <heiko at sntech.de>
Tested-by: John Youn <johnyoun at synopsys.com>
---
Changes in v5: None
Changes in v4:
- Add John's Acks from <https://patchwork.kernel.org/patch/7631551>

Changes in v3: None
Changes in v2:
- Add a warn if setup_dma is not aligned (Julius Werner).

 drivers/usb/dwc2/core.c      |  21 +-----
 drivers/usb/dwc2/hcd.c       | 170 +++++++++++++++++++++----------------------
 drivers/usb/dwc2/hcd.h       |  10 ---
 drivers/usb/dwc2/hcd_intr.c  |  65 -----------------
 drivers/usb/dwc2/hcd_queue.c |   7 +-
 5 files changed, 87 insertions(+), 186 deletions(-)

diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index 39a0fa8a4c0a..73f2771b7740 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -1958,19 +1958,11 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
 	}
 
 	if (hsotg->core_params->dma_enable > 0) {
-		dma_addr_t dma_addr;
-
-		if (chan->align_buf) {
-			if (dbg_hc(chan))
-				dev_vdbg(hsotg->dev, "align_buf\n");
-			dma_addr = chan->align_buf;
-		} else {
-			dma_addr = chan->xfer_dma;
-		}
-		dwc2_writel((u32)dma_addr, hsotg->regs + HCDMA(chan->hc_num));
+		dwc2_writel((u32)chan->xfer_dma,
+			    hsotg->regs + HCDMA(chan->hc_num));
 		if (dbg_hc(chan))
 			dev_vdbg(hsotg->dev, "Wrote %08lx to HCDMA(%d)\n",
-				 (unsigned long)dma_addr, chan->hc_num);
+				 (unsigned long)chan->xfer_dma, chan->hc_num);
 	}
 
 	/* Start the split */
@@ -3363,13 +3355,6 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
 	width = (hwcfg3 & GHWCFG3_XFER_SIZE_CNTR_WIDTH_MASK) >>
 		GHWCFG3_XFER_SIZE_CNTR_WIDTH_SHIFT;
 	hw->max_transfer_size = (1 << (width + 11)) - 1;
-	/*
-	 * Clip max_transfer_size to 65535. dwc2_hc_setup_align_buf() allocates
-	 * coherent buffers with this size, and if it's too large we can
-	 * exhaust the coherent DMA pool.
-	 */
-	if (hw->max_transfer_size > 65535)
-		hw->max_transfer_size = 65535;
 	width = (hwcfg3 & GHWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK) >>
 		GHWCFG3_PACKET_SIZE_CNTR_WIDTH_SHIFT;
 	hw->max_packet_count = (1 << (width + 4)) - 1;
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 8847c72e55f6..bc4bdbc1534e 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -635,9 +635,9 @@ static void dwc2_hc_init_split(struct dwc2_hsotg *hsotg,
 	chan->hub_port = (u8)hub_port;
 }
 
-static void *dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
-			       struct dwc2_host_chan *chan,
-			       struct dwc2_qtd *qtd, void *bufptr)
+static void dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
+			      struct dwc2_host_chan *chan,
+			      struct dwc2_qtd *qtd)
 {
 	struct dwc2_hcd_urb *urb = qtd->urb;
 	struct dwc2_hcd_iso_packet_desc *frame_desc;
@@ -657,7 +657,6 @@ static void *dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
 			else
 				chan->xfer_buf = urb->setup_packet;
 			chan->xfer_len = 8;
-			bufptr = NULL;
 			break;
 
 		case DWC2_CONTROL_DATA:
@@ -684,7 +683,6 @@ static void *dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
 				chan->xfer_dma = hsotg->status_buf_dma;
 			else
 				chan->xfer_buf = hsotg->status_buf;
-			bufptr = NULL;
 			break;
 		}
 		break;
@@ -717,14 +715,6 @@ static void *dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
 
 		chan->xfer_len = frame_desc->length - qtd->isoc_split_offset;
 
-		/* For non-dword aligned buffers */
-		if (hsotg->core_params->dma_enable > 0 &&
-		    (chan->xfer_dma & 0x3))
-			bufptr = (u8 *)urb->buf + frame_desc->offset +
-					qtd->isoc_split_offset;
-		else
-			bufptr = NULL;
-
 		if (chan->xact_pos == DWC2_HCSPLT_XACTPOS_ALL) {
 			if (chan->xfer_len <= 188)
 				chan->xact_pos = DWC2_HCSPLT_XACTPOS_ALL;
@@ -733,63 +723,93 @@ static void *dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
 		}
 		break;
 	}
+}
+
+#define DWC2_USB_DMA_ALIGN 4
+
+struct dma_aligned_buffer {
+	void *kmalloc_ptr;
+	void *old_xfer_buffer;
+	u8 data[0];
+};
+
+static void dwc2_free_dma_aligned_buffer(struct urb *urb)
+{
+	struct dma_aligned_buffer *temp;
+
+	if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER))
+		return;
+
+	temp = container_of(urb->transfer_buffer,
+		struct dma_aligned_buffer, data);
 
-	return bufptr;
+	if (usb_urb_dir_in(urb))
+		memcpy(temp->old_xfer_buffer, temp->data,
+		       urb->transfer_buffer_length);
+	urb->transfer_buffer = temp->old_xfer_buffer;
+	kfree(temp->kmalloc_ptr);
+
+	urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER;
 }
 
-static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
-				   struct dwc2_host_chan *chan,
-				   struct dwc2_hcd_urb *urb, void *bufptr)
+static int dwc2_alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags)
 {
-	u32 buf_size;
-	struct urb *usb_urb;
-	struct usb_hcd *hcd;
+	struct dma_aligned_buffer *temp, *kmalloc_ptr;
+	size_t kmalloc_size;
 
-	if (!qh->dw_align_buf) {
-		if (chan->ep_type != USB_ENDPOINT_XFER_ISOC)
-			buf_size = hsotg->core_params->max_transfer_size;
-		else
-			/* 3072 = 3 max-size Isoc packets */
-			buf_size = 3072;
+	if (urb->num_sgs || urb->sg ||
+	    urb->transfer_buffer_length == 0 ||
+	    !((uintptr_t)urb->transfer_buffer & (DWC2_USB_DMA_ALIGN - 1)))
+		return 0;
 
-		qh->dw_align_buf = kmalloc(buf_size, GFP_ATOMIC | GFP_DMA);
-		if (!qh->dw_align_buf)
-			return -ENOMEM;
-		qh->dw_align_buf_size = buf_size;
-	}
+	/* Allocate a buffer with enough padding for alignment */
+	kmalloc_size = urb->transfer_buffer_length +
+		sizeof(struct dma_aligned_buffer) + DWC2_USB_DMA_ALIGN - 1;
 
-	if (chan->xfer_len) {
-		dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
-		usb_urb = urb->priv;
+	kmalloc_ptr = kmalloc(kmalloc_size, mem_flags);
+	if (!kmalloc_ptr)
+		return -ENOMEM;
 
-		if (usb_urb) {
-			if (usb_urb->transfer_flags &
-			    (URB_SETUP_MAP_SINGLE | URB_DMA_MAP_SG |
-			     URB_DMA_MAP_PAGE | URB_DMA_MAP_SINGLE)) {
-				hcd = dwc2_hsotg_to_hcd(hsotg);
-				usb_hcd_unmap_urb_for_dma(hcd, usb_urb);
-			}
-			if (!chan->ep_is_in)
-				memcpy(qh->dw_align_buf, bufptr,
-				       chan->xfer_len);
-		} else {
-			dev_warn(hsotg->dev, "no URB in dwc2_urb\n");
-		}
-	}
+	/* Position our struct dma_aligned_buffer such that data is aligned */
+	temp = PTR_ALIGN(kmalloc_ptr + 1, DWC2_USB_DMA_ALIGN) - 1;
+	temp->kmalloc_ptr = kmalloc_ptr;
+	temp->old_xfer_buffer = urb->transfer_buffer;
+	if (usb_urb_dir_out(urb))
+		memcpy(temp->data, urb->transfer_buffer,
+		       urb->transfer_buffer_length);
+	urb->transfer_buffer = temp->data;
 
-	qh->dw_align_buf_dma = dma_map_single(hsotg->dev,
-			qh->dw_align_buf, qh->dw_align_buf_size,
-			chan->ep_is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
-	if (dma_mapping_error(hsotg->dev, qh->dw_align_buf_dma)) {
-		dev_err(hsotg->dev, "can't map align_buf\n");
-		chan->align_buf = 0;
-		return -EINVAL;
-	}
+	urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER;
 
-	chan->align_buf = qh->dw_align_buf_dma;
 	return 0;
 }
 
+static int dwc2_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
+				      gfp_t mem_flags)
+{
+	int ret;
+
+	/* We assume setup_dma is always aligned; warn if not */
+	WARN_ON_ONCE(urb->setup_dma &&
+		     (urb->setup_dma & (DWC2_USB_DMA_ALIGN - 1)));
+
+	ret = dwc2_alloc_dma_aligned_buffer(urb, mem_flags);
+	if (ret)
+		return ret;
+
+	ret = usb_hcd_map_urb_for_dma(hcd, urb, mem_flags);
+	if (ret)
+		dwc2_free_dma_aligned_buffer(urb);
+
+	return ret;
+}
+
+static void dwc2_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
+{
+	usb_hcd_unmap_urb_for_dma(hcd, urb);
+	dwc2_free_dma_aligned_buffer(urb);
+}
+
 /**
  * dwc2_assign_and_init_hc() - Assigns transactions from a QTD to a free host
  * channel and initializes the host channel to perform the transactions. The
@@ -804,7 +824,6 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 	struct dwc2_host_chan *chan;
 	struct dwc2_hcd_urb *urb;
 	struct dwc2_qtd *qtd;
-	void *bufptr = NULL;
 
 	if (dbg_qh(qh))
 		dev_vdbg(hsotg->dev, "%s(%p,%p)\n", __func__, hsotg, qh);
@@ -866,16 +885,10 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		!dwc2_hcd_is_pipe_in(&urb->pipe_info))
 		urb->actual_length = urb->length;
 
-	if (hsotg->core_params->dma_enable > 0) {
+	if (hsotg->core_params->dma_enable > 0)
 		chan->xfer_dma = urb->dma + urb->actual_length;
-
-		/* For non-dword aligned case */
-		if (hsotg->core_params->dma_desc_enable <= 0 &&
-		    (chan->xfer_dma & 0x3))
-			bufptr = (u8 *)urb->buf + urb->actual_length;
-	} else {
+	else
 		chan->xfer_buf = (u8 *)urb->buf + urb->actual_length;
-	}
 
 	chan->xfer_len = urb->length - urb->actual_length;
 	chan->xfer_count = 0;
@@ -887,27 +900,7 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		chan->do_split = 0;
 
 	/* Set the transfer attributes */
-	bufptr = dwc2_hc_init_xfer(hsotg, chan, qtd, bufptr);
-
-	/* Non DWORD-aligned buffer case */
-	if (bufptr) {
-		dev_vdbg(hsotg->dev, "Non-aligned buffer\n");
-		if (dwc2_hc_setup_align_buf(hsotg, qh, chan, urb, bufptr)) {
-			dev_err(hsotg->dev,
-				"%s: Failed to allocate memory to handle non-dword aligned buffer\n",
-				__func__);
-			/* Add channel back to free list */
-			chan->align_buf = 0;
-			chan->multi_count = 0;
-			list_add_tail(&chan->hc_list_entry,
-				      &hsotg->free_hc_list);
-			qtd->in_process = 0;
-			qh->channel = NULL;
-			return -ENOMEM;
-		}
-	} else {
-		chan->align_buf = 0;
-	}
+	dwc2_hc_init_xfer(hsotg, chan, qtd);
 
 	if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
 	    chan->ep_type == USB_ENDPOINT_XFER_ISOC)
@@ -2971,6 +2964,9 @@ static struct hc_driver dwc2_hc_driver = {
 
 	.bus_suspend = _dwc2_hcd_suspend,
 	.bus_resume = _dwc2_hcd_resume,
+
+	.map_urb_for_dma	= dwc2_map_urb_for_dma,
+	.unmap_urb_for_dma	= dwc2_unmap_urb_for_dma,
 };
 
 /*
diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index 8f0a29cefdf7..42f2e4e233da 100644
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -75,8 +75,6 @@ struct dwc2_qh;
  *                      (micro)frame
  * @xfer_buf:           Pointer to current transfer buffer position
  * @xfer_dma:           DMA address of xfer_buf
- * @align_buf:          In Buffer DMA mode this will be used if xfer_buf is not
- *                      DWORD aligned
  * @xfer_len:           Total number of bytes to transfer
  * @xfer_count:         Number of bytes transferred so far
  * @start_pkt_count:    Packet count at start of transfer
@@ -133,7 +131,6 @@ struct dwc2_host_chan {
 
 	u8 *xfer_buf;
 	dma_addr_t xfer_dma;
-	dma_addr_t align_buf;
 	u32 xfer_len;
 	u32 xfer_count;
 	u16 start_pkt_count;
@@ -243,10 +240,6 @@ enum dwc2_transaction_type {
  * @frame_usecs:        Internal variable used by the microframe scheduler
  * @start_split_frame:  (Micro)frame at which last start split was initialized
  * @ntd:                Actual number of transfer descriptors in a list
- * @dw_align_buf:       Used instead of original buffer if its physical address
- *                      is not dword-aligned
- * @dw_align_buf_size:  Size of dw_align_buf
- * @dw_align_buf_dma:   DMA address for dw_align_buf
  * @qtd_list:           List of QTDs for this QH
  * @channel:            Host channel currently processing transfers for this QH
  * @qh_list_entry:      Entry for QH in either the periodic or non-periodic
@@ -279,9 +272,6 @@ struct dwc2_qh {
 	u16 frame_usecs[8];
 	u16 start_split_frame;
 	u16 ntd;
-	u8 *dw_align_buf;
-	int dw_align_buf_size;
-	dma_addr_t dw_align_buf_dma;
 	struct list_head qtd_list;
 	struct dwc2_host_chan *channel;
 	struct list_head qh_list_entry;
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index f8253803a050..352c98364317 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -472,18 +472,6 @@ static int dwc2_update_urb_state(struct dwc2_hsotg *hsotg,
 		xfer_length = urb->length - urb->actual_length;
 	}
 
-	/* Non DWORD-aligned buffer case handling */
-	if (chan->align_buf && xfer_length) {
-		dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
-		dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
-				chan->qh->dw_align_buf_size,
-				chan->ep_is_in ?
-				DMA_FROM_DEVICE : DMA_TO_DEVICE);
-		if (chan->ep_is_in)
-			memcpy(urb->buf + urb->actual_length,
-					chan->qh->dw_align_buf, xfer_length);
-	}
-
 	dev_vdbg(hsotg->dev, "urb->actual_length=%d xfer_length=%d\n",
 		 urb->actual_length, xfer_length);
 	urb->actual_length += xfer_length;
@@ -565,21 +553,6 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state(
 		frame_desc->status = 0;
 		frame_desc->actual_length = dwc2_get_actual_xfer_length(hsotg,
 					chan, chnum, qtd, halt_status, NULL);
-
-		/* Non DWORD-aligned buffer case handling */
-		if (chan->align_buf && frame_desc->actual_length) {
-			dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n",
-				 __func__);
-			dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
-					chan->qh->dw_align_buf_size,
-					chan->ep_is_in ?
-					DMA_FROM_DEVICE : DMA_TO_DEVICE);
-			if (chan->ep_is_in)
-				memcpy(urb->buf + frame_desc->offset +
-					qtd->isoc_split_offset,
-					chan->qh->dw_align_buf,
-					frame_desc->actual_length);
-		}
 		break;
 	case DWC2_HC_XFER_FRAME_OVERRUN:
 		urb->error_count++;
@@ -600,21 +573,6 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state(
 		frame_desc->actual_length = dwc2_get_actual_xfer_length(hsotg,
 					chan, chnum, qtd, halt_status, NULL);
 
-		/* Non DWORD-aligned buffer case handling */
-		if (chan->align_buf && frame_desc->actual_length) {
-			dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n",
-				 __func__);
-			dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
-					chan->qh->dw_align_buf_size,
-					chan->ep_is_in ?
-					DMA_FROM_DEVICE : DMA_TO_DEVICE);
-			if (chan->ep_is_in)
-				memcpy(urb->buf + frame_desc->offset +
-					qtd->isoc_split_offset,
-					chan->qh->dw_align_buf,
-					frame_desc->actual_length);
-		}
-
 		/* Skip whole frame */
 		if (chan->qh->do_split &&
 		    chan->ep_type == USB_ENDPOINT_XFER_ISOC && chan->ep_is_in &&
@@ -680,8 +638,6 @@ static void dwc2_deactivate_qh(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 	}
 
 no_qtd:
-	if (qh->channel)
-		qh->channel->align_buf = 0;
 	qh->channel = NULL;
 	dwc2_hcd_qh_deactivate(hsotg, qh, continue_split);
 }
@@ -946,14 +902,6 @@ static int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg,
 
 	frame_desc->actual_length += len;
 
-	if (chan->align_buf) {
-		dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
-		dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
-				chan->qh->dw_align_buf_size, DMA_FROM_DEVICE);
-		memcpy(qtd->urb->buf + frame_desc->offset +
-		       qtd->isoc_split_offset, chan->qh->dw_align_buf, len);
-	}
-
 	qtd->isoc_split_offset += len;
 
 	if (frame_desc->actual_length >= frame_desc->length) {
@@ -1176,19 +1124,6 @@ static void dwc2_update_urb_state_abn(struct dwc2_hsotg *hsotg,
 		xfer_length = urb->length - urb->actual_length;
 	}
 
-	/* Non DWORD-aligned buffer case handling */
-	if (chan->align_buf && xfer_length && chan->ep_is_in) {
-		dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
-		dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
-				chan->qh->dw_align_buf_size,
-				chan->ep_is_in ?
-				DMA_FROM_DEVICE : DMA_TO_DEVICE);
-		if (chan->ep_is_in)
-			memcpy(urb->buf + urb->actual_length,
-					chan->qh->dw_align_buf,
-					xfer_length);
-	}
-
 	urb->actual_length += xfer_length;
 
 	hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum));
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 27d402f680a3..e0933a9dfad7 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -232,13 +232,8 @@ struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
  */
 void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
-	if (qh->desc_list) {
+	if (qh->desc_list)
 		dwc2_hcd_qh_free_ddma(hsotg, qh);
-	} else {
-		/* kfree(NULL) is safe */
-		kfree(qh->dw_align_buf);
-		qh->dw_align_buf_dma = (dma_addr_t)0;
-	}
 	kfree(qh);
 }
 
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Fri Jan 22 10:18:39 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Fri, 22 Jan 2016 10:18:39 -0800
Subject: [PATCH v5 04/21] usb: dwc2: host: Set host_perio_tx_fifo_size to 304
 for rk3066
In-Reply-To: <1453486736-15358-1-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
Message-ID: <1453486736-15358-5-git-send-email-dianders@chromium.org>

Looking at rk3288, there are two dwc2 ports.  One has 960
total_fifo_size and the other 972.

We're currently assigning 528 + 128 + 256 = 912.  That means we're
wasting 48 words on one port and 60 words on the other.  Since we have
one settings block for both ports, let's just eat the 48 words that we
know we have and assign them to what's probably the most
latency-senstive part of the system: periodic transfers.

Presumably other rk3288 parts using the rk3066 settings also have these
extra 48 words as well.

Note: ideally we'd adjust dwc2_calculate_dynamic_fifo() to handle this
more dynamically, but until that's done hardcoding things here seems OK.

NOTE: no known use cases are improved by this patch, but in my stressful
USB tests I certainly see my periodic FIFO filling up both before and
after this change.  Presumably there are some USB configurations where
the periodic FIFO would have been filled before where it no longer is.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v5: None
Changes in v4:
- Set host_perio_tx_fifo_size to 304 for rk3066 new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/platform.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
index b6d7666e715c..afdcdeee266d 100644
--- a/drivers/usb/dwc2/platform.c
+++ b/drivers/usb/dwc2/platform.c
@@ -128,7 +128,7 @@ static const struct dwc2_core_params params_rk3066 = {
 	.en_multiple_tx_fifo		= -1,
 	.host_rx_fifo_size		= 528,	/* 528 DWORDs */
 	.host_nperio_tx_fifo_size	= 128,	/* 128 DWORDs */
-	.host_perio_tx_fifo_size	= 256,	/* 256 DWORDs */
+	.host_perio_tx_fifo_size	= 304,	/* 304 DWORDs */
 	.max_transfer_size		= -1,
 	.max_packet_count		= -1,
 	.host_channels			= -1,
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Fri Jan 22 10:18:40 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Fri, 22 Jan 2016 10:18:40 -0800
Subject: [PATCH v5 05/21] usb: dwc2: host: Avoid use of chan->qh after qh freed
In-Reply-To: <1453486736-15358-1-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
Message-ID: <1453486736-15358-6-git-send-email-dianders@chromium.org>

When poking around with USB devices with slub_debug enabled, I found
another obvious use after free.  Turns out that in dwc2_hc_n_intr() I
was in a state when the contents of chan->qh was filled with 0x6b,
indicating that chan->qh was freed but chan still had a reference to
it.

Let's make sure that whenever we free qh we also make sure we remove a
reference from its channel.

The bug fixed here doesn't appear to be new--I believe I just got lucky
and happened to see it while stress testing.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v5: None
Changes in v4:
- Avoid use of chan->qh after qh freed new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/hcd.c      |  8 ++++++++
 drivers/usb/dwc2/hcd_intr.c | 10 ++++++++++
 2 files changed, 18 insertions(+)

diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index bc4bdbc1534e..7783c8ba0173 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -164,6 +164,9 @@ static void dwc2_qh_list_free(struct dwc2_hsotg *hsotg,
 					 qtd_list_entry)
 			dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh);
 
+		if (qh->channel && qh->channel->qh == qh)
+			qh->channel->qh = NULL;
+
 		spin_unlock_irqrestore(&hsotg->lock, flags);
 		dwc2_hcd_qh_free(hsotg, qh);
 		spin_lock_irqsave(&hsotg->lock, flags);
@@ -554,7 +557,12 @@ static int dwc2_hcd_endpoint_disable(struct dwc2_hsotg *hsotg,
 		dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh);
 
 	ep->hcpriv = NULL;
+
+	if (qh->channel && qh->channel->qh == qh)
+		qh->channel->qh = NULL;
+
 	spin_unlock_irqrestore(&hsotg->lock, flags);
+
 	dwc2_hcd_qh_free(hsotg, qh);
 
 	return 0;
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index 352c98364317..99efc2bd1617 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -1935,6 +1935,16 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum)
 	}
 
 	dwc2_writel(hcint, hsotg->regs + HCINT(chnum));
+
+	/*
+	 * If we got an interrupt after someone called
+	 * dwc2_hcd_endpoint_disable() we don't want to crash below
+	 */
+	if (!chan->qh) {
+		dev_warn(hsotg->dev, "Interrupt on disabled channel\n");
+		return;
+	}
+
 	chan->hcint = hcint;
 	hcint &= hcintmsk;
 
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Fri Jan 22 10:18:41 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Fri, 22 Jan 2016 10:18:41 -0800
Subject: [PATCH v5 06/21] usb: dwc2: host: Always add to the tail of queues
In-Reply-To: <1453486736-15358-1-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
Message-ID: <1453486736-15358-7-git-send-email-dianders@chromium.org>

The queues the the dwc2 host controller used are truly queues.  That
means FIFO or first in first out.

Unfortunately though the code was iterating through these queues
starting from the head, some places in the code was adding things to the
queue by adding at the head instead of the tail.  That means last in
first out.  Doh.

Go through and just always add to the tail.

Doing this makes things much happier when I've got:
* 7-port USB 2.0 Single-TT hub
* - Microsoft 2.4 GHz Transceiver v7.0 dongle
* - Jabra speakerphone playing music

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v5: None
Changes in v4:
- Always add to the tail of queues new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/hcd.c       | 11 ++++++-----
 drivers/usb/dwc2/hcd_ddma.c  |  4 ++--
 drivers/usb/dwc2/hcd_intr.c  |  4 ++--
 drivers/usb/dwc2/hcd_queue.c |  6 ++++--
 4 files changed, 14 insertions(+), 11 deletions(-)

diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 7783c8ba0173..d2daaea88d91 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -969,7 +969,8 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions(
 		 * periodic assigned schedule
 		 */
 		qh_ptr = qh_ptr->next;
-		list_move(&qh->qh_list_entry, &hsotg->periodic_sched_assigned);
+		list_move_tail(&qh->qh_list_entry,
+			       &hsotg->periodic_sched_assigned);
 		ret_val = DWC2_TRANSACTION_PERIODIC;
 	}
 
@@ -1002,8 +1003,8 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions(
 		 * non-periodic active schedule
 		 */
 		qh_ptr = qh_ptr->next;
-		list_move(&qh->qh_list_entry,
-			  &hsotg->non_periodic_sched_active);
+		list_move_tail(&qh->qh_list_entry,
+			       &hsotg->non_periodic_sched_active);
 
 		if (ret_val == DWC2_TRANSACTION_NONE)
 			ret_val = DWC2_TRANSACTION_NON_PERIODIC;
@@ -1176,8 +1177,8 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg)
 			 * Move the QH from the periodic assigned schedule to
 			 * the periodic queued schedule
 			 */
-			list_move(&qh->qh_list_entry,
-				  &hsotg->periodic_sched_queued);
+			list_move_tail(&qh->qh_list_entry,
+				       &hsotg->periodic_sched_queued);
 
 			/* done queuing high bandwidth */
 			hsotg->queuing_high_bandwidth = 0;
diff --git a/drivers/usb/dwc2/hcd_ddma.c b/drivers/usb/dwc2/hcd_ddma.c
index 36606fc33c0d..16b261cfa92d 100644
--- a/drivers/usb/dwc2/hcd_ddma.c
+++ b/drivers/usb/dwc2/hcd_ddma.c
@@ -1327,8 +1327,8 @@ void dwc2_hcd_complete_xfer_ddma(struct dwc2_hsotg *hsotg,
 			dwc2_hcd_qh_unlink(hsotg, qh);
 		} else {
 			/* Keep in assigned schedule to continue transfer */
-			list_move(&qh->qh_list_entry,
-				  &hsotg->periodic_sched_assigned);
+			list_move_tail(&qh->qh_list_entry,
+				       &hsotg->periodic_sched_assigned);
 			/*
 			 * If channel has been halted during giveback of urb
 			 * then prevent any new scheduling.
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index 99efc2bd1617..2c521c00e5e0 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -143,7 +143,7 @@ static void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
 			 * Move QH to the ready list to be executed next
 			 * (micro)frame
 			 */
-			list_move(&qh->qh_list_entry,
+			list_move_tail(&qh->qh_list_entry,
 				  &hsotg->periodic_sched_ready);
 	}
 	tr_type = dwc2_hcd_select_transactions(hsotg);
@@ -794,7 +794,7 @@ static void dwc2_halt_channel(struct dwc2_hsotg *hsotg,
 			 * halt to be queued when the periodic schedule is
 			 * processed.
 			 */
-			list_move(&chan->qh->qh_list_entry,
+			list_move_tail(&chan->qh->qh_list_entry,
 				  &hsotg->periodic_sched_assigned);
 
 			/*
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index e0933a9dfad7..bc632a72f611 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -732,9 +732,11 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 	     dwc2_frame_num_le(qh->sched_frame, frame_number)) ||
 	    (hsotg->core_params->uframe_sched <= 0 &&
 	     qh->sched_frame == frame_number))
-		list_move(&qh->qh_list_entry, &hsotg->periodic_sched_ready);
+		list_move_tail(&qh->qh_list_entry,
+			       &hsotg->periodic_sched_ready);
 	else
-		list_move(&qh->qh_list_entry, &hsotg->periodic_sched_inactive);
+		list_move_tail(&qh->qh_list_entry,
+			       &hsotg->periodic_sched_inactive);
 }
 
 /**
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Fri Jan 22 10:18:42 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Fri, 22 Jan 2016 10:18:42 -0800
Subject: [PATCH v5 07/21] usb: dwc2: hcd: fix split transfer schedule sequence
In-Reply-To: <1453486736-15358-1-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
Message-ID: <1453486736-15358-8-git-send-email-dianders@chromium.org>

We're supposed to keep outstanding splits in order.  Keep track of a
list of the order of splits and process channel interrupts in that
order.

Without this change and the following setup:
* Rockchip rk3288 Chromebook, using port ff540000
  -> Pluggable 7-port Hub with Charging (powered)
     -> Microsoft Wireless Keyboard 2000 in port 1.
     -> Das Keyboard in port 2.

...I find that I get dropped keys on the Microsoft keyboard (I'm sure
there are other combinations that fail, but this documents my test).
Specifically I've been typing "hahahahahahaha" on the keyboard and often
see keys dropped or repeated.

After this change the above setup works properly.  This patch is based
on a previous patch proposed by Yunzhi Li ("usb: dwc2: hcd: fix periodic
transfer schedule sequence")

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Signed-off-by: Yunzhi Li <lyz at rock-chips.com>
---
Changes in v5:
- Move list maintenance to hcd.c to avoid gadget-only compile error

Changes in v4:
- fix split transfer schedule sequence new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/core.c     |  2 ++
 drivers/usb/dwc2/core.h     |  2 ++
 drivers/usb/dwc2/hcd.c      |  8 ++++++++
 drivers/usb/dwc2/hcd.h      |  2 ++
 drivers/usb/dwc2/hcd_intr.c | 17 +++++++++++++++++
 5 files changed, 31 insertions(+)

diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index 73f2771b7740..ed73b26818c0 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -1676,6 +1676,8 @@ void dwc2_hc_cleanup(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan)
 
 	chan->xfer_started = 0;
 
+	list_del_init(&chan->split_order_list_entry);
+
 	/*
 	 * Clear channel interrupt enables and any unhandled channel interrupt
 	 * conditions
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 7fb6434f4639..538cf38af0e4 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -657,6 +657,7 @@ struct dwc2_hregs_backup {
  *                      periodic_sched_ready because it must be rescheduled for
  *                      the next frame. Otherwise, the item moves to
  *                      periodic_sched_inactive.
+ * @split_order:        List keeping track of channels doing splits, in order.
  * @periodic_usecs:     Total bandwidth claimed so far for periodic transfers.
  *                      This value is in microseconds per (micro)frame. The
  *                      assumption is that all periodic transfers may occur in
@@ -780,6 +781,7 @@ struct dwc2_hsotg {
 	struct list_head periodic_sched_ready;
 	struct list_head periodic_sched_assigned;
 	struct list_head periodic_sched_queued;
+	struct list_head split_order;
 	u16 periodic_usecs;
 	u16 frame_usecs[8];
 	u16 frame_number;
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index d2daaea88d91..87ad5bf2d166 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -1045,6 +1045,11 @@ static int dwc2_queue_transaction(struct dwc2_hsotg *hsotg,
 {
 	int retval = 0;
 
+	if (chan->do_split)
+		/* Put ourselves on the list to keep order straight */
+		list_move_tail(&chan->split_order_list_entry,
+			       &hsotg->split_order);
+
 	if (hsotg->core_params->dma_enable > 0) {
 		if (hsotg->core_params->dma_desc_enable > 0) {
 			if (!chan->xfer_started ||
@@ -3151,6 +3156,8 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
 	INIT_LIST_HEAD(&hsotg->periodic_sched_assigned);
 	INIT_LIST_HEAD(&hsotg->periodic_sched_queued);
 
+	INIT_LIST_HEAD(&hsotg->split_order);
+
 	/*
 	 * Create a host channel descriptor for each host channel implemented
 	 * in the controller. Initialize the channel descriptor array.
@@ -3164,6 +3171,7 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
 		if (channel == NULL)
 			goto error3;
 		channel->hc_num = i;
+		INIT_LIST_HEAD(&channel->split_order_list_entry);
 		hsotg->hc_ptr_array[i] = channel;
 	}
 
diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index 42f2e4e233da..1b46e2e617cc 100644
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -106,6 +106,7 @@ struct dwc2_qh;
  * @hc_list_entry:      For linking to list of host channels
  * @desc_list_addr:     Current QH's descriptor list DMA address
  * @desc_list_sz:       Current QH's descriptor list size
+ * @split_order_list_entry: List entry for keeping track of the order of splits
  *
  * This structure represents the state of a single host channel when acting in
  * host mode. It contains the data items needed to transfer packets to an
@@ -158,6 +159,7 @@ struct dwc2_host_chan {
 	struct list_head hc_list_entry;
 	dma_addr_t desc_list_addr;
 	u32 desc_list_sz;
+	struct list_head split_order_list_entry;
 };
 
 struct dwc2_hcd_pipe_info {
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index 2c521c00e5e0..577c91096a51 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -2067,6 +2067,7 @@ static void dwc2_hc_intr(struct dwc2_hsotg *hsotg)
 {
 	u32 haint;
 	int i;
+	struct dwc2_host_chan *chan, *chan_tmp;
 
 	haint = dwc2_readl(hsotg->regs + HAINT);
 	if (dbg_perio()) {
@@ -2075,6 +2076,22 @@ static void dwc2_hc_intr(struct dwc2_hsotg *hsotg)
 		dev_vdbg(hsotg->dev, "HAINT=%08x\n", haint);
 	}
 
+	/*
+	 * According to USB 2.0 spec section 11.18.8, a host must
+	 * issue complete-split transactions in a microframe for a
+	 * set of full-/low-speed endpoints in the same relative
+	 * order as the start-splits were issued in a microframe for.
+	 */
+	list_for_each_entry_safe(chan, chan_tmp, &hsotg->split_order,
+				 split_order_list_entry) {
+		int hc_num = chan->hc_num;
+
+		if (haint & (1 << hc_num)) {
+			dwc2_hc_n_intr(hsotg, hc_num);
+			haint &= ~(1 << hc_num);
+		}
+	}
+
 	for (i = 0; i < hsotg->core_params->host_channels; i++) {
 		if (haint & (1 << i))
 			dwc2_hc_n_intr(hsotg, i);
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Fri Jan 22 10:18:43 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Fri, 22 Jan 2016 10:18:43 -0800
Subject: [PATCH v5 08/21] usb: dwc2: host: Add scheduler tracing
In-Reply-To: <1453486736-15358-1-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
Message-ID: <1453486736-15358-9-git-send-email-dianders@chromium.org>

In preparation for future changes to the scheduler let's add some
tracing that makes it easy for us to see what's happening.  By default
this tracing will be off.

By changing "core.h" you can easily trace to ftrace, the console, or
nowhere.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v5: None
Changes in v4:
- Retooled scheduler tracing a bit, so left off John's Ack from v3.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/core.h      | 20 ++++++++++++++++++++
 drivers/usb/dwc2/hcd.h       |  5 +++++
 drivers/usb/dwc2/hcd_intr.c  |  6 +++++-
 drivers/usb/dwc2/hcd_queue.c | 24 +++++++++++++++++++++++-
 4 files changed, 53 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 538cf38af0e4..18f9e4045643 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -44,6 +44,26 @@
 #include <linux/usb/phy.h>
 #include "hw.h"
 
+/*
+ * Suggested defines for tracers:
+ * - no_printk:    Disable tracing
+ * - pr_info:      Print this info to the console
+ * - trace_printk: Print this info to trace buffer (good for verbose logging)
+ */
+
+#define DWC2_TRACE_SCHEDULER		no_printk
+#define DWC2_TRACE_SCHEDULER_VB		no_printk
+
+/* Detailed scheduler tracing, but won't overwhelm console */
+#define dwc2_sch_dbg(hsotg, fmt, ...)					\
+	DWC2_TRACE_SCHEDULER(pr_fmt("%s: SCH: " fmt),			\
+			     dev_name(hsotg->dev), ##__VA_ARGS__)
+
+/* Verbose scheduler tracing */
+#define dwc2_sch_vdbg(hsotg, fmt, ...)					\
+	DWC2_TRACE_SCHEDULER_VB(pr_fmt("%s: SCH: " fmt),		\
+				dev_name(hsotg->dev), ##__VA_ARGS__)
+
 static inline u32 dwc2_readl(const void __iomem *addr)
 {
 	u32 value = __raw_readl(addr);
diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index 1b46e2e617cc..809bc4ff9116 100644
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -563,6 +563,11 @@ static inline u16 dwc2_frame_num_inc(u16 frame, u16 inc)
 	return (frame + inc) & HFNUM_MAX_FRNUM;
 }
 
+static inline u16 dwc2_frame_num_dec(u16 frame, u16 dec)
+{
+	return (frame + HFNUM_MAX_FRNUM + 1 - dec) & HFNUM_MAX_FRNUM;
+}
+
 static inline u16 dwc2_full_frame_num(u16 frame)
 {
 	return (frame & HFNUM_MAX_FRNUM) >> 3;
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index 577c91096a51..5d25a5ec9736 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -138,13 +138,17 @@ static void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
 	while (qh_entry != &hsotg->periodic_sched_inactive) {
 		qh = list_entry(qh_entry, struct dwc2_qh, qh_list_entry);
 		qh_entry = qh_entry->next;
-		if (dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number))
+		if (dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number)) {
+			dwc2_sch_vdbg(hsotg, "QH=%p ready fn=%04x, sch=%04x\n",
+				      qh, hsotg->frame_number, qh->sched_frame);
+
 			/*
 			 * Move QH to the ready list to be executed next
 			 * (micro)frame
 			 */
 			list_move_tail(&qh->qh_list_entry,
 				  &hsotg->periodic_sched_ready);
+		}
 	}
 	tr_type = dwc2_hcd_select_transactions(hsotg);
 	if (tr_type != DWC2_TRANSACTION_NONE)
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index bc632a72f611..0e9faa75593c 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -113,6 +113,9 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 		qh->sched_frame = dwc2_frame_num_inc(hsotg->frame_number,
 						     SCHEDULE_SLOP);
 		qh->interval = urb->interval;
+		dwc2_sch_dbg(hsotg, "QH=%p init sch=%04x, fn=%04x, int=%#x\n",
+			     qh, qh->sched_frame, hsotg->frame_number,
+			     qh->interval);
 #if 0
 		/* Increase interrupt polling rate for debugging */
 		if (qh->ep_type == USB_ENDPOINT_XFER_INT)
@@ -126,6 +129,11 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 			qh->interval *= 8;
 			qh->sched_frame |= 0x7;
 			qh->start_split_frame = qh->sched_frame;
+			dwc2_sch_dbg(hsotg,
+				     "QH=%p init*8 sch=%04x, fn=%04x, int=%#x\n",
+				     qh, qh->sched_frame, hsotg->frame_number,
+				     qh->interval);
+
 		}
 		dev_dbg(hsotg->dev, "interval=%d\n", qh->interval);
 	}
@@ -482,6 +490,8 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		if (frame >= 0) {
 			qh->sched_frame &= ~0x7;
 			qh->sched_frame |= (frame & 7);
+			dwc2_sch_dbg(hsotg, "QH=%p sched_p sch=%04x, uf=%d\n",
+				     qh, qh->sched_frame, frame);
 		}
 
 		if (status > 0)
@@ -583,10 +593,16 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 
 	if (!dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number) &&
 			!hsotg->frame_number) {
+		u16 new_frame;
+
 		dev_dbg(hsotg->dev,
 				"reset frame number counter\n");
-		qh->sched_frame = dwc2_frame_num_inc(hsotg->frame_number,
+		new_frame = dwc2_frame_num_inc(hsotg->frame_number,
 				SCHEDULE_SLOP);
+
+		dwc2_sch_vdbg(hsotg, "QH=%p reset sch=%04x=>%04x\n",
+			      qh, qh->sched_frame, new_frame);
+		qh->sched_frame = new_frame;
 	}
 
 	/* Add the new QH to the appropriate schedule */
@@ -652,6 +668,7 @@ static void dwc2_sched_periodic_split(struct dwc2_hsotg *hsotg,
 				      int sched_next_periodic_split)
 {
 	u16 incr;
+	u16 old_frame = qh->sched_frame;
 
 	if (sched_next_periodic_split) {
 		qh->sched_frame = frame_number;
@@ -677,6 +694,11 @@ static void dwc2_sched_periodic_split(struct dwc2_hsotg *hsotg,
 		qh->sched_frame |= 0x7;
 		qh->start_split_frame = qh->sched_frame;
 	}
+
+	dwc2_sch_vdbg(hsotg, "QH=%p next(%d) fn=%04x, sch=%04x=>%04x (%+d)\n",
+		      qh, sched_next_periodic_split, frame_number, old_frame,
+		      qh->sched_frame,
+		      dwc2_frame_num_dec(qh->sched_frame, old_frame));
 }
 
 /*
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Fri Jan 22 10:18:45 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Fri, 22 Jan 2016 10:18:45 -0800
Subject: [PATCH v5 10/21] usb: dwc2: host: Giveback URB in tasklet context
In-Reply-To: <1453486736-15358-1-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
Message-ID: <1453486736-15358-11-git-send-email-dianders@chromium.org>

In commit 94dfd7edfd5c ("USB: HCD: support giveback of URB in tasklet
context") support was added to give back the URB in tasklet context.
Let's take advantage of this in dwc2.

This speeds up the dwc2 interrupt handler considerably.

Note that this requires the change ("usb: dwc2: host: Add a delay before
releasing periodic bandwidth") to come first.

Note that, as per Alan Stern in
<https://patchwork.kernel.org/patch/7555771/>, we also need to make sure
that the extra delay before the device drivers submit more data doesn't
break the scheduler.  At the moment the scheduler is pretty broken (see
future patches) so it's hard to be 100% certain, but I have yet to see
any new breakage introduced by this delay.  ...and speeding up interrupt
processing for dwc2 is a huge deal because it means we've got a better
chance of not missing SOF interrupts.  That means we've got an overall
win here.

Note that when playing USB audio and using a USB webcam and having
several USB keyboards plugged in, the crackling on the USB audio device
is noticably reduced with this patch.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Tested-by: Heiko Stuebner <heiko at sntech.de>
---
Changes in v5: None
Changes in v4:
- A bit earlier in the list of patches than in v3.

Changes in v3: None
Changes in v2:
- Commit message now says that URB giveback change needs delay change.

 drivers/usb/dwc2/hcd.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 87ad5bf2d166..9fd14b74b49c 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -2341,9 +2341,7 @@ void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
 	kfree(qtd->urb);
 	qtd->urb = NULL;
 
-	spin_unlock(&hsotg->lock);
 	usb_hcd_giveback_urb(dwc2_hsotg_to_hcd(hsotg), urb, status);
-	spin_lock(&hsotg->lock);
 }
 
 /*
@@ -2962,7 +2960,7 @@ static struct hc_driver dwc2_hc_driver = {
 	.hcd_priv_size = sizeof(struct wrapper_priv_data),
 
 	.irq = _dwc2_hcd_irq,
-	.flags = HCD_MEMORY | HCD_USB2,
+	.flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
 
 	.start = _dwc2_hcd_start,
 	.stop = _dwc2_hcd_stop,
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Fri Jan 22 10:18:44 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Fri, 22 Jan 2016 10:18:44 -0800
Subject: [PATCH v5 09/21] usb: dwc2: host: Add a delay before releasing
 periodic bandwidth
In-Reply-To: <1453486736-15358-1-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
Message-ID: <1453486736-15358-10-git-send-email-dianders@chromium.org>

We'd like to be able to use HCD_BH in order to speed up the dwc2 host
interrupt handler quite a bit.  However, according to the kernel doc for
usb_submit_urb() (specifically the part about "Reserved Bandwidth
Transfers"), we need to keep a reservation active as long as a device
driver keeps submitting.  That was easy to do when we gave back the URB
in the interrupt context: we just looked at when our queue was empty and
released the reserved bandwidth then.  ...but now we need a little more
complexity.

We'll follow EHCI's lead in commit 9118f9eb4f1e ("USB: EHCI: improve
interrupt qh unlink") and add a 5ms delay.  Since we don't have a whole
timer infrastructure in dwc2, we'll just add a timer per QH.  The
overhead for this is very small.

Note that the dwc2 scheduler is pretty broken (see future patches to fix
it).  This patch attempts to replicate all old behavior and just add the
proper delay.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v5: None
Changes in v4:
- Moved periodic bandwidth release delay patch earlier again.

Changes in v3:
- Moved periodic bandwidth release delay patch later in the series.

Changes in v2:
- Periodic bandwidth release delay new for V2

 drivers/usb/dwc2/hcd.h       |   6 ++
 drivers/usb/dwc2/hcd_queue.c | 237 +++++++++++++++++++++++++++++++++----------
 2 files changed, 187 insertions(+), 56 deletions(-)

diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index 809bc4ff9116..79473ea35bd6 100644
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -215,6 +215,7 @@ enum dwc2_transaction_type {
 /**
  * struct dwc2_qh - Software queue head structure
  *
+ * @hsotg:              The HCD state structure for the DWC OTG controller
  * @ep_type:            Endpoint type. One of the following values:
  *                       - USB_ENDPOINT_XFER_CONTROL
  *                       - USB_ENDPOINT_XFER_BULK
@@ -252,13 +253,16 @@ enum dwc2_transaction_type {
  * @n_bytes:            Xfer Bytes array. Each element corresponds to a transfer
  *                      descriptor and indicates original XferSize value for the
  *                      descriptor
+ * @unreserve_timer:    Timer for releasing periodic reservation.
  * @tt_buffer_dirty     True if clear_tt_buffer_complete is pending
+ * @unreserve_pending:  True if we planned to unreserve but haven't yet.
  *
  * A Queue Head (QH) holds the static characteristics of an endpoint and
  * maintains a list of transfers (QTDs) for that endpoint. A QH structure may
  * be entered in either the non-periodic or periodic schedule.
  */
 struct dwc2_qh {
+	struct dwc2_hsotg *hsotg;
 	u8 ep_type;
 	u8 ep_is_in;
 	u16 maxp;
@@ -281,7 +285,9 @@ struct dwc2_qh {
 	dma_addr_t desc_list_dma;
 	u32 desc_list_sz;
 	u32 *n_bytes;
+	struct timer_list unreserve_timer;
 	unsigned tt_buffer_dirty:1;
+	unsigned unreserve_pending:1;
 };
 
 /**
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 0e9faa75593c..b9e4867e1afd 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -53,6 +53,94 @@
 #include "core.h"
 #include "hcd.h"
 
+/* Wait this long before releasing periodic reservation */
+#define DWC2_UNRESERVE_DELAY (msecs_to_jiffies(5))
+
+/**
+ * dwc2_do_unreserve() - Actually release the periodic reservation
+ *
+ * This function actually releases the periodic bandwidth that was reserved
+ * by the given qh.
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller
+ * @qh:    QH for the periodic transfer.
+ */
+static void dwc2_do_unreserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+	assert_spin_locked(&hsotg->lock);
+
+	WARN_ON(!qh->unreserve_pending);
+
+	/* No more unreserve pending--we're doing it */
+	qh->unreserve_pending = false;
+
+	if (WARN_ON(!list_empty(&qh->qh_list_entry)))
+		list_del_init(&qh->qh_list_entry);
+
+	/* Update claimed usecs per (micro)frame */
+	hsotg->periodic_usecs -= qh->usecs;
+
+	if (hsotg->core_params->uframe_sched > 0) {
+		int i;
+
+		for (i = 0; i < 8; i++) {
+			hsotg->frame_usecs[i] += qh->frame_usecs[i];
+			qh->frame_usecs[i] = 0;
+		}
+	} else {
+		/* Release periodic channel reservation */
+		hsotg->periodic_channels--;
+	}
+}
+
+/**
+ * dwc2_unreserve_timer_fn() - Timer function to release periodic reservation
+ *
+ * According to the kernel doc for usb_submit_urb() (specifically the part about
+ * "Reserved Bandwidth Transfers"), we need to keep a reservation active as
+ * long as a device driver keeps submitting.  Since we're using HCD_BH to give
+ * back the URB we need to give the driver a little bit of time before we
+ * release the reservation.  This worker is called after the appropriate
+ * delay.
+ *
+ * @work: Pointer to a qh unreserve_work.
+ */
+static void dwc2_unreserve_timer_fn(unsigned long data)
+{
+	struct dwc2_qh *qh = (struct dwc2_qh *)data;
+	struct dwc2_hsotg *hsotg = qh->hsotg;
+	unsigned long flags;
+
+	/*
+	 * Wait for the lock, or for us to be scheduled again.  We
+	 * could be scheduled again if:
+	 * - We started executing but didn't get the lock yet.
+	 * - A new reservation came in, but cancel didn't take effect
+	 *   because we already started executing.
+	 * - The timer has been kicked again.
+	 * In that case cancel and wait for the next call.
+	 */
+	while (!spin_trylock_irqsave(&hsotg->lock, flags)) {
+		if (timer_pending(&qh->unreserve_timer))
+			return;
+	}
+
+	/*
+	 * Might be no more unreserve pending if:
+	 * - We started executing but didn't get the lock yet.
+	 * - A new reservation came in, but cancel didn't take effect
+	 *   because we already started executing.
+	 *
+	 * We can't put this in the loop above because unreserve_pending needs
+	 * to be accessed under lock, so we can only check it once we got the
+	 * lock.
+	 */
+	if (qh->unreserve_pending)
+		dwc2_do_unreserve(hsotg, qh);
+
+	spin_unlock_irqrestore(&hsotg->lock, flags);
+}
+
 /**
  * dwc2_qh_init() - Initializes a QH structure
  *
@@ -71,6 +159,9 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 	dev_vdbg(hsotg->dev, "%s()\n", __func__);
 
 	/* Initialize QH */
+	qh->hsotg = hsotg;
+	setup_timer(&qh->unreserve_timer, dwc2_unreserve_timer_fn,
+		    (unsigned long)qh);
 	qh->ep_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
 	qh->ep_is_in = dwc2_hcd_is_pipe_in(&urb->pipe_info) ? 1 : 0;
 
@@ -240,6 +331,15 @@ struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
  */
 void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
+	/* Make sure any unreserve work is finished. */
+	if (del_timer_sync(&qh->unreserve_timer)) {
+		unsigned long flags;
+
+		spin_lock_irqsave(&hsotg->lock, flags);
+		dwc2_do_unreserve(hsotg, qh);
+		spin_unlock_irqrestore(&hsotg->lock, flags);
+	}
+
 	if (qh->desc_list)
 		dwc2_hcd_qh_free_ddma(hsotg, qh);
 	kfree(qh);
@@ -477,51 +577,74 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
 	int status;
 
-	if (hsotg->core_params->uframe_sched > 0) {
-		int frame = -1;
-
-		status = dwc2_find_uframe(hsotg, qh);
-		if (status == 0)
-			frame = 7;
-		else if (status > 0)
-			frame = status - 1;
-
-		/* Set the new frame up */
-		if (frame >= 0) {
-			qh->sched_frame &= ~0x7;
-			qh->sched_frame |= (frame & 7);
-			dwc2_sch_dbg(hsotg, "QH=%p sched_p sch=%04x, uf=%d\n",
-				     qh, qh->sched_frame, frame);
+	status = dwc2_check_max_xfer_size(hsotg, qh);
+	if (status) {
+		dev_dbg(hsotg->dev,
+			"%s: Channel max transfer size too small for periodic transfer\n",
+			__func__);
+		return status;
+	}
+
+	/* Cancel pending unreserve; if canceled OK, unreserve was pending */
+	if (del_timer(&qh->unreserve_timer))
+		WARN_ON(!qh->unreserve_pending);
+
+	/*
+	 * Only need to reserve if there's not an unreserve pending, since if an
+	 * unreserve is pending then by definition our old reservation is still
+	 * valid.  Unreserve might still be pending even if we didn't cancel if
+	 * dwc2_unreserve_timer_fn() already started.  Code in the timer handles
+	 * that case.
+	 */
+	if (!qh->unreserve_pending) {
+		if (hsotg->core_params->uframe_sched > 0) {
+			int frame = -1;
+
+			status = dwc2_find_uframe(hsotg, qh);
+			if (status == 0)
+				frame = 7;
+			else if (status > 0)
+				frame = status - 1;
+
+			/* Set the new frame up */
+			if (frame >= 0) {
+				qh->sched_frame &= ~0x7;
+				qh->sched_frame |= (frame & 7);
+				dwc2_sch_dbg(hsotg,
+					     "QH=%p sched_p sch=%04x, uf=%d\n",
+					     qh, qh->sched_frame, frame);
+			}
+
+			if (status > 0)
+				status = 0;
+		} else {
+			status = dwc2_periodic_channel_available(hsotg);
+			if (status) {
+				dev_info(hsotg->dev,
+					"%s: No host channel available for periodic transfer\n",
+					__func__);
+				return status;
+			}
+
+			status = dwc2_check_periodic_bandwidth(hsotg, qh);
 		}
 
-		if (status > 0)
-			status = 0;
-	} else {
-		status = dwc2_periodic_channel_available(hsotg);
 		if (status) {
-			dev_info(hsotg->dev,
-				 "%s: No host channel available for periodic transfer\n",
-				 __func__);
+			dev_dbg(hsotg->dev,
+				"%s: Insufficient periodic bandwidth for periodic transfer\n",
+				__func__);
 			return status;
 		}
 
-		status = dwc2_check_periodic_bandwidth(hsotg, qh);
-	}
+		if (hsotg->core_params->uframe_sched <= 0)
+			/* Reserve periodic channel */
+			hsotg->periodic_channels++;
 
-	if (status) {
-		dev_dbg(hsotg->dev,
-			"%s: Insufficient periodic bandwidth for periodic transfer\n",
-			__func__);
-		return status;
+		/* Update claimed usecs per (micro)frame */
+		hsotg->periodic_usecs += qh->usecs;
 	}
 
-	status = dwc2_check_max_xfer_size(hsotg, qh);
-	if (status) {
-		dev_dbg(hsotg->dev,
-			"%s: Channel max transfer size too small for periodic transfer\n",
-			__func__);
-		return status;
-	}
+	qh->unreserve_pending = 0;
 
 	if (hsotg->core_params->dma_desc_enable > 0)
 		/* Don't rely on SOF and start in ready schedule */
@@ -531,13 +654,6 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		list_add_tail(&qh->qh_list_entry,
 			      &hsotg->periodic_sched_inactive);
 
-	if (hsotg->core_params->uframe_sched <= 0)
-		/* Reserve periodic channel */
-		hsotg->periodic_channels++;
-
-	/* Update claimed usecs per (micro)frame */
-	hsotg->periodic_usecs += qh->usecs;
-
 	return status;
 }
 
@@ -551,22 +667,31 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 static void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg,
 				     struct dwc2_qh *qh)
 {
-	int i;
+	bool did_modify;
 
-	list_del_init(&qh->qh_list_entry);
+	assert_spin_locked(&hsotg->lock);
 
-	/* Update claimed usecs per (micro)frame */
-	hsotg->periodic_usecs -= qh->usecs;
+	/*
+	 * Schedule the unreserve to happen in a little bit.  Cases here:
+	 * - Unreserve worker might be sitting there waiting to grab the lock.
+	 *   In this case it will notice it's been schedule again and will
+	 *   quit.
+	 * - Unreserve worker might not be scheduled.
+	 *
+	 * We should never already be scheduled since dwc2_schedule_periodic()
+	 * should have canceled the scheduled unreserve timer (hence the
+	 * warning on did_modify).
+	 *
+	 * We add + 1 to the timer to guarantee that at least 1 jiffy has
+	 * passed (otherwise if the jiffy counter might tick right after we
+	 * read it and we'll get no delay).
+	 */
+	did_modify = mod_timer(&qh->unreserve_timer,
+			       jiffies + DWC2_UNRESERVE_DELAY + 1);
+	WARN_ON(did_modify);
+	qh->unreserve_pending = 1;
 
-	if (hsotg->core_params->uframe_sched > 0) {
-		for (i = 0; i < 8; i++) {
-			hsotg->frame_usecs[i] += qh->frame_usecs[i];
-			qh->frame_usecs[i] = 0;
-		}
-	} else {
-		/* Release periodic channel reservation */
-		hsotg->periodic_channels--;
-	}
+	list_del_init(&qh->qh_list_entry);
 }
 
 /**
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Fri Jan 22 10:18:46 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Fri, 22 Jan 2016 10:18:46 -0800
Subject: [PATCH v5 11/21] usb: dwc2: host: Use periodic interrupt even with DMA
In-Reply-To: <1453486736-15358-1-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
Message-ID: <1453486736-15358-12-git-send-email-dianders@chromium.org>

The old code in dwc2_process_periodic_channels() would only enable the
"periodic empty" interrupt if we weren't using DMA.  That wasn't right
since we can still get into cases where we have small FIFOs even on
systems that have DMA (the rk3288 is a prime example).

Let's always enable/disable the "periodic empty" when appropriate.  As
part of this:

* Always call dwc2_process_periodic_channels() even if there's nothing
  in periodic_sched_assigned (we move the queue empty check so we still
  avoid the extra work).  That will make extra certain that we will
  properly disable the "periodic empty" interrupt even if there's
  nothing queued up.

* Move the enable of "periodic empty" due to non-empty
  periodic_sched_assigned to be for slave mode (non-DMA mode) only.
  Presumably this was the original intention of the check for DMA since
  it seems to match the comments above where in slave mode we leave
  things on the assigned queue.

Note that even before this change slave mode didn't work for me, so I
can't say for sure that my understanding of slave mode is correct.
However, this shouldn't change anything for slave mode so if slave mode
worked for someone in the past it ought to still work.

With this change, I no longer get constant misses reported by my other
debugging code (and with future patches) when I've got:
* Rockchip rk3288 Chromebook, using port ff540000
  -> Pluggable 7-port Hub with Charging (powered)
     -> Microsoft Wireless Keyboard 2000 in port 1.
     -> Das Keyboard in port 2.
     -> Jabra Speaker in port 3
     -> Logitech, Inc. Webcam C600 in port 4
     -> Microsoft Sidewinder X6 Keyboard in port 5

...and I'm playing music on the USB speaker and capturing video from the
webcam.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v5: None
Changes in v4:
- Use periodic interrupt even with DMA new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/hcd.c | 71 +++++++++++++++++++++++---------------------------
 1 file changed, 32 insertions(+), 39 deletions(-)

diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 9fd14b74b49c..584c64114a82 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -1109,10 +1109,14 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg)
 	u32 fspcavail;
 	u32 gintmsk;
 	int status;
-	int no_queue_space = 0;
-	int no_fifo_space = 0;
+	bool no_queue_space = false;
+	bool no_fifo_space = false;
 	u32 qspcavail;
 
+	/* If empty list then just adjust interrupt enables */
+	if (list_empty(&hsotg->periodic_sched_assigned))
+		goto exit;
+
 	if (dbg_perio())
 		dev_vdbg(hsotg->dev, "Queue periodic transactions\n");
 
@@ -1190,42 +1194,32 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg)
 		}
 	}
 
-	if (hsotg->core_params->dma_enable <= 0) {
-		tx_status = dwc2_readl(hsotg->regs + HPTXSTS);
-		qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >>
-			    TXSTS_QSPCAVAIL_SHIFT;
-		fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >>
-			    TXSTS_FSPCAVAIL_SHIFT;
-		if (dbg_perio()) {
-			dev_vdbg(hsotg->dev,
-				 "  P Tx Req Queue Space Avail (after queue): %d\n",
-				 qspcavail);
-			dev_vdbg(hsotg->dev,
-				 "  P Tx FIFO Space Avail (after queue): %d\n",
-				 fspcavail);
-		}
-
-		if (!list_empty(&hsotg->periodic_sched_assigned) ||
-		    no_queue_space || no_fifo_space) {
-			/*
-			 * May need to queue more transactions as the request
-			 * queue or Tx FIFO empties. Enable the periodic Tx
-			 * FIFO empty interrupt. (Always use the half-empty
-			 * level to ensure that new requests are loaded as
-			 * soon as possible.)
-			 */
-			gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+exit:
+	if (no_queue_space || no_fifo_space ||
+	    (hsotg->core_params->dma_enable <= 0 &&
+	     !list_empty(&hsotg->periodic_sched_assigned))) {
+		/*
+		 * May need to queue more transactions as the request
+		 * queue or Tx FIFO empties. Enable the periodic Tx
+		 * FIFO empty interrupt. (Always use the half-empty
+		 * level to ensure that new requests are loaded as
+		 * soon as possible.)
+		 */
+		gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+		if (!(gintmsk & GINTSTS_PTXFEMP)) {
 			gintmsk |= GINTSTS_PTXFEMP;
 			dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
-		} else {
-			/*
-			 * Disable the Tx FIFO empty interrupt since there are
-			 * no more transactions that need to be queued right
-			 * now. This function is called from interrupt
-			 * handlers to queue more transactions as transfer
-			 * states change.
-			 */
-			gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+		}
+	} else {
+		/*
+		 * Disable the Tx FIFO empty interrupt since there are
+		 * no more transactions that need to be queued right
+		 * now. This function is called from interrupt
+		 * handlers to queue more transactions as transfer
+		 * states change.
+		*/
+		gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+		if (gintmsk & GINTSTS_PTXFEMP) {
 			gintmsk &= ~GINTSTS_PTXFEMP;
 			dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
 		}
@@ -1372,9 +1366,8 @@ void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg,
 	dev_vdbg(hsotg->dev, "Queue Transactions\n");
 #endif
 	/* Process host channels associated with periodic transfers */
-	if ((tr_type == DWC2_TRANSACTION_PERIODIC ||
-	     tr_type == DWC2_TRANSACTION_ALL) &&
-	    !list_empty(&hsotg->periodic_sched_assigned))
+	if (tr_type == DWC2_TRANSACTION_PERIODIC ||
+	    tr_type == DWC2_TRANSACTION_ALL)
 		dwc2_process_periodic_channels(hsotg);
 
 	/* Process host channels associated with non-periodic transfers */
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Fri Jan 22 10:18:47 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Fri, 22 Jan 2016 10:18:47 -0800
Subject: [PATCH v5 12/21] usb: dwc2: host: Rename some fields in struct dwc2_qh
In-Reply-To: <1453486736-15358-1-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
Message-ID: <1453486736-15358-13-git-send-email-dianders@chromium.org>

This no-op change just does some renames to simplify a future patch.

1. The "interval" field is renamed to "host_interval" to make it more
   obvious that this interval may be 8 times the interval that the
   device sees (if we're doing split transactions).  A future patch will
   also add the "device_interval" field.
2. The "usecs" field is renamed to "host_us" again to make it more
   obvious that this is the time for the transaction as seen by the
   host.  For split transactions the device may see a much longer
   transaction time.  A future patch will also add "device_us".
3. The "sched_frame" field is renamed to "next_active_frame".  The name
   "sched_frame" kept confusing me because it felt like something more
   permament (the QH's reservation or something).  The name
   "next_active_frame" makes it more obvious that this field is
   constantly changing.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v5: None
Changes in v4:
- Rename some fields in struct dwc2_qh new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/hcd.h       |  20 ++++----
 drivers/usb/dwc2/hcd_ddma.c  |  37 ++++++++-------
 drivers/usb/dwc2/hcd_intr.c  |  10 ++--
 drivers/usb/dwc2/hcd_queue.c | 107 ++++++++++++++++++++++---------------------
 4 files changed, 92 insertions(+), 82 deletions(-)

diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index 79473ea35bd6..10c35585a2bd 100644
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -236,10 +236,14 @@ enum dwc2_transaction_type {
  * @do_split:           Full/low speed endpoint on high-speed hub requires split
  * @td_first:           Index of first activated isochronous transfer descriptor
  * @td_last:            Index of last activated isochronous transfer descriptor
- * @usecs:              Bandwidth in microseconds per (micro)frame
- * @interval:           Interval between transfers in (micro)frames
- * @sched_frame:        (Micro)frame to initialize a periodic transfer.
- *                      The transfer executes in the following (micro)frame.
+ * @host_us:            Bandwidth in microseconds per transfer as seen by host
+ * @host_interval:      Interval between transfers as seen by the host.  If
+ *                      the host is high speed and the device is low speed this
+ *                      will be 8 times device interval.
+ * @next_active_frame:  (Micro)frame before we next need to put something on
+ *                      the bus.  We'll move the qh to active here.  If the
+ *                      host is in high speed mode this will be a uframe.  If
+ *                      the host is in low speed mode this will be a full frame.
  * @frame_usecs:        Internal variable used by the microframe scheduler
  * @start_split_frame:  (Micro)frame at which last start split was initialized
  * @ntd:                Actual number of transfer descriptors in a list
@@ -272,9 +276,9 @@ struct dwc2_qh {
 	u8 do_split;
 	u8 td_first;
 	u8 td_last;
-	u16 usecs;
-	u16 interval;
-	u16 sched_frame;
+	u16 host_us;
+	u16 host_interval;
+	u16 next_active_frame;
 	u16 frame_usecs[8];
 	u16 start_split_frame;
 	u16 ntd;
@@ -651,7 +655,7 @@ static inline u16 dwc2_hcd_get_ep_bandwidth(struct dwc2_hsotg *hsotg,
 		return 0;
 	}
 
-	return qh->usecs;
+	return qh->host_us;
 }
 
 extern void dwc2_hcd_save_data_toggle(struct dwc2_hsotg *hsotg,
diff --git a/drivers/usb/dwc2/hcd_ddma.c b/drivers/usb/dwc2/hcd_ddma.c
index 16b261cfa92d..26b00270ca0b 100644
--- a/drivers/usb/dwc2/hcd_ddma.c
+++ b/drivers/usb/dwc2/hcd_ddma.c
@@ -81,7 +81,7 @@ static u16 dwc2_max_desc_num(struct dwc2_qh *qh)
 static u16 dwc2_frame_incr_val(struct dwc2_qh *qh)
 {
 	return qh->dev_speed == USB_SPEED_HIGH ?
-	       (qh->interval + 8 - 1) / 8 : qh->interval;
+	       (qh->host_interval + 8 - 1) / 8 : qh->host_interval;
 }
 
 static int dwc2_desc_list_alloc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
@@ -252,7 +252,7 @@ static void dwc2_update_frame_list(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 	chan = qh->channel;
 	inc = dwc2_frame_incr_val(qh);
 	if (qh->ep_type == USB_ENDPOINT_XFER_ISOC)
-		i = dwc2_frame_list_idx(qh->sched_frame);
+		i = dwc2_frame_list_idx(qh->next_active_frame);
 	else
 		i = 0;
 
@@ -278,13 +278,13 @@ static void dwc2_update_frame_list(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 		return;
 
 	chan->schinfo = 0;
-	if (chan->speed == USB_SPEED_HIGH && qh->interval) {
+	if (chan->speed == USB_SPEED_HIGH && qh->host_interval) {
 		j = 1;
 		/* TODO - check this */
-		inc = (8 + qh->interval - 1) / qh->interval;
+		inc = (8 + qh->host_interval - 1) / qh->host_interval;
 		for (i = 0; i < inc; i++) {
 			chan->schinfo |= j;
-			j = j << qh->interval;
+			j = j << qh->host_interval;
 		}
 	} else {
 		chan->schinfo = 0xff;
@@ -431,7 +431,10 @@ static u16 dwc2_calc_starting_frame(struct dwc2_hsotg *hsotg,
 
 	hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
 
-	/* sched_frame is always frame number (not uFrame) both in FS and HS! */
+	/*
+	 * next_active_frame is always frame number (not uFrame) both in FS
+	 * and HS!
+	 */
 
 	/*
 	 * skip_frames is used to limit activated descriptors number
@@ -514,13 +517,13 @@ static u16 dwc2_recalc_initial_desc_idx(struct dwc2_hsotg *hsotg,
 		 */
 		fr_idx_tmp = dwc2_frame_list_idx(frame);
 		fr_idx = (FRLISTEN_64_SIZE +
-			  dwc2_frame_list_idx(qh->sched_frame) - fr_idx_tmp)
-			 % dwc2_frame_incr_val(qh);
+			  dwc2_frame_list_idx(qh->next_active_frame) -
+			  fr_idx_tmp) % dwc2_frame_incr_val(qh);
 		fr_idx = (fr_idx + fr_idx_tmp) % FRLISTEN_64_SIZE;
 	} else {
-		qh->sched_frame = dwc2_calc_starting_frame(hsotg, qh,
+		qh->next_active_frame = dwc2_calc_starting_frame(hsotg, qh,
 							   &skip_frames);
-		fr_idx = dwc2_frame_list_idx(qh->sched_frame);
+		fr_idx = dwc2_frame_list_idx(qh->next_active_frame);
 	}
 
 	qh->td_first = qh->td_last = dwc2_frame_to_desc_idx(qh, fr_idx);
@@ -583,7 +586,7 @@ static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
 	u16 next_idx;
 
 	idx = qh->td_last;
-	inc = qh->interval;
+	inc = qh->host_interval;
 	hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
 	cur_idx = dwc2_frame_list_idx(hsotg->frame_number);
 	next_idx = dwc2_desclist_idx_inc(qh->td_last, inc, qh->dev_speed);
@@ -605,11 +608,11 @@ static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
 		}
 	}
 
-	if (qh->interval) {
-		ntd_max = (dwc2_max_desc_num(qh) + qh->interval - 1) /
-				qh->interval;
+	if (qh->host_interval) {
+		ntd_max = (dwc2_max_desc_num(qh) + qh->host_interval - 1) /
+				qh->host_interval;
 		if (skip_frames && !qh->channel)
-			ntd_max -= skip_frames / qh->interval;
+			ntd_max -= skip_frames / qh->host_interval;
 	}
 
 	max_xfer_size = qh->dev_speed == USB_SPEED_HIGH ?
@@ -1029,7 +1032,7 @@ static void dwc2_complete_isoc_xfer_ddma(struct dwc2_hsotg *hsotg,
 							  idx);
 			if (rc < 0)
 				return;
-			idx = dwc2_desclist_idx_inc(idx, qh->interval,
+			idx = dwc2_desclist_idx_inc(idx, qh->host_interval,
 						    chan->speed);
 			if (!rc)
 				continue;
@@ -1039,7 +1042,7 @@ static void dwc2_complete_isoc_xfer_ddma(struct dwc2_hsotg *hsotg,
 
 			/* rc == DWC2_CMPL_STOP */
 
-			if (qh->interval >= 32)
+			if (qh->host_interval >= 32)
 				goto stop_scan;
 
 			qh->td_first = idx;
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index 5d25a5ec9736..b54a0c41d34f 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -138,9 +138,11 @@ static void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
 	while (qh_entry != &hsotg->periodic_sched_inactive) {
 		qh = list_entry(qh_entry, struct dwc2_qh, qh_list_entry);
 		qh_entry = qh_entry->next;
-		if (dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number)) {
-			dwc2_sch_vdbg(hsotg, "QH=%p ready fn=%04x, sch=%04x\n",
-				      qh, hsotg->frame_number, qh->sched_frame);
+		if (dwc2_frame_num_le(qh->next_active_frame,
+				      hsotg->frame_number)) {
+			dwc2_sch_vdbg(hsotg, "QH=%p ready fn=%04x, nxt=%04x\n",
+				      qh, hsotg->frame_number,
+				      qh->next_active_frame);
 
 			/*
 			 * Move QH to the ready list to be executed next
@@ -1350,7 +1352,7 @@ static void dwc2_hc_nyet_intr(struct dwc2_hsotg *hsotg,
 			int frnum = dwc2_hcd_get_frame_number(hsotg);
 
 			if (dwc2_full_frame_num(frnum) !=
-			    dwc2_full_frame_num(chan->qh->sched_frame)) {
+			    dwc2_full_frame_num(chan->qh->next_active_frame)) {
 				/*
 				 * No longer in the same full speed frame.
 				 * Treat this as a transaction error.
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index b9e4867e1afd..39f4de6279f8 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -78,7 +78,7 @@ static void dwc2_do_unreserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		list_del_init(&qh->qh_list_entry);
 
 	/* Update claimed usecs per (micro)frame */
-	hsotg->periodic_usecs -= qh->usecs;
+	hsotg->periodic_usecs -= qh->host_us;
 
 	if (hsotg->core_params->uframe_sched > 0) {
 		int i;
@@ -193,40 +193,40 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 		int bytecount =
 			dwc2_hb_mult(qh->maxp) * dwc2_max_packet(qh->maxp);
 
-		qh->usecs = NS_TO_US(usb_calc_bus_time(qh->do_split ?
-				USB_SPEED_HIGH : dev_speed, qh->ep_is_in,
-				qh->ep_type == USB_ENDPOINT_XFER_ISOC,
-				bytecount));
+		qh->host_us = NS_TO_US(usb_calc_bus_time(qh->do_split ?
+			      USB_SPEED_HIGH : dev_speed, qh->ep_is_in,
+			      qh->ep_type == USB_ENDPOINT_XFER_ISOC,
+			      bytecount));
 
 		/* Ensure frame_number corresponds to the reality */
 		hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
 		/* Start in a slightly future (micro)frame */
-		qh->sched_frame = dwc2_frame_num_inc(hsotg->frame_number,
+		qh->next_active_frame = dwc2_frame_num_inc(hsotg->frame_number,
 						     SCHEDULE_SLOP);
-		qh->interval = urb->interval;
-		dwc2_sch_dbg(hsotg, "QH=%p init sch=%04x, fn=%04x, int=%#x\n",
-			     qh, qh->sched_frame, hsotg->frame_number,
-			     qh->interval);
+		qh->host_interval = urb->interval;
+		dwc2_sch_dbg(hsotg, "QH=%p init nxt=%04x, fn=%04x, int=%#x\n",
+			     qh, qh->next_active_frame, hsotg->frame_number,
+			     qh->host_interval);
 #if 0
 		/* Increase interrupt polling rate for debugging */
 		if (qh->ep_type == USB_ENDPOINT_XFER_INT)
-			qh->interval = 8;
+			qh->host_interval = 8;
 #endif
 		hprt = dwc2_readl(hsotg->regs + HPRT0);
 		prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
 		if (prtspd == HPRT0_SPD_HIGH_SPEED &&
 		    (dev_speed == USB_SPEED_LOW ||
 		     dev_speed == USB_SPEED_FULL)) {
-			qh->interval *= 8;
-			qh->sched_frame |= 0x7;
-			qh->start_split_frame = qh->sched_frame;
+			qh->host_interval *= 8;
+			qh->next_active_frame |= 0x7;
+			qh->start_split_frame = qh->next_active_frame;
 			dwc2_sch_dbg(hsotg,
-				     "QH=%p init*8 sch=%04x, fn=%04x, int=%#x\n",
-				     qh, qh->sched_frame, hsotg->frame_number,
-				     qh->interval);
+				     "QH=%p init*8 nxt=%04x, fn=%04x, int=%#x\n",
+				     qh, qh->next_active_frame,
+				     hsotg->frame_number, qh->host_interval);
 
 		}
-		dev_dbg(hsotg->dev, "interval=%d\n", qh->interval);
+		dev_dbg(hsotg->dev, "interval=%d\n", qh->host_interval);
 	}
 
 	dev_vdbg(hsotg->dev, "DWC OTG HCD QH Initialized\n");
@@ -277,9 +277,9 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 
 	if (qh->ep_type == USB_ENDPOINT_XFER_INT) {
 		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - usecs = %d\n",
-			 qh->usecs);
+			 qh->host_us);
 		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - interval = %d\n",
-			 qh->interval);
+			 qh->host_interval);
 	}
 }
 
@@ -404,19 +404,19 @@ static int dwc2_check_periodic_bandwidth(struct dwc2_hsotg *hsotg,
 		 * High speed mode
 		 * Max periodic usecs is 80% x 125 usec = 100 usec
 		 */
-		max_claimed_usecs = 100 - qh->usecs;
+		max_claimed_usecs = 100 - qh->host_us;
 	} else {
 		/*
 		 * Full speed mode
 		 * Max periodic usecs is 90% x 1000 usec = 900 usec
 		 */
-		max_claimed_usecs = 900 - qh->usecs;
+		max_claimed_usecs = 900 - qh->host_us;
 	}
 
 	if (hsotg->periodic_usecs > max_claimed_usecs) {
 		dev_err(hsotg->dev,
 			"%s: already claimed usecs %d, required usecs %d\n",
-			__func__, hsotg->periodic_usecs, qh->usecs);
+			__func__, hsotg->periodic_usecs, qh->host_us);
 		status = -ENOSPC;
 	}
 
@@ -443,7 +443,7 @@ void dwc2_hcd_init_usecs(struct dwc2_hsotg *hsotg)
 
 static int dwc2_find_single_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
-	unsigned short utime = qh->usecs;
+	unsigned short utime = qh->host_us;
 	int i;
 
 	for (i = 0; i < 8; i++) {
@@ -462,7 +462,7 @@ static int dwc2_find_single_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
  */
 static int dwc2_find_multi_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
-	unsigned short utime = qh->usecs;
+	unsigned short utime = qh->host_us;
 	unsigned short xtime;
 	int t_left;
 	int i;
@@ -608,11 +608,11 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 
 			/* Set the new frame up */
 			if (frame >= 0) {
-				qh->sched_frame &= ~0x7;
-				qh->sched_frame |= (frame & 7);
+				qh->next_active_frame &= ~0x7;
+				qh->next_active_frame |= (frame & 7);
 				dwc2_sch_dbg(hsotg,
-					     "QH=%p sched_p sch=%04x, uf=%d\n",
-					     qh, qh->sched_frame, frame);
+					     "QH=%p sched_p nxt=%04x, uf=%d\n",
+					     qh, qh->next_active_frame, frame);
 			}
 
 			if (status > 0)
@@ -641,7 +641,7 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 			hsotg->periodic_channels++;
 
 		/* Update claimed usecs per (micro)frame */
-		hsotg->periodic_usecs += qh->usecs;
+		hsotg->periodic_usecs += qh->host_us;
 	}
 
 	qh->unreserve_pending = 0;
@@ -716,7 +716,7 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		/* QH already in a schedule */
 		return 0;
 
-	if (!dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number) &&
+	if (!dwc2_frame_num_le(qh->next_active_frame, hsotg->frame_number) &&
 			!hsotg->frame_number) {
 		u16 new_frame;
 
@@ -725,9 +725,9 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		new_frame = dwc2_frame_num_inc(hsotg->frame_number,
 				SCHEDULE_SLOP);
 
-		dwc2_sch_vdbg(hsotg, "QH=%p reset sch=%04x=>%04x\n",
-			      qh, qh->sched_frame, new_frame);
-		qh->sched_frame = new_frame;
+		dwc2_sch_vdbg(hsotg, "QH=%p reset nxt=%04x=>%04x\n",
+			      qh, qh->next_active_frame, new_frame);
+		qh->next_active_frame = new_frame;
 	}
 
 	/* Add the new QH to the appropriate schedule */
@@ -793,10 +793,10 @@ static void dwc2_sched_periodic_split(struct dwc2_hsotg *hsotg,
 				      int sched_next_periodic_split)
 {
 	u16 incr;
-	u16 old_frame = qh->sched_frame;
+	u16 old_frame = qh->next_active_frame;
 
 	if (sched_next_periodic_split) {
-		qh->sched_frame = frame_number;
+		qh->next_active_frame = frame_number;
 		incr = dwc2_frame_num_inc(qh->start_split_frame, 1);
 		if (dwc2_frame_num_le(frame_number, incr)) {
 			/*
@@ -807,23 +807,24 @@ static void dwc2_sched_periodic_split(struct dwc2_hsotg *hsotg,
 			 */
 			if (qh->ep_type != USB_ENDPOINT_XFER_ISOC ||
 			    qh->ep_is_in != 0) {
-				qh->sched_frame =
-					dwc2_frame_num_inc(qh->sched_frame, 1);
+				qh->next_active_frame = dwc2_frame_num_inc(
+					qh->next_active_frame, 1);
 			}
 		}
 	} else {
-		qh->sched_frame = dwc2_frame_num_inc(qh->start_split_frame,
-						     qh->interval);
-		if (dwc2_frame_num_le(qh->sched_frame, frame_number))
-			qh->sched_frame = frame_number;
-		qh->sched_frame |= 0x7;
-		qh->start_split_frame = qh->sched_frame;
+		qh->next_active_frame =
+			dwc2_frame_num_inc(qh->start_split_frame,
+					   qh->host_interval);
+		if (dwc2_frame_num_le(qh->next_active_frame, frame_number))
+			qh->next_active_frame = frame_number;
+		qh->next_active_frame |= 0x7;
+		qh->start_split_frame = qh->next_active_frame;
 	}
 
-	dwc2_sch_vdbg(hsotg, "QH=%p next(%d) fn=%04x, sch=%04x=>%04x (%+d)\n",
+	dwc2_sch_vdbg(hsotg, "QH=%p next(%d) fn=%04x, nxt=%04x=>%04x (%+d)\n",
 		      qh, sched_next_periodic_split, frame_number, old_frame,
-		      qh->sched_frame,
-		      dwc2_frame_num_dec(qh->sched_frame, old_frame));
+		      qh->next_active_frame,
+		      dwc2_frame_num_dec(qh->next_active_frame, old_frame));
 }
 
 /*
@@ -861,10 +862,10 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 		dwc2_sched_periodic_split(hsotg, qh, frame_number,
 					  sched_next_periodic_split);
 	} else {
-		qh->sched_frame = dwc2_frame_num_inc(qh->sched_frame,
-						     qh->interval);
-		if (dwc2_frame_num_le(qh->sched_frame, frame_number))
-			qh->sched_frame = frame_number;
+		qh->next_active_frame = dwc2_frame_num_inc(
+			qh->next_active_frame, qh->host_interval);
+		if (dwc2_frame_num_le(qh->next_active_frame, frame_number))
+			qh->next_active_frame = frame_number;
 	}
 
 	if (list_empty(&qh->qtd_list)) {
@@ -876,9 +877,9 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 	 * appropriate queue
 	 */
 	if ((hsotg->core_params->uframe_sched > 0 &&
-	     dwc2_frame_num_le(qh->sched_frame, frame_number)) ||
+	     dwc2_frame_num_le(qh->next_active_frame, frame_number)) ||
 	    (hsotg->core_params->uframe_sched <= 0 &&
-	     qh->sched_frame == frame_number))
+	     qh->next_active_frame == frame_number))
 		list_move_tail(&qh->qh_list_entry,
 			       &hsotg->periodic_sched_ready);
 	else
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Fri Jan 22 10:18:49 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Fri, 22 Jan 2016 10:18:49 -0800
Subject: [PATCH v5 14/21] usb: dwc2: host: Split code out to make
 dwc2_do_reserve()
In-Reply-To: <1453486736-15358-1-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
Message-ID: <1453486736-15358-15-git-send-email-dianders@chromium.org>

This no-op change splits code out of dwc2_schedule_periodic() into a
dwc2_do_reserve() function.  This makes it a little easier to follow the
logic.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v5: None
Changes in v4:
- Split code out to make dwc2_do_reserve() new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/hcd_queue.c | 112 ++++++++++++++++++++++++++-----------------
 1 file changed, 67 insertions(+), 45 deletions(-)

diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 8a2067bc1e62..9ce407e5017d 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -245,6 +245,70 @@ static int dwc2_find_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 }
 
 /**
+ * dwc2_do_reserve() - Make a periodic reservation
+ *
+ * Try to allocate space in the periodic schedule.  Depending on parameters
+ * this might use the microframe scheduler or the dumb scheduler.
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller
+ * @qh:    QH for the periodic transfer.
+ *
+ * Returns: 0 upon success; error upon failure.
+ */
+static int dwc2_do_reserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+	int status;
+
+	if (hsotg->core_params->uframe_sched > 0) {
+		int frame = -1;
+
+		status = dwc2_find_uframe(hsotg, qh);
+		if (status == 0)
+			frame = 7;
+		else if (status > 0)
+			frame = status - 1;
+
+		/* Set the new frame up */
+		if (frame >= 0) {
+			qh->next_active_frame &= ~0x7;
+			qh->next_active_frame |= (frame & 7);
+			dwc2_sch_dbg(hsotg,
+				     "QH=%p sched_p nxt=%04x, uf=%d\n",
+				     qh, qh->next_active_frame, frame);
+		}
+
+		if (status > 0)
+			status = 0;
+	} else {
+		status = dwc2_periodic_channel_available(hsotg);
+		if (status) {
+			dev_info(hsotg->dev,
+				 "%s: No host channel available for periodic transfer\n",
+				 __func__);
+			return status;
+		}
+
+		status = dwc2_check_periodic_bandwidth(hsotg, qh);
+	}
+
+	if (status) {
+		dev_dbg(hsotg->dev,
+			"%s: Insufficient periodic bandwidth for periodic transfer\n",
+			__func__);
+		return status;
+	}
+
+	if (hsotg->core_params->uframe_sched <= 0)
+		/* Reserve periodic channel */
+		hsotg->periodic_channels++;
+
+	/* Update claimed usecs per (micro)frame */
+	hsotg->periodic_usecs += qh->host_us;
+
+	return 0;
+}
+
+/**
  * dwc2_do_unreserve() - Actually release the periodic reservation
  *
  * This function actually releases the periodic bandwidth that was reserved
@@ -393,51 +457,9 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 	 * that case.
 	 */
 	if (!qh->unreserve_pending) {
-		if (hsotg->core_params->uframe_sched > 0) {
-			int frame = -1;
-
-			status = dwc2_find_uframe(hsotg, qh);
-			if (status == 0)
-				frame = 7;
-			else if (status > 0)
-				frame = status - 1;
-
-			/* Set the new frame up */
-			if (frame >= 0) {
-				qh->next_active_frame &= ~0x7;
-				qh->next_active_frame |= (frame & 7);
-				dwc2_sch_dbg(hsotg,
-					     "QH=%p sched_p nxt=%04x, uf=%d\n",
-					     qh, qh->next_active_frame, frame);
-			}
-
-			if (status > 0)
-				status = 0;
-		} else {
-			status = dwc2_periodic_channel_available(hsotg);
-			if (status) {
-				dev_info(hsotg->dev,
-					"%s: No host channel available for periodic transfer\n",
-					__func__);
-				return status;
-			}
-
-			status = dwc2_check_periodic_bandwidth(hsotg, qh);
-		}
-
-		if (status) {
-			dev_dbg(hsotg->dev,
-				"%s: Insufficient periodic bandwidth for periodic transfer\n",
-				__func__);
+		status = dwc2_do_reserve(hsotg, qh);
+		if (status)
 			return status;
-		}
-
-		if (hsotg->core_params->uframe_sched <= 0)
-			/* Reserve periodic channel */
-			hsotg->periodic_channels++;
-
-		/* Update claimed usecs per (micro)frame */
-		hsotg->periodic_usecs += qh->host_us;
 	}
 
 	qh->unreserve_pending = 0;
@@ -450,7 +472,7 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		list_add_tail(&qh->qh_list_entry,
 			      &hsotg->periodic_sched_inactive);
 
-	return status;
+	return 0;
 }
 
 /**
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Fri Jan 22 10:18:50 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Fri, 22 Jan 2016 10:18:50 -0800
Subject: [PATCH v5 15/21] usb: dwc2: host: Add scheduler logging for missed
 SOFs
In-Reply-To: <1453486736-15358-1-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
Message-ID: <1453486736-15358-16-git-send-email-dianders@chromium.org>

We'll use the new "scheduler verbose debugging" macro to log missed
SOFs.  This is fast enough (assuming you configure it to use the ftrace
buffer) that we can do it without worrying about the speed hit.  The
overhead hit if the scheduler tracing is set to "no_printk" should be
near zero.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v5: None
Changes in v4:
- Add scheduler logging for missed SOFs new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/core.h     |  3 ++-
 drivers/usb/dwc2/hcd.c      |  2 +-
 drivers/usb/dwc2/hcd_intr.c | 12 ++++++++----
 3 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 18f9e4045643..64d45a2053bb 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -809,9 +809,10 @@ struct dwc2_hsotg {
 	bool bus_suspended;
 	bool new_connection;
 
+	u16 last_frame_num;
+
 #ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
 #define FRAME_NUM_ARRAY_SIZE 1000
-	u16 last_frame_num;
 	u16 *frame_num_array;
 	u16 *last_frame_num_array;
 	int frame_num_idx;
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 584c64114a82..03072d1fd44d 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -3082,8 +3082,8 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
 			FRAME_NUM_ARRAY_SIZE, GFP_KERNEL);
 	if (!hsotg->last_frame_num_array)
 		goto error1;
-	hsotg->last_frame_num = HFNUM_MAX_FRNUM;
 #endif
+	hsotg->last_frame_num = HFNUM_MAX_FRNUM;
 
 	/* Check if the bus driver or platform code has setup a dma_mask */
 	if (hsotg->core_params->dma_enable > 0 &&
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index b54a0c41d34f..1875e4590a3c 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -55,12 +55,16 @@
 /* This function is for debug only */
 static void dwc2_track_missed_sofs(struct dwc2_hsotg *hsotg)
 {
-#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
 	u16 curr_frame_number = hsotg->frame_number;
+	u16 expected = dwc2_frame_num_inc(hsotg->last_frame_num, 1);
+
+	if (expected != curr_frame_number)
+		dwc2_sch_vdbg(hsotg, "MISSED SOF %04x != %04x\n",
+			expected, curr_frame_number);
 
+#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
 	if (hsotg->frame_num_idx < FRAME_NUM_ARRAY_SIZE) {
-		if (((hsotg->last_frame_num + 1) & HFNUM_MAX_FRNUM) !=
-		    curr_frame_number) {
+		if (expected != curr_frame_number) {
 			hsotg->frame_num_array[hsotg->frame_num_idx] =
 					curr_frame_number;
 			hsotg->last_frame_num_array[hsotg->frame_num_idx] =
@@ -79,8 +83,8 @@ static void dwc2_track_missed_sofs(struct dwc2_hsotg *hsotg)
 		}
 		hsotg->dumped_frame_num_array = 1;
 	}
-	hsotg->last_frame_num = curr_frame_number;
 #endif
+	hsotg->last_frame_num = curr_frame_number;
 }
 
 static void dwc2_hc_handle_tt_clear(struct dwc2_hsotg *hsotg,
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Fri Jan 22 10:18:48 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Fri, 22 Jan 2016 10:18:48 -0800
Subject: [PATCH v5 13/21] usb: dwc2: host: Reorder things in hcd_queue.c
In-Reply-To: <1453486736-15358-1-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
Message-ID: <1453486736-15358-14-git-send-email-dianders@chromium.org>

This no-op change just reorders a few functions in hcd_queue.c in order
to prepare for future changes.  Motivations here:

The functions dwc2_hcd_qh_free() and dwc2_hcd_qh_create() are exported
functions.  They are not called within the file.  That means that they
should be near the bottom so that they can easily call static helpers.

The function dwc2_qh_init() is only called by dwc2_hcd_qh_create() and
should move near the bottom with it.

The only reason that the dwc2_unreserve_timer_fn() timer function (and
its subroutine dwc2_do_unreserve()) were so high in the file was that
they needed to be above dwc2_qh_init().  Now that dwc2_qh_init() has
been moved down it can be moved down a bit.  A later patch will split
the reserve code out of dwc2_schedule_periodic() and the reserve
function should be near the unreserve function.  The reserve function
needs to be below dwc2_find_uframe() since it calls that.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v5: None
Changes in v4:
- Reorder things in hcd_queue.c new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/hcd_queue.c | 600 +++++++++++++++++++++----------------------
 1 file changed, 300 insertions(+), 300 deletions(-)

diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 39f4de6279f8..8a2067bc1e62 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -57,295 +57,6 @@
 #define DWC2_UNRESERVE_DELAY (msecs_to_jiffies(5))
 
 /**
- * dwc2_do_unreserve() - Actually release the periodic reservation
- *
- * This function actually releases the periodic bandwidth that was reserved
- * by the given qh.
- *
- * @hsotg: The HCD state structure for the DWC OTG controller
- * @qh:    QH for the periodic transfer.
- */
-static void dwc2_do_unreserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
-{
-	assert_spin_locked(&hsotg->lock);
-
-	WARN_ON(!qh->unreserve_pending);
-
-	/* No more unreserve pending--we're doing it */
-	qh->unreserve_pending = false;
-
-	if (WARN_ON(!list_empty(&qh->qh_list_entry)))
-		list_del_init(&qh->qh_list_entry);
-
-	/* Update claimed usecs per (micro)frame */
-	hsotg->periodic_usecs -= qh->host_us;
-
-	if (hsotg->core_params->uframe_sched > 0) {
-		int i;
-
-		for (i = 0; i < 8; i++) {
-			hsotg->frame_usecs[i] += qh->frame_usecs[i];
-			qh->frame_usecs[i] = 0;
-		}
-	} else {
-		/* Release periodic channel reservation */
-		hsotg->periodic_channels--;
-	}
-}
-
-/**
- * dwc2_unreserve_timer_fn() - Timer function to release periodic reservation
- *
- * According to the kernel doc for usb_submit_urb() (specifically the part about
- * "Reserved Bandwidth Transfers"), we need to keep a reservation active as
- * long as a device driver keeps submitting.  Since we're using HCD_BH to give
- * back the URB we need to give the driver a little bit of time before we
- * release the reservation.  This worker is called after the appropriate
- * delay.
- *
- * @work: Pointer to a qh unreserve_work.
- */
-static void dwc2_unreserve_timer_fn(unsigned long data)
-{
-	struct dwc2_qh *qh = (struct dwc2_qh *)data;
-	struct dwc2_hsotg *hsotg = qh->hsotg;
-	unsigned long flags;
-
-	/*
-	 * Wait for the lock, or for us to be scheduled again.  We
-	 * could be scheduled again if:
-	 * - We started executing but didn't get the lock yet.
-	 * - A new reservation came in, but cancel didn't take effect
-	 *   because we already started executing.
-	 * - The timer has been kicked again.
-	 * In that case cancel and wait for the next call.
-	 */
-	while (!spin_trylock_irqsave(&hsotg->lock, flags)) {
-		if (timer_pending(&qh->unreserve_timer))
-			return;
-	}
-
-	/*
-	 * Might be no more unreserve pending if:
-	 * - We started executing but didn't get the lock yet.
-	 * - A new reservation came in, but cancel didn't take effect
-	 *   because we already started executing.
-	 *
-	 * We can't put this in the loop above because unreserve_pending needs
-	 * to be accessed under lock, so we can only check it once we got the
-	 * lock.
-	 */
-	if (qh->unreserve_pending)
-		dwc2_do_unreserve(hsotg, qh);
-
-	spin_unlock_irqrestore(&hsotg->lock, flags);
-}
-
-/**
- * dwc2_qh_init() - Initializes a QH structure
- *
- * @hsotg: The HCD state structure for the DWC OTG controller
- * @qh:    The QH to init
- * @urb:   Holds the information about the device/endpoint needed to initialize
- *         the QH
- */
-#define SCHEDULE_SLOP 10
-static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
-			 struct dwc2_hcd_urb *urb)
-{
-	int dev_speed, hub_addr, hub_port;
-	char *speed, *type;
-
-	dev_vdbg(hsotg->dev, "%s()\n", __func__);
-
-	/* Initialize QH */
-	qh->hsotg = hsotg;
-	setup_timer(&qh->unreserve_timer, dwc2_unreserve_timer_fn,
-		    (unsigned long)qh);
-	qh->ep_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
-	qh->ep_is_in = dwc2_hcd_is_pipe_in(&urb->pipe_info) ? 1 : 0;
-
-	qh->data_toggle = DWC2_HC_PID_DATA0;
-	qh->maxp = dwc2_hcd_get_mps(&urb->pipe_info);
-	INIT_LIST_HEAD(&qh->qtd_list);
-	INIT_LIST_HEAD(&qh->qh_list_entry);
-
-	/* FS/LS Endpoint on HS Hub, NOT virtual root hub */
-	dev_speed = dwc2_host_get_speed(hsotg, urb->priv);
-
-	dwc2_host_hub_info(hsotg, urb->priv, &hub_addr, &hub_port);
-
-	if ((dev_speed == USB_SPEED_LOW || dev_speed == USB_SPEED_FULL) &&
-	    hub_addr != 0 && hub_addr != 1) {
-		dev_vdbg(hsotg->dev,
-			 "QH init: EP %d: TT found at hub addr %d, for port %d\n",
-			 dwc2_hcd_get_ep_num(&urb->pipe_info), hub_addr,
-			 hub_port);
-		qh->do_split = 1;
-	}
-
-	if (qh->ep_type == USB_ENDPOINT_XFER_INT ||
-	    qh->ep_type == USB_ENDPOINT_XFER_ISOC) {
-		/* Compute scheduling parameters once and save them */
-		u32 hprt, prtspd;
-
-		/* Todo: Account for split transfers in the bus time */
-		int bytecount =
-			dwc2_hb_mult(qh->maxp) * dwc2_max_packet(qh->maxp);
-
-		qh->host_us = NS_TO_US(usb_calc_bus_time(qh->do_split ?
-			      USB_SPEED_HIGH : dev_speed, qh->ep_is_in,
-			      qh->ep_type == USB_ENDPOINT_XFER_ISOC,
-			      bytecount));
-
-		/* Ensure frame_number corresponds to the reality */
-		hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
-		/* Start in a slightly future (micro)frame */
-		qh->next_active_frame = dwc2_frame_num_inc(hsotg->frame_number,
-						     SCHEDULE_SLOP);
-		qh->host_interval = urb->interval;
-		dwc2_sch_dbg(hsotg, "QH=%p init nxt=%04x, fn=%04x, int=%#x\n",
-			     qh, qh->next_active_frame, hsotg->frame_number,
-			     qh->host_interval);
-#if 0
-		/* Increase interrupt polling rate for debugging */
-		if (qh->ep_type == USB_ENDPOINT_XFER_INT)
-			qh->host_interval = 8;
-#endif
-		hprt = dwc2_readl(hsotg->regs + HPRT0);
-		prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
-		if (prtspd == HPRT0_SPD_HIGH_SPEED &&
-		    (dev_speed == USB_SPEED_LOW ||
-		     dev_speed == USB_SPEED_FULL)) {
-			qh->host_interval *= 8;
-			qh->next_active_frame |= 0x7;
-			qh->start_split_frame = qh->next_active_frame;
-			dwc2_sch_dbg(hsotg,
-				     "QH=%p init*8 nxt=%04x, fn=%04x, int=%#x\n",
-				     qh, qh->next_active_frame,
-				     hsotg->frame_number, qh->host_interval);
-
-		}
-		dev_dbg(hsotg->dev, "interval=%d\n", qh->host_interval);
-	}
-
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH Initialized\n");
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - qh = %p\n", qh);
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Device Address = %d\n",
-		 dwc2_hcd_get_dev_addr(&urb->pipe_info));
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Endpoint %d, %s\n",
-		 dwc2_hcd_get_ep_num(&urb->pipe_info),
-		 dwc2_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT");
-
-	qh->dev_speed = dev_speed;
-
-	switch (dev_speed) {
-	case USB_SPEED_LOW:
-		speed = "low";
-		break;
-	case USB_SPEED_FULL:
-		speed = "full";
-		break;
-	case USB_SPEED_HIGH:
-		speed = "high";
-		break;
-	default:
-		speed = "?";
-		break;
-	}
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Speed = %s\n", speed);
-
-	switch (qh->ep_type) {
-	case USB_ENDPOINT_XFER_ISOC:
-		type = "isochronous";
-		break;
-	case USB_ENDPOINT_XFER_INT:
-		type = "interrupt";
-		break;
-	case USB_ENDPOINT_XFER_CONTROL:
-		type = "control";
-		break;
-	case USB_ENDPOINT_XFER_BULK:
-		type = "bulk";
-		break;
-	default:
-		type = "?";
-		break;
-	}
-
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Type = %s\n", type);
-
-	if (qh->ep_type == USB_ENDPOINT_XFER_INT) {
-		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - usecs = %d\n",
-			 qh->host_us);
-		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - interval = %d\n",
-			 qh->host_interval);
-	}
-}
-
-/**
- * dwc2_hcd_qh_create() - Allocates and initializes a QH
- *
- * @hsotg:        The HCD state structure for the DWC OTG controller
- * @urb:          Holds the information about the device/endpoint needed
- *                to initialize the QH
- * @atomic_alloc: Flag to do atomic allocation if needed
- *
- * Return: Pointer to the newly allocated QH, or NULL on error
- */
-struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
-					  struct dwc2_hcd_urb *urb,
-					  gfp_t mem_flags)
-{
-	struct dwc2_qh *qh;
-
-	if (!urb->priv)
-		return NULL;
-
-	/* Allocate memory */
-	qh = kzalloc(sizeof(*qh), mem_flags);
-	if (!qh)
-		return NULL;
-
-	dwc2_qh_init(hsotg, qh, urb);
-
-	if (hsotg->core_params->dma_desc_enable > 0 &&
-	    dwc2_hcd_qh_init_ddma(hsotg, qh, mem_flags) < 0) {
-		dwc2_hcd_qh_free(hsotg, qh);
-		return NULL;
-	}
-
-	return qh;
-}
-
-/**
- * dwc2_hcd_qh_free() - Frees the QH
- *
- * @hsotg: HCD instance
- * @qh:    The QH to free
- *
- * QH should already be removed from the list. QTD list should already be empty
- * if called from URB Dequeue.
- *
- * Must NOT be called with interrupt disabled or spinlock held
- */
-void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
-{
-	/* Make sure any unreserve work is finished. */
-	if (del_timer_sync(&qh->unreserve_timer)) {
-		unsigned long flags;
-
-		spin_lock_irqsave(&hsotg->lock, flags);
-		dwc2_do_unreserve(hsotg, qh);
-		spin_unlock_irqrestore(&hsotg->lock, flags);
-	}
-
-	if (qh->desc_list)
-		dwc2_hcd_qh_free_ddma(hsotg, qh);
-	kfree(qh);
-}
-
-/**
  * dwc2_periodic_channel_available() - Checks that a channel is available for a
  * periodic transfer
  *
@@ -518,19 +229,104 @@ static int dwc2_find_multi_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 
 static int dwc2_find_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
-	int ret;
+	int ret;
+
+	if (qh->dev_speed == USB_SPEED_HIGH) {
+		/* if this is a hs transaction we need a full frame */
+		ret = dwc2_find_single_uframe(hsotg, qh);
+	} else {
+		/*
+		 * if this is a fs transaction we may need a sequence
+		 * of frames
+		 */
+		ret = dwc2_find_multi_uframe(hsotg, qh);
+	}
+	return ret;
+}
+
+/**
+ * dwc2_do_unreserve() - Actually release the periodic reservation
+ *
+ * This function actually releases the periodic bandwidth that was reserved
+ * by the given qh.
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller
+ * @qh:    QH for the periodic transfer.
+ */
+static void dwc2_do_unreserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+	assert_spin_locked(&hsotg->lock);
+
+	WARN_ON(!qh->unreserve_pending);
+
+	/* No more unreserve pending--we're doing it */
+	qh->unreserve_pending = false;
+
+	if (WARN_ON(!list_empty(&qh->qh_list_entry)))
+		list_del_init(&qh->qh_list_entry);
+
+	/* Update claimed usecs per (micro)frame */
+	hsotg->periodic_usecs -= qh->host_us;
+
+	if (hsotg->core_params->uframe_sched > 0) {
+		int i;
+
+		for (i = 0; i < 8; i++) {
+			hsotg->frame_usecs[i] += qh->frame_usecs[i];
+			qh->frame_usecs[i] = 0;
+		}
+	} else {
+		/* Release periodic channel reservation */
+		hsotg->periodic_channels--;
+	}
+}
+
+/**
+ * dwc2_unreserve_timer_fn() - Timer function to release periodic reservation
+ *
+ * According to the kernel doc for usb_submit_urb() (specifically the part about
+ * "Reserved Bandwidth Transfers"), we need to keep a reservation active as
+ * long as a device driver keeps submitting.  Since we're using HCD_BH to give
+ * back the URB we need to give the driver a little bit of time before we
+ * release the reservation.  This worker is called after the appropriate
+ * delay.
+ *
+ * @work: Pointer to a qh unreserve_work.
+ */
+static void dwc2_unreserve_timer_fn(unsigned long data)
+{
+	struct dwc2_qh *qh = (struct dwc2_qh *)data;
+	struct dwc2_hsotg *hsotg = qh->hsotg;
+	unsigned long flags;
 
-	if (qh->dev_speed == USB_SPEED_HIGH) {
-		/* if this is a hs transaction we need a full frame */
-		ret = dwc2_find_single_uframe(hsotg, qh);
-	} else {
-		/*
-		 * if this is a fs transaction we may need a sequence
-		 * of frames
-		 */
-		ret = dwc2_find_multi_uframe(hsotg, qh);
+	/*
+	 * Wait for the lock, or for us to be scheduled again.  We
+	 * could be scheduled again if:
+	 * - We started executing but didn't get the lock yet.
+	 * - A new reservation came in, but cancel didn't take effect
+	 *   because we already started executing.
+	 * - The timer has been kicked again.
+	 * In that case cancel and wait for the next call.
+	 */
+	while (!spin_trylock_irqsave(&hsotg->lock, flags)) {
+		if (timer_pending(&qh->unreserve_timer))
+			return;
 	}
-	return ret;
+
+	/*
+	 * Might be no more unreserve pending if:
+	 * - We started executing but didn't get the lock yet.
+	 * - A new reservation came in, but cancel didn't take effect
+	 *   because we already started executing.
+	 *
+	 * We can't put this in the loop above because unreserve_pending needs
+	 * to be accessed under lock, so we can only check it once we got the
+	 * lock.
+	 */
+	if (qh->unreserve_pending)
+		dwc2_do_unreserve(hsotg, qh);
+
+	spin_unlock_irqrestore(&hsotg->lock, flags);
 }
 
 /**
@@ -695,6 +491,210 @@ static void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg,
 }
 
 /**
+ * dwc2_qh_init() - Initializes a QH structure
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller
+ * @qh:    The QH to init
+ * @urb:   Holds the information about the device/endpoint needed to initialize
+ *         the QH
+ */
+#define SCHEDULE_SLOP 10
+static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
+			 struct dwc2_hcd_urb *urb)
+{
+	int dev_speed, hub_addr, hub_port;
+	char *speed, *type;
+
+	dev_vdbg(hsotg->dev, "%s()\n", __func__);
+
+	/* Initialize QH */
+	qh->hsotg = hsotg;
+	setup_timer(&qh->unreserve_timer, dwc2_unreserve_timer_fn,
+		    (unsigned long)qh);
+	qh->ep_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
+	qh->ep_is_in = dwc2_hcd_is_pipe_in(&urb->pipe_info) ? 1 : 0;
+
+	qh->data_toggle = DWC2_HC_PID_DATA0;
+	qh->maxp = dwc2_hcd_get_mps(&urb->pipe_info);
+	INIT_LIST_HEAD(&qh->qtd_list);
+	INIT_LIST_HEAD(&qh->qh_list_entry);
+
+	/* FS/LS Endpoint on HS Hub, NOT virtual root hub */
+	dev_speed = dwc2_host_get_speed(hsotg, urb->priv);
+
+	dwc2_host_hub_info(hsotg, urb->priv, &hub_addr, &hub_port);
+
+	if ((dev_speed == USB_SPEED_LOW || dev_speed == USB_SPEED_FULL) &&
+	    hub_addr != 0 && hub_addr != 1) {
+		dev_vdbg(hsotg->dev,
+			 "QH init: EP %d: TT found at hub addr %d, for port %d\n",
+			 dwc2_hcd_get_ep_num(&urb->pipe_info), hub_addr,
+			 hub_port);
+		qh->do_split = 1;
+	}
+
+	if (qh->ep_type == USB_ENDPOINT_XFER_INT ||
+	    qh->ep_type == USB_ENDPOINT_XFER_ISOC) {
+		/* Compute scheduling parameters once and save them */
+		u32 hprt, prtspd;
+
+		/* Todo: Account for split transfers in the bus time */
+		int bytecount =
+			dwc2_hb_mult(qh->maxp) * dwc2_max_packet(qh->maxp);
+
+		qh->host_us = NS_TO_US(usb_calc_bus_time(qh->do_split ?
+			      USB_SPEED_HIGH : dev_speed, qh->ep_is_in,
+			      qh->ep_type == USB_ENDPOINT_XFER_ISOC,
+			      bytecount));
+
+		/* Ensure frame_number corresponds to the reality */
+		hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
+		/* Start in a slightly future (micro)frame */
+		qh->next_active_frame = dwc2_frame_num_inc(hsotg->frame_number,
+						     SCHEDULE_SLOP);
+		qh->host_interval = urb->interval;
+		dwc2_sch_dbg(hsotg, "QH=%p init nxt=%04x, fn=%04x, int=%#x\n",
+			     qh, qh->next_active_frame, hsotg->frame_number,
+			     qh->host_interval);
+#if 0
+		/* Increase interrupt polling rate for debugging */
+		if (qh->ep_type == USB_ENDPOINT_XFER_INT)
+			qh->host_interval = 8;
+#endif
+		hprt = dwc2_readl(hsotg->regs + HPRT0);
+		prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
+		if (prtspd == HPRT0_SPD_HIGH_SPEED &&
+		    (dev_speed == USB_SPEED_LOW ||
+		     dev_speed == USB_SPEED_FULL)) {
+			qh->host_interval *= 8;
+			qh->next_active_frame |= 0x7;
+			qh->start_split_frame = qh->next_active_frame;
+			dwc2_sch_dbg(hsotg,
+				     "QH=%p init*8 nxt=%04x, fn=%04x, int=%#x\n",
+				     qh, qh->next_active_frame,
+				     hsotg->frame_number, qh->host_interval);
+
+		}
+		dev_dbg(hsotg->dev, "interval=%d\n", qh->host_interval);
+	}
+
+	dev_vdbg(hsotg->dev, "DWC OTG HCD QH Initialized\n");
+	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - qh = %p\n", qh);
+	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Device Address = %d\n",
+		 dwc2_hcd_get_dev_addr(&urb->pipe_info));
+	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Endpoint %d, %s\n",
+		 dwc2_hcd_get_ep_num(&urb->pipe_info),
+		 dwc2_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT");
+
+	qh->dev_speed = dev_speed;
+
+	switch (dev_speed) {
+	case USB_SPEED_LOW:
+		speed = "low";
+		break;
+	case USB_SPEED_FULL:
+		speed = "full";
+		break;
+	case USB_SPEED_HIGH:
+		speed = "high";
+		break;
+	default:
+		speed = "?";
+		break;
+	}
+	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Speed = %s\n", speed);
+
+	switch (qh->ep_type) {
+	case USB_ENDPOINT_XFER_ISOC:
+		type = "isochronous";
+		break;
+	case USB_ENDPOINT_XFER_INT:
+		type = "interrupt";
+		break;
+	case USB_ENDPOINT_XFER_CONTROL:
+		type = "control";
+		break;
+	case USB_ENDPOINT_XFER_BULK:
+		type = "bulk";
+		break;
+	default:
+		type = "?";
+		break;
+	}
+
+	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Type = %s\n", type);
+
+	if (qh->ep_type == USB_ENDPOINT_XFER_INT) {
+		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - usecs = %d\n",
+			 qh->host_us);
+		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - interval = %d\n",
+			 qh->host_interval);
+	}
+}
+
+/**
+ * dwc2_hcd_qh_create() - Allocates and initializes a QH
+ *
+ * @hsotg:        The HCD state structure for the DWC OTG controller
+ * @urb:          Holds the information about the device/endpoint needed
+ *                to initialize the QH
+ * @atomic_alloc: Flag to do atomic allocation if needed
+ *
+ * Return: Pointer to the newly allocated QH, or NULL on error
+ */
+struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
+					  struct dwc2_hcd_urb *urb,
+					  gfp_t mem_flags)
+{
+	struct dwc2_qh *qh;
+
+	if (!urb->priv)
+		return NULL;
+
+	/* Allocate memory */
+	qh = kzalloc(sizeof(*qh), mem_flags);
+	if (!qh)
+		return NULL;
+
+	dwc2_qh_init(hsotg, qh, urb);
+
+	if (hsotg->core_params->dma_desc_enable > 0 &&
+	    dwc2_hcd_qh_init_ddma(hsotg, qh, mem_flags) < 0) {
+		dwc2_hcd_qh_free(hsotg, qh);
+		return NULL;
+	}
+
+	return qh;
+}
+
+/**
+ * dwc2_hcd_qh_free() - Frees the QH
+ *
+ * @hsotg: HCD instance
+ * @qh:    The QH to free
+ *
+ * QH should already be removed from the list. QTD list should already be empty
+ * if called from URB Dequeue.
+ *
+ * Must NOT be called with interrupt disabled or spinlock held
+ */
+void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+	/* Make sure any unreserve work is finished. */
+	if (del_timer_sync(&qh->unreserve_timer)) {
+		unsigned long flags;
+
+		spin_lock_irqsave(&hsotg->lock, flags);
+		dwc2_do_unreserve(hsotg, qh);
+		spin_unlock_irqrestore(&hsotg->lock, flags);
+	}
+
+	if (qh->desc_list)
+		dwc2_hcd_qh_free_ddma(hsotg, qh);
+	kfree(qh);
+}
+
+/**
  * dwc2_hcd_qh_add() - Adds a QH to either the non periodic or periodic
  * schedule if it is not already in the schedule. If the QH is already in
  * the schedule, no action is taken.
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Fri Jan 22 10:18:52 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Fri, 22 Jan 2016 10:18:52 -0800
Subject: [PATCH v5 17/21] usb: dwc2: host: Schedule periodic right away if
 it's time
In-Reply-To: <1453486736-15358-1-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
Message-ID: <1453486736-15358-18-git-send-email-dianders@chromium.org>

In dwc2_hcd_qh_deactivate() we will put some things on the
periodic_sched_ready list.  These things won't be taken off the ready
list until the next SOF, which might be a little late.  Let's put them
on right away.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v5: None
Changes in v4:
- Schedule periodic right away if it's time new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/hcd_queue.c | 18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index f235402dfc5e..2b6e57ad2661 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -1078,12 +1078,26 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 	 * Note: we purposely use the frame_number from the "hsotg" structure
 	 * since we know SOF interrupt will handle future frames.
 	 */
-	if (dwc2_frame_num_le(qh->next_active_frame, hsotg->frame_number))
+	if (dwc2_frame_num_le(qh->next_active_frame, hsotg->frame_number)) {
+		enum dwc2_transaction_type tr_type;
+
+		/*
+		 * We're bypassing the SOF handler which is normally what puts
+		 * us on the ready list because we're in a hurry and need to
+		 * try to catch up.
+		 */
+		dwc2_sch_vdbg(hsotg, "QH=%p IMM ready fn=%04x, nxt=%04x\n",
+			      qh, frame_number, qh->next_active_frame);
 		list_move_tail(&qh->qh_list_entry,
 			       &hsotg->periodic_sched_ready);
-	else
+
+		tr_type = dwc2_hcd_select_transactions(hsotg);
+		if (tr_type != DWC2_TRANSACTION_NONE)
+			dwc2_hcd_queue_transactions(hsotg, tr_type);
+	} else {
 		list_move_tail(&qh->qh_list_entry,
 			       &hsotg->periodic_sched_inactive);
+	}
 }
 
 /**
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Fri Jan 22 10:18:53 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Fri, 22 Jan 2016 10:18:53 -0800
Subject: [PATCH v5 18/21] usb: dwc2: host: Add
 dwc2_hcd_get_future_frame_number() call
In-Reply-To: <1453486736-15358-1-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
Message-ID: <1453486736-15358-19-git-send-email-dianders@chromium.org>

As we start getting more exact about our scheduling it's becoming more
and more important to know exactly how far through the current frame we
are.  This lets us make decisions about whether there's still time left
to start a new transaction in the current frame.

We'll add dwc2_hcd_get_future_frame_number() which will tell you what
the frame number will be a certain number of microseconds (us) from
now.  We can use this information to help decide if there's enough time
left in the frame for a transaction that will take a certain duration.

This is expected to be used by a future change ("usb: dwc2: host:
Properly set even/odd frame").

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v5: None
Changes in v4:
- Add dwc2_hcd_get_future_frame_number() call new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/core.h |  4 ++++
 drivers/usb/dwc2/hcd.c  | 29 +++++++++++++++++++++++++++++
 2 files changed, 33 insertions(+)

diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 64d45a2053bb..52cbea28d0e9 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -1235,12 +1235,16 @@ static inline int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg,
 
 #if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
 extern int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg);
+extern int dwc2_hcd_get_future_frame_number(struct dwc2_hsotg *hsotg, int us);
 extern void dwc2_hcd_connect(struct dwc2_hsotg *hsotg);
 extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force);
 extern void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
 #else
 static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
 { return 0; }
+static inline int dwc2_hcd_get_future_frame_number(struct dwc2_hsotg *hsotg,
+						   int us)
+{ return 0; }
 static inline void dwc2_hcd_connect(struct dwc2_hsotg *hsotg) {}
 static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force) {}
 static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {}
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 03072d1fd44d..83dc92da10a0 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -1947,6 +1947,35 @@ int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
 	return (hfnum & HFNUM_FRNUM_MASK) >> HFNUM_FRNUM_SHIFT;
 }
 
+int dwc2_hcd_get_future_frame_number(struct dwc2_hsotg *hsotg, int us)
+{
+	u32 hprt = dwc2_readl(hsotg->regs + HPRT0);
+	u32 hfir = dwc2_readl(hsotg->regs + HFIR);
+	u32 hfnum = dwc2_readl(hsotg->regs + HFNUM);
+	unsigned int us_per_frame;
+	unsigned int frame_number;
+	unsigned int remaining;
+	unsigned int interval;
+	unsigned int phy_clks;
+
+	/* High speed has 125 us per (micro) frame; others are 1 ms per */
+	us_per_frame = (hprt & HPRT0_SPD_MASK) ? 1000 : 125;
+
+	/* Extract fields */
+	frame_number = (hfnum & HFNUM_FRNUM_MASK) >> HFNUM_FRNUM_SHIFT;
+	remaining = (hfnum & HFNUM_FRREM_MASK) >> HFNUM_FRREM_SHIFT;
+	interval = (hfir & HFIR_FRINT_MASK) >> HFIR_FRINT_SHIFT;
+
+	/*
+	 * Number of phy clocks since the last tick of the frame number after
+	 * "us" has passed.
+	 */
+	phy_clks = (interval - remaining) +
+		   DIV_ROUND_UP(interval * us, us_per_frame);
+
+	return dwc2_frame_num_inc(frame_number, phy_clks / interval);
+}
+
 int dwc2_hcd_is_b_host(struct dwc2_hsotg *hsotg)
 {
 	return hsotg->op_state == OTG_STATE_B_HOST;
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Fri Jan 22 10:18:56 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Fri, 22 Jan 2016 10:18:56 -0800
Subject: [PATCH v5 21/21] usb: dwc2: host: If using uframe scheduler,
 end splits better
In-Reply-To: <1453486736-15358-1-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
Message-ID: <1453486736-15358-22-git-send-email-dianders@chromium.org>

The microframe scheduler figured out exactly how many transfers we need
for a split transaction.  Let's use this knowledge to know when to end
things.

Without this I found that certain devices would just keep responding
with tons of NYET resonses on their INT_IN endpoint.  These would just
keep going and going and eventually we'd decide to terminate the
transfer (because the whole frame changed), but by that time the
scheduler would decide that we "missed" the start of the next transfer.
I can also imagine that if we blow past the end of our scheduled time we
may mess up other things that were scheduled to happen.

No known test cases are improved by this patch except that the scheduler
code doesn't yell about MISSES constantly anymore.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v5: None
Changes in v4:
- If using uframe scheduler, end splits better new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/hcd_intr.c | 48 +++++++++++++++++++++++++++++++++++++++------
 1 file changed, 42 insertions(+), 6 deletions(-)

diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index 1875e4590a3c..2519e1bdad1a 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -1353,14 +1353,50 @@ static void dwc2_hc_nyet_intr(struct dwc2_hsotg *hsotg,
 
 		if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
 		    chan->ep_type == USB_ENDPOINT_XFER_ISOC) {
-			int frnum = dwc2_hcd_get_frame_number(hsotg);
+			struct dwc2_qh *qh = chan->qh;
+			bool past_end;
+
+			if (hsotg->core_params->uframe_sched <= 0) {
+				int frnum = dwc2_hcd_get_frame_number(hsotg);
+
+				/* Don't have num_hs_transfers; simple logic */
+				past_end = dwc2_full_frame_num(frnum) !=
+				     dwc2_full_frame_num(qh->next_active_frame);
+			} else {
+				int end_frnum;
 
-			if (dwc2_full_frame_num(frnum) !=
-			    dwc2_full_frame_num(chan->qh->next_active_frame)) {
 				/*
-				 * No longer in the same full speed frame.
-				 * Treat this as a transaction error.
-				 */
+				* Figure out the end frame based on schedule.
+				*
+				* We don't want to go on trying again and again
+				* forever.  Let's stop when we've done all the
+				* transfers that were scheduled.
+				*
+				* We're going to be comparing start_active_frame
+				* and next_active_frame, both of which are 1
+				* before the time the packet goes on the wire,
+				* so that cancels out.  Basically if had 1
+				* transfer and we saw 1 NYET then we're done.
+				* We're getting a NYET here so if next >=
+				* (start + num_transfers) we're done. The
+				* complexity is that for all but ISOC_OUT we
+				* skip one slot.
+				*/
+				end_frnum = dwc2_frame_num_inc(
+					qh->start_active_frame,
+					qh->num_hs_transfers);
+
+				if (qh->ep_type != USB_ENDPOINT_XFER_ISOC ||
+				    qh->ep_is_in)
+					end_frnum =
+					       dwc2_frame_num_inc(end_frnum, 1);
+
+				past_end = dwc2_frame_num_le(
+					end_frnum, qh->next_active_frame);
+			}
+
+			if (past_end) {
+				/* Treat this as a transaction error. */
 #if 0
 				/*
 				 * Todo: Fix system performance so this can
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Fri Jan 22 10:18:54 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Fri, 22 Jan 2016 10:18:54 -0800
Subject: [PATCH v5 19/21] usb: dwc2: host: Properly set even/odd frame
In-Reply-To: <1453486736-15358-1-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
Message-ID: <1453486736-15358-20-git-send-email-dianders@chromium.org>

When setting up ISO and INT transfers dwc2 needs to specify whether the
transfer is for an even or an odd frame (or microframe if the controller
is running in high speed mode).

The controller appears to use this as a simple way to figure out if a
transfer should happen right away (in the current microframe) or should
happen at the start of the next microframe.  Said another way:

- If you set "odd" and the current frame number is odd it appears that
  the controller will try to transfer right away.  Same thing if you set
  "even" and the current frame number is even.
- If the oddness you set and the oddness of the frame number are
  _different_, the transfer will be delayed until the frame number
  changes.

As I understand it, the above technique allows you to plan ahead of time
where possible by always working on the next frame.  ...but it still
allows you to properly respond immediately to things that happened in
the previous frame.

The old dwc2_hc_set_even_odd_frame() didn't really handle this concept.
It always looked at the frame number and setup the transfer to happen in
the next frame.  In some cases that meant that certain transactions
would be transferred in the wrong frame.

We'll try our best to set the even / odd to do the transfer in the
scheduled frame.  If that fails then we'll do an ugly "schedule ASAP".
We'll also modify the scheduler code to handle this and not try to
schedule a second transfer for the same frame.

Note that this change relies on the work to redo the microframe
scheduler.  It can work atop ("usb: dwc2: host: Manage frame nums better
in scheduler") but it works even better after ("usb: dwc2: host: Totally
redo the microframe scheduler").

With this change my stressful USB test (USB webcam + USB audio +
keyboards) has less audio crackling than before.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v5: None
Changes in v4:
- Properly set even/odd frame new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/core.c      | 92 +++++++++++++++++++++++++++++++++++++++++++-
 drivers/usb/dwc2/hcd_queue.c | 11 +++++-
 2 files changed, 100 insertions(+), 3 deletions(-)

diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index ed73b26818c0..488862e08d59 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -1703,9 +1703,97 @@ static void dwc2_hc_set_even_odd_frame(struct dwc2_hsotg *hsotg,
 {
 	if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
 	    chan->ep_type == USB_ENDPOINT_XFER_ISOC) {
-		/* 1 if _next_ frame is odd, 0 if it's even */
-		if (!(dwc2_hcd_get_frame_number(hsotg) & 0x1))
+		int host_speed;
+		int xfer_ns;
+		int xfer_us;
+		int bytes_in_fifo;
+		u16 fifo_space;
+		u16 frame_number;
+		u16 wire_frame;
+
+		/*
+		 * Try to figure out if we're an even or odd frame. If we set
+		 * even and the current frame number is even the the transfer
+		 * will happen immediately.  Similar if both are odd. If one is
+		 * even and the other is odd then the transfer will happen when
+		 * the frame number ticks.
+		 *
+		 * There's a bit of a balancing act to get this right.
+		 * Sometimes we may want to send data in the current frame (AK
+		 * right away).  We might want to do this if the frame number
+		 * _just_ ticked, but we might also want to do this in order
+		 * to continue a split transaction that happened late in a
+		 * microframe (so we didn't know to queue the next transfer
+		 * until the frame number had ticked).  The problem is that we
+		 * need a lot of knowledge to know if there's actually still
+		 * time to send things or if it would be better to wait until
+		 * the next frame.
+		 *
+		 * We can look at how much time is left in the current frame
+		 * and make a guess about whether we'll have time to transfer.
+		 * We'll do that.
+		 */
+
+		/* Get speed host is running at */
+		host_speed = (chan->speed != USB_SPEED_HIGH &&
+			      !chan->do_split) ? chan->speed : USB_SPEED_HIGH;
+
+		/* See how many bytes are in the periodic FIFO right now */
+		fifo_space = (dwc2_readl(hsotg->regs + HPTXSTS) &
+			      TXSTS_FSPCAVAIL_MASK) >> TXSTS_FSPCAVAIL_SHIFT;
+		bytes_in_fifo = sizeof(u32) *
+				(hsotg->core_params->host_perio_tx_fifo_size -
+				 fifo_space);
+
+		/*
+		 * Roughly estimate bus time for everything in the periodic
+		 * queue + our new transfer.  This is "rough" because we're
+		 * using a function that makes takes into account IN/OUT
+		 * and INT/ISO and we're just slamming in one value for all
+		 * transfers.  This should be an over-estimate and that should
+		 * be OK, but we can probably tighten it.
+		 */
+		xfer_ns = usb_calc_bus_time(host_speed, false, false,
+					    chan->xfer_len + bytes_in_fifo);
+		xfer_us = NS_TO_US(xfer_ns);
+
+		/* See what frame number we'll be at by the time we finish */
+		frame_number = dwc2_hcd_get_future_frame_number(hsotg, xfer_us);
+
+		/* This is when we were scheduled to be on the wire */
+		wire_frame = dwc2_frame_num_inc(chan->qh->next_active_frame, 1);
+
+		/*
+		 * If we'd finish _after_ the frame we're scheduled in then
+		 * it's hopeless.  Just schedule right away and hope for the
+		 * best.  Note that it _might_ be wise to call back into the
+		 * scheduler to pick a better frame, but this is better than
+		 * nothing.
+		 */
+		if (dwc2_frame_num_gt(frame_number, wire_frame)) {
+			dwc2_sch_vdbg(hsotg,
+				      "QH=%p EO MISS fr=%04x=>%04x (%+d)\n",
+				      chan->qh, wire_frame, frame_number,
+				      dwc2_frame_num_dec(frame_number,
+							 wire_frame));
+			wire_frame = frame_number;
+
+			/*
+			 * We picked a different frame number; communicate this
+			 * back to the scheduler so it doesn't try to schedule
+			 * another in the same frame.
+			 *
+			 * Remember that next_active_frame is 1 before the wire
+			 * frame.
+			 */
+			chan->qh->next_active_frame =
+				dwc2_frame_num_dec(frame_number, 1);
+		}
+
+		if (wire_frame & 1)
 			*hcchar |= HCCHAR_ODDFRM;
+		else
+			*hcchar &= ~HCCHAR_ODDFRM;
 	}
 }
 
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 2b6e57ad2661..7f54eeb2bf5f 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -983,6 +983,14 @@ static int dwc2_next_periodic_start(struct dwc2_hsotg *hsotg,
 	 *   and next_active_frame are always 1 frame before we want things
 	 *   to be active and we assume we can still get scheduled in the
 	 *   current frame number.
+	 * - It's possible for start_active_frame (now incremented) to be
+	 *   next_active_frame if we got an EO MISS (even_odd miss) which
+	 *   basically means that we detected there wasn't enough time for
+	 *   the last packet and dwc2_hc_set_even_odd_frame() rescheduled us
+	 *   at the last second.  We want to make sure we don't schedule
+	 *   another transfer for the same frame.  My test webcam doesn't seem
+	 *   terribly upset by missing a transfer but really doesn't like when
+	 *   we do two transfers in the same frame.
 	 * - Some misses are expected.  Specifically, in order to work
 	 *   perfectly dwc2 really needs quite spectacular interrupt latency
 	 *   requirements.  It needs to be able to handle its interrupts
@@ -993,7 +1001,8 @@ static int dwc2_next_periodic_start(struct dwc2_hsotg *hsotg,
 	 *   guarantee that a system will have interrupt latency < 125 us, so
 	 *   we have to be robust to some misses.
 	 */
-	if (dwc2_frame_num_gt(prev_frame_number, qh->start_active_frame)) {
+	if (qh->start_active_frame == qh->next_active_frame ||
+	    dwc2_frame_num_gt(prev_frame_number, qh->start_active_frame)) {
 		u16 ideal_start = qh->start_active_frame;
 
 		/* Adjust interval as per gcd with plan length. */
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Fri Jan 22 10:18:51 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Fri, 22 Jan 2016 10:18:51 -0800
Subject: [PATCH v5 16/21] usb: dwc2: host: Manage frame nums better in
 scheduler
In-Reply-To: <1453486736-15358-1-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
Message-ID: <1453486736-15358-17-git-send-email-dianders@chromium.org>

The dwc2 scheduler (contained in hcd_queue.c) was a bit confusing in the
way it initted / kept track of which frames a QH was going to be active
in.  Let's clean things up a little bit in preparation for a rewrite of
the microframe scheduler.

Specifically:
* Old code would pick a frame number in dwc2_qh_init() and would try to
  pick it "in a slightly future (micro)frame".  As far as I can tell the
  reason for this was that there was a delay between dwc2_qh_init() and
  when we actually wanted to dwc2_hcd_qh_add().  ...but apparently this
  attempt to be slightly in the future wasn't enough because
  dwc2_hcd_qh_add() then had code to reset things if the frame _wasn't_
  in the future.  There's no reason not to just pick the frame later.
  For non-periodic QH we now pick the frame in dwc2_hcd_qh_add().  For
  periodic QH we pick the frame at dwc2_schedule_periodic() time.
* The old "dwc2_qh_init() actually assigned to "hsotg->frame_number".
  This doesn't seem like a great idea since that variable is supposed to
  be used to keep track of which SOF the interrupt handler has seen.
  Let's be clean: anyone who wants the current frame number (instead of
  the one as of the last interrupt) should ask for it.
* The old code wasn't terribly consistent about trying to use the frame
  that the microframe scheduler assigned to it.  In
  dwc2_sched_periodic_split() when it was scheduling the first frame it
  always "ORed" in 0x7 (!).  Since the frame goes on the wire 1 uFrame
  after next_active_frame it meant that the SSPLIT would always try for
  uFrame 0 and the transaction would happen on the low speed bus during
  uFrame 1.  This is irregardless of what the microframe scheduler
  said.
* The old code assumed it would get called to schedule the next in a
  periodic split very quickly.  That is if next_active_frame was
  0 (transfer on wire in uFrame 1) it assumed it was getting called to
  schedule the next uFrame during uFrame 1 too (so it could queue
  something up for uFrame 2).  It should be possible to actually queue
  something up for uFrame 2 while in uFrame 2 (AKA queue up ASAP).  To
  do this, code needs to look at the previously scheduled frame when
  deciding when to next be active, not look at the current frame number.
* If there was no microframe scheduler, the old code would check for
  whether we should be active using "qh->next_active_frame ==
  frame_number".  This seemed like a race waiting to happen.  ...plus
  there's no way that you wouldn't want to schedule if next_active_frame
  was actually less than frame number.

Note that this change doesn't make 100% sense on its own since it's
expecting some sanity in the frame numbers assigned by the microframe
scheduler and (as per the future patch which rewries it) I think that
the current microframe scheduler is quite insane.  However, it seems
like splitting this up from the microframe scheduler patch makes things
into smaller chunks and hopefully adds to clarity rather than reduces
it.  The two patches could certainly be squashed.  Not that in the very
least, I don't see any obvious bad behavior introduced with just this
patch.

I've attempted to keep the config parameter to disable the microframe
scheduler in tact in this change, though I'm not sure it's worth it.
Obviously the code is touched a lot so it's possible I regressed
something when the microframe scheduler is disabled, though I did some
basic testing and it seemed to work OK.  I'm still not 100% sure why you
wouldn't want the microframe scheduler (presuming it works), so maybe a
future patch (or a future version of this patch?) could remove that
parameter.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v5: None
Changes in v4:
- Manage frame nums better in scheduler new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/hcd.h       |  10 +-
 drivers/usb/dwc2/hcd_queue.c | 355 ++++++++++++++++++++++++++++++++-----------
 2 files changed, 273 insertions(+), 92 deletions(-)

diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index 10c35585a2bd..fd266ac53a28 100644
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -244,8 +244,11 @@ enum dwc2_transaction_type {
  *                      the bus.  We'll move the qh to active here.  If the
  *                      host is in high speed mode this will be a uframe.  If
  *                      the host is in low speed mode this will be a full frame.
+ * @start_active_frame: If we are partway through a split transfer, this will be
+ *			what next_active_frame was when we started.  Otherwise
+ *			it should always be the same as next_active_frame.
+ * @assigned_uframe:    The uframe (0 -7) assigned by dwc2_find_uframe().
  * @frame_usecs:        Internal variable used by the microframe scheduler
- * @start_split_frame:  (Micro)frame at which last start split was initialized
  * @ntd:                Actual number of transfer descriptors in a list
  * @qtd_list:           List of QTDs for this QH
  * @channel:            Host channel currently processing transfers for this QH
@@ -279,8 +282,9 @@ struct dwc2_qh {
 	u16 host_us;
 	u16 host_interval;
 	u16 next_active_frame;
+	u16 start_active_frame;
+	u16 assigned_uframe;
 	u16 frame_usecs[8];
-	u16 start_split_frame;
 	u16 ntd;
 	struct list_head qtd_list;
 	struct dwc2_host_chan *channel;
@@ -746,7 +750,7 @@ do {									\
 	_qtd_ = list_entry((_qh_)->qtd_list.next, struct dwc2_qtd,	\
 			   qtd_list_entry);				\
 	if (usb_pipeint(_qtd_->urb->pipe) &&				\
-	    (_qh_)->start_split_frame != 0 && !_qtd_->complete_split) {	\
+	    (_qh_)->start_active_frame != 0 && !_qtd_->complete_split) { \
 		_hfnum_.d32 = dwc2_readl((_hcd_)->regs + HFNUM);	\
 		switch (_hfnum_.b.frnum & 0x7) {			\
 		case 7:							\
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 9ce407e5017d..f235402dfc5e 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -38,6 +38,7 @@
  * This file contains the functions to manage Queue Heads and Queue
  * Transfer Descriptors for Host mode
  */
+#include <linux/gcd.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/spinlock.h>
@@ -245,6 +246,96 @@ static int dwc2_find_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 }
 
 /**
+ * dwc2_pick_first_frame() - Choose 1st frame for qh that's already scheduled
+ *
+ * Takes a qh that has already been scheduled (which means we know we have the
+ * bandwdith reserved for us) and set the next_active_frame and the
+ * start_active_frame.
+ *
+ * This is expected to be called on qh's that weren't previously actively
+ * running.  It just picks the next frame that we can fit into without any
+ * thought about the past.
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller
+ * @qh:    QH for a periodic endpoint
+ *
+ */
+static void dwc2_pick_first_frame(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+	u16 frame_number;
+	u16 earliest_frame;
+	u16 next_active_frame;
+	u16 interval;
+
+	/*
+	 * Use the real frame number rather than the cached value as of the
+	 * last SOF to give us a little extra slop.
+	 */
+	frame_number = dwc2_hcd_get_frame_number(hsotg);
+
+	/*
+	 * We wouldn't want to start any earlier than the next frame just in
+	 * case the frame number ticks as we're doing this calculation.
+	 *
+	 * NOTE: if we could quantify how long till we actually get scheduled
+	 * we might be able to avoid the "+ 1" by looking at the upper part of
+	 * HFNUM (the FRREM field).  For now we'll just use the + 1 though.
+	 */
+	earliest_frame = dwc2_frame_num_inc(frame_number, 1);
+	next_active_frame = earliest_frame;
+
+	/* Get the "no microframe schduler" out of the way... */
+	if (hsotg->core_params->uframe_sched <= 0) {
+		if (qh->do_split)
+			/* Splits are active at microframe 0 minus 1 */
+			next_active_frame |= 0x7;
+		goto exit;
+	}
+
+	/* Adjust interval as per high speed schedule which has 8 uFrame */
+	interval = gcd(qh->host_interval, 8);
+
+	/*
+	 * We know interval must divide (HFNUM_MAX_FRNUM + 1) now that we've
+	 * done the gcd(), so it's safe to move to the beginning of the current
+	 * interval like this.
+	 *
+	 * After this we might be before earliest_frame, but don't worry,
+	 * we'll fix it...
+	 */
+	next_active_frame = (next_active_frame / interval) * interval;
+
+	/*
+	 * Actually choose to start at the frame number we've been
+	 * scheduled for.
+	 */
+	next_active_frame = dwc2_frame_num_inc(next_active_frame,
+					       qh->assigned_uframe);
+
+	/*
+	 * We actually need 1 frame before since the next_active_frame is
+	 * the frame number we'll be put on the ready list and we won't be on
+	 * the bus until 1 frame later.
+	 */
+	next_active_frame = dwc2_frame_num_dec(next_active_frame, 1);
+
+	/*
+	 * By now we might actually be before the earliest_frame.  Let's move
+	 * up intervals until we're not.
+	 */
+	while (dwc2_frame_num_gt(earliest_frame, next_active_frame))
+		next_active_frame = dwc2_frame_num_inc(next_active_frame,
+						       interval);
+
+exit:
+	qh->next_active_frame = next_active_frame;
+	qh->start_active_frame = next_active_frame;
+
+	dwc2_sch_vdbg(hsotg, "QH=%p First fn=%04x nxt=%04x\n",
+		     qh, frame_number, qh->next_active_frame);
+}
+
+/**
  * dwc2_do_reserve() - Make a periodic reservation
  *
  * Try to allocate space in the periodic schedule.  Depending on parameters
@@ -260,25 +351,9 @@ static int dwc2_do_reserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 	int status;
 
 	if (hsotg->core_params->uframe_sched > 0) {
-		int frame = -1;
-
 		status = dwc2_find_uframe(hsotg, qh);
-		if (status == 0)
-			frame = 7;
-		else if (status > 0)
-			frame = status - 1;
-
-		/* Set the new frame up */
-		if (frame >= 0) {
-			qh->next_active_frame &= ~0x7;
-			qh->next_active_frame |= (frame & 7);
-			dwc2_sch_dbg(hsotg,
-				     "QH=%p sched_p nxt=%04x, uf=%d\n",
-				     qh, qh->next_active_frame, frame);
-		}
-
-		if (status > 0)
-			status = 0;
+		if (status >= 0)
+			qh->assigned_uframe = status;
 	} else {
 		status = dwc2_periodic_channel_available(hsotg);
 		if (status) {
@@ -305,6 +380,8 @@ static int dwc2_do_reserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 	/* Update claimed usecs per (micro)frame */
 	hsotg->periodic_usecs += qh->host_us;
 
+	dwc2_pick_first_frame(hsotg, qh);
+
 	return 0;
 }
 
@@ -460,6 +537,14 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		status = dwc2_do_reserve(hsotg, qh);
 		if (status)
 			return status;
+	} else {
+		/*
+		 * It might have been a while, so make sure that frame_number
+		 * is still good.  Note: we could also try to use the similar
+		 * dwc2_next_periodic_start() but that schedules much more
+		 * tightly and we might need to hurry and queue things up.
+		 */
+		dwc2_pick_first_frame(hsotg, qh);
 	}
 
 	qh->unreserve_pending = 0;
@@ -520,7 +605,6 @@ static void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg,
  * @urb:   Holds the information about the device/endpoint needed to initialize
  *         the QH
  */
-#define SCHEDULE_SLOP 10
 static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 			 struct dwc2_hcd_urb *urb)
 {
@@ -569,11 +653,6 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 			      qh->ep_type == USB_ENDPOINT_XFER_ISOC,
 			      bytecount));
 
-		/* Ensure frame_number corresponds to the reality */
-		hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
-		/* Start in a slightly future (micro)frame */
-		qh->next_active_frame = dwc2_frame_num_inc(hsotg->frame_number,
-						     SCHEDULE_SLOP);
 		qh->host_interval = urb->interval;
 		dwc2_sch_dbg(hsotg, "QH=%p init nxt=%04x, fn=%04x, int=%#x\n",
 			     qh, qh->next_active_frame, hsotg->frame_number,
@@ -589,8 +668,6 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 		    (dev_speed == USB_SPEED_LOW ||
 		     dev_speed == USB_SPEED_FULL)) {
 			qh->host_interval *= 8;
-			qh->next_active_frame |= 0x7;
-			qh->start_split_frame = qh->next_active_frame;
 			dwc2_sch_dbg(hsotg,
 				     "QH=%p init*8 nxt=%04x, fn=%04x, int=%#x\n",
 				     qh, qh->next_active_frame,
@@ -738,22 +815,12 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		/* QH already in a schedule */
 		return 0;
 
-	if (!dwc2_frame_num_le(qh->next_active_frame, hsotg->frame_number) &&
-			!hsotg->frame_number) {
-		u16 new_frame;
-
-		dev_dbg(hsotg->dev,
-				"reset frame number counter\n");
-		new_frame = dwc2_frame_num_inc(hsotg->frame_number,
-				SCHEDULE_SLOP);
-
-		dwc2_sch_vdbg(hsotg, "QH=%p reset nxt=%04x=>%04x\n",
-			      qh, qh->next_active_frame, new_frame);
-		qh->next_active_frame = new_frame;
-	}
-
 	/* Add the new QH to the appropriate schedule */
 	if (dwc2_qh_is_non_per(qh)) {
+		/* Schedule right away */
+		qh->start_active_frame = hsotg->frame_number;
+		qh->next_active_frame = qh->start_active_frame;
+
 		/* Always start in inactive schedule */
 		list_add_tail(&qh->qh_list_entry,
 			      &hsotg->non_periodic_sched_inactive);
@@ -807,46 +874,145 @@ void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 	}
 }
 
-/*
- * Schedule the next continuing periodic split transfer
+/**
+ * dwc2_next_for_periodic_split() - Set next_active_frame midway thru a split.
+ *
+ * This is called for setting next_active_frame for periodic splits for all but
+ * the first packet of the split.  Confusing?  I thought so...
+ *
+ * Periodic splits are single low/full speed transfers that we end up splitting
+ * up into several high speed transfers.  They always fit into one full (1 ms)
+ * frame but might be split over several microframes (125 us each).  We to put
+ * each of the parts on a very specific high speed frame.
+ *
+ * This function figures out where the next active uFrame needs to be.
+ *
+ * @hsotg:        The HCD state structure
+ * @qh:           QH for the periodic transfer.
+ * @frame_number: The current frame number.
+ *
+ * Return: number missed by (or 0 if we didn't miss).
  */
-static void dwc2_sched_periodic_split(struct dwc2_hsotg *hsotg,
-				      struct dwc2_qh *qh, u16 frame_number,
-				      int sched_next_periodic_split)
+static int dwc2_next_for_periodic_split(struct dwc2_hsotg *hsotg,
+					 struct dwc2_qh *qh, u16 frame_number)
 {
-	u16 incr;
 	u16 old_frame = qh->next_active_frame;
+	u16 prev_frame_number = dwc2_frame_num_dec(frame_number, 1);
+	int missed = 0;
+	u16 incr;
+
+	/*
+	 * Basically: increment 1 normally, but 2 right after the start split
+	 * (except for ISOC out).
+	 */
+	if (old_frame == qh->start_active_frame &&
+	    !(qh->ep_type == USB_ENDPOINT_XFER_ISOC && !qh->ep_is_in))
+		incr = 2;
+	else
+		incr = 1;
+
+	qh->next_active_frame = dwc2_frame_num_inc(old_frame, incr);
 
-	if (sched_next_periodic_split) {
+	/*
+	 * Note that it's OK for frame_number to be 1 frame past
+	 * next_active_frame.  Remember that next_active_frame is supposed to
+	 * be 1 frame _before_ when we want to be scheduled.  If we're 1 frame
+	 * past it just means schedule ASAP.
+	 *
+	 * It's _not_ OK, however, if we're more than one frame past.
+	 */
+	if (dwc2_frame_num_gt(prev_frame_number, qh->next_active_frame)) {
+		/*
+		 * OOPS, we missed.  That's actually pretty bad since
+		 * the hub will be unhappy; try ASAP I guess.
+		 */
+		missed = dwc2_frame_num_dec(prev_frame_number,
+					    qh->next_active_frame);
 		qh->next_active_frame = frame_number;
-		incr = dwc2_frame_num_inc(qh->start_split_frame, 1);
-		if (dwc2_frame_num_le(frame_number, incr)) {
-			/*
-			 * Allow one frame to elapse after start split
-			 * microframe before scheduling complete split, but
-			 * DON'T if we are doing the next start split in the
-			 * same frame for an ISOC out
-			 */
-			if (qh->ep_type != USB_ENDPOINT_XFER_ISOC ||
-			    qh->ep_is_in != 0) {
-				qh->next_active_frame = dwc2_frame_num_inc(
-					qh->next_active_frame, 1);
-			}
-		}
-	} else {
-		qh->next_active_frame =
-			dwc2_frame_num_inc(qh->start_split_frame,
-					   qh->host_interval);
-		if (dwc2_frame_num_le(qh->next_active_frame, frame_number))
-			qh->next_active_frame = frame_number;
-		qh->next_active_frame |= 0x7;
-		qh->start_split_frame = qh->next_active_frame;
 	}
 
-	dwc2_sch_vdbg(hsotg, "QH=%p next(%d) fn=%04x, nxt=%04x=>%04x (%+d)\n",
-		      qh, sched_next_periodic_split, frame_number, old_frame,
-		      qh->next_active_frame,
-		      dwc2_frame_num_dec(qh->next_active_frame, old_frame));
+	return missed;
+}
+
+/**
+ * dwc2_next_periodic_start() - Set next_active_frame for next transfer start
+ *
+ * This is called for setting next_active_frame for a periodic transfer for
+ * all cases other than midway through a periodic split.  This will also update
+ * start_active_frame.
+ *
+ * Since we _always_ keep start_active_frame as the start of the previous
+ * transfer this is normally pretty easy: we just add our interval to
+ * start_active_frame and we've got our answer.
+ *
+ * The tricks come into play if we miss.  In that case we'll look for the next
+ * slot we can fit into.
+ *
+ * @hsotg:        The HCD state structure
+ * @qh:           QH for the periodic transfer.
+ * @frame_number: The current frame number.
+ *
+ * Return: number missed by (or 0 if we didn't miss).
+ */
+static int dwc2_next_periodic_start(struct dwc2_hsotg *hsotg,
+				     struct dwc2_qh *qh, u16 frame_number)
+{
+	int missed = 0;
+	u16 interval = qh->host_interval;
+	u16 prev_frame_number = dwc2_frame_num_dec(frame_number, 1);
+
+	qh->start_active_frame = dwc2_frame_num_inc(qh->start_active_frame,
+						    interval);
+
+	/*
+	 * The dwc2_frame_num_gt() function used below won't work terribly well
+	 * with if we just incremented by a really large intervals since the
+	 * frame counter only goes to 0x3fff.  It's terribly unlikely that we
+	 * will have missed in this case anyway.  Just go to exit.  If we want
+	 * to try to do better we'll need to keep track of a bigger counter
+	 * somewhere in the driver and handle overflows.
+	 */
+	if (interval >= 0x1000)
+		goto exit;
+
+	/*
+	 * Test for misses, which is when it's too late to schedule.
+	 *
+	 * A few things to note:
+	 * - We compare against prev_frame_number since start_active_frame
+	 *   and next_active_frame are always 1 frame before we want things
+	 *   to be active and we assume we can still get scheduled in the
+	 *   current frame number.
+	 * - Some misses are expected.  Specifically, in order to work
+	 *   perfectly dwc2 really needs quite spectacular interrupt latency
+	 *   requirements.  It needs to be able to handle its interrupts
+	 *   completely within 125 us of them being asserted. That not only
+	 *   means that the dwc2 interrupt handler needs to be fast but it
+	 *   means that nothing else in the system has to block dwc2 for a long
+	 *   time.  We can help with the dwc2 parts of this, but it's hard to
+	 *   guarantee that a system will have interrupt latency < 125 us, so
+	 *   we have to be robust to some misses.
+	 */
+	if (dwc2_frame_num_gt(prev_frame_number, qh->start_active_frame)) {
+		u16 ideal_start = qh->start_active_frame;
+
+		/* Adjust interval as per gcd with plan length. */
+		interval = gcd(interval, 8);
+
+		do {
+			qh->start_active_frame = dwc2_frame_num_inc(
+				qh->start_active_frame, interval);
+		} while (dwc2_frame_num_gt(prev_frame_number,
+					   qh->start_active_frame));
+
+		missed = dwc2_frame_num_dec(qh->start_active_frame,
+					    ideal_start);
+	}
+
+exit:
+	qh->next_active_frame = qh->start_active_frame;
+
+	return missed;
 }
 
 /*
@@ -865,7 +1031,9 @@ static void dwc2_sched_periodic_split(struct dwc2_hsotg *hsotg,
 void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 			    int sched_next_periodic_split)
 {
+	u16 old_frame = qh->next_active_frame;
 	u16 frame_number;
+	int missed;
 
 	if (dbg_qh(qh))
 		dev_vdbg(hsotg->dev, "%s()\n", __func__);
@@ -878,30 +1046,39 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 		return;
 	}
 
-	frame_number = dwc2_hcd_get_frame_number(hsotg);
-
-	if (qh->do_split) {
-		dwc2_sched_periodic_split(hsotg, qh, frame_number,
-					  sched_next_periodic_split);
-	} else {
-		qh->next_active_frame = dwc2_frame_num_inc(
-			qh->next_active_frame, qh->host_interval);
-		if (dwc2_frame_num_le(qh->next_active_frame, frame_number))
-			qh->next_active_frame = frame_number;
-	}
-
 	if (list_empty(&qh->qtd_list)) {
 		dwc2_hcd_qh_unlink(hsotg, qh);
 		return;
 	}
+
+	/*
+	 * Use the real frame number rather than the cached value as of the
+	 * last SOF just to get us a little closer to reality.  Note that
+	 * means we don't actually know if we've already handled the SOF
+	 * interrupt for this frame.
+	 */
+	frame_number = dwc2_hcd_get_frame_number(hsotg);
+
+	if (sched_next_periodic_split)
+		missed = dwc2_next_for_periodic_split(hsotg, qh, frame_number);
+	else
+		missed = dwc2_next_periodic_start(hsotg, qh, frame_number);
+
+	dwc2_sch_vdbg(hsotg,
+		     "QH=%p next(%d) fn=%04x, sch=%04x=>%04x (%+d) miss=%d %s\n",
+		     qh, sched_next_periodic_split, frame_number, old_frame,
+		     qh->next_active_frame,
+		     dwc2_frame_num_dec(qh->next_active_frame, old_frame),
+		missed, missed ? "MISS" : "");
+
 	/*
 	 * Remove from periodic_sched_queued and move to
 	 * appropriate queue
+	 *
+	 * Note: we purposely use the frame_number from the "hsotg" structure
+	 * since we know SOF interrupt will handle future frames.
 	 */
-	if ((hsotg->core_params->uframe_sched > 0 &&
-	     dwc2_frame_num_le(qh->next_active_frame, frame_number)) ||
-	    (hsotg->core_params->uframe_sched <= 0 &&
-	     qh->next_active_frame == frame_number))
+	if (dwc2_frame_num_le(qh->next_active_frame, hsotg->frame_number))
 		list_move_tail(&qh->qh_list_entry,
 			       &hsotg->periodic_sched_ready);
 	else
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Fri Jan 22 10:18:55 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Fri, 22 Jan 2016 10:18:55 -0800
Subject: [PATCH v5 20/21] usb: dwc2: host: Totally redo the microframe
 scheduler
In-Reply-To: <1453486736-15358-1-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
Message-ID: <1453486736-15358-21-git-send-email-dianders@chromium.org>

This totally reimplements the microframe scheduler in dwc2 to attempt to
handle periodic splits properly.  The old code didn't even try, so this
was a significant effort since periodic splits are one of the most
complicated things in USB.

I've attempted to keep the old "don't use the microframe" schduler
around for now, but not sure it's needed.  It has also only been lightly
tested.

I think it's pretty certain that this scheduler isn't perfect and might
have some bugs, but it seems much better than what was there before.
With this change my stressful USB test (USB webcam + USB audio + some
keyboards) crackles less.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v5:
- Moved defines outside of ifdef to avoid gadget-only compile error.

Changes in v4:
- Figured out what the microframe scheduler was supposed to do.
- Microframe rewrite is totally different from v3, hopefully more right.
- Microframe rewrite is later in the series now.

Changes in v3:
- The uframe scheduler patch is folded into optimization series.
- Optimize uframe scheduler "single uframe" case a little.
- uframe scheduler now atop logging patches.
- uframe scheduler now before delayed bandwidth release patches.
- Add defines like EARLY_FRAME_USEC
- Reorder dwc2_deschedule_periodic() in prep for future patches.
- uframe scheduler now shows real usefulness w/ future patches!
- Assuming single_tt is new for v3; not terribly well tested (yet).
- Keep track and use our uframe new for v3.

Changes in v2:
- Totally rewrote uframe scheduler again after writing test code.
- uframe scheduler atop delayed bandwidth release patches.

 drivers/usb/dwc2/core.h      |   85 ++-
 drivers/usb/dwc2/hcd.c       |   87 ++-
 drivers/usb/dwc2/hcd.h       |   79 ++-
 drivers/usb/dwc2/hcd_queue.c | 1208 +++++++++++++++++++++++++++++++++++-------
 4 files changed, 1271 insertions(+), 188 deletions(-)

diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 52cbea28d0e9..115925909390 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -592,6 +592,84 @@ struct dwc2_hregs_backup {
 	bool valid;
 };
 
+/*
+ * Constants related to high speed periodic scheduling
+ *
+ * We have a periodic schedule that is DWC2_HS_SCHEDULE_UFRAMES long.  From a
+ * reservation point of view it's assumed that the schedule goes right back to
+ * the beginning after the end of the schedule.
+ *
+ * What does that mean for scheduling things with a long interval?  It means
+ * we'll reserve time for them in every possible microframe that they could
+ * ever be scheduled in.  ...but we'll still only actually schedule them as
+ * often as they were requested.
+ *
+ * We keep our schedule in a "bitmap" structure.  This simplifies having
+ * to keep track of and merge intervals: we just let the bitmap code do most
+ * of the heavy lifting.  In a way scheduling is much like memory allocation.
+ *
+ * We schedule 100us per uframe or 80% of 125us (the maximum amount you're
+ * supposed to schedule for periodic transfers).  That's according to spec.
+ *
+ * Note that though we only schedule 80% of each microframe, the bitmap that we
+ * keep the schedule in is tightly packed (AKA it doesn't have 100us worth of
+ * space for each uFrame).
+ *
+ * Requirements:
+ * - DWC2_HS_SCHEDULE_UFRAMES must even divide 0x4000 (HFNUM_MAX_FRNUM + 1)
+ * - DWC2_HS_SCHEDULE_UFRAMES must be 8 times DWC2_LS_SCHEDULE_FRAMES (probably
+ *   could be any multiple of 8 times DWC2_LS_SCHEDULE_FRAMES, but there might
+ *   be bugs).  The 8 comes from the USB spec: number of microframes per frame.
+ */
+#define DWC2_US_PER_UFRAME		125
+#define DWC2_HS_PERIODIC_US_PER_UFRAME	100
+
+#define DWC2_HS_SCHEDULE_UFRAMES	8
+#define DWC2_HS_SCHEDULE_US		(DWC2_HS_SCHEDULE_UFRAMES * \
+					 DWC2_HS_PERIODIC_US_PER_UFRAME)
+
+/*
+ * Constants related to low speed scheduling
+ *
+ * For high speed we schedule every 1us.  For low speed that's a bit overkill,
+ * so we make up a unit called a "slice" that's worth 25us.  There are 40
+ * slices in a full frame and we can schedule 36 of those (90%) for periodic
+ * transfers.
+ *
+ * Our low speed schedule can be as short as 1 frame or could be longer.  When
+ * we only schedule 1 frame it means that we'll need to reserve a time every
+ * frame even for things that only transfer very rarely, so something that runs
+ * every 2048 frames will get time reserved in every frame.  Our low speed
+ * schedule can be longer and we'll be able to handle more overlap, but that
+ * will come at increased memory cost and increased time to schedule.
+ *
+ * Note: one other advantage of a short low speed schedule is that if we mess
+ * up and miss scheduling we can jump in and use any of the slots that we
+ * happened to reserve.
+ *
+ * With 25 us per slice and 1 frame in the schedule, we only need 4 bytes for
+ * the schedule.  There will be one schedule per TT.
+ *
+ * Requirements:
+ * - DWC2_US_PER_SLICE must evenly divide DWC2_LS_PERIODIC_US_PER_FRAME.
+ */
+#define DWC2_US_PER_SLICE	25
+#define DWC2_SLICES_PER_UFRAME	(DWC2_US_PER_UFRAME / DWC2_US_PER_SLICE)
+
+#define DWC2_ROUND_US_TO_SLICE(us) \
+				(DIV_ROUND_UP((us), DWC2_US_PER_SLICE) * \
+				 DWC2_US_PER_SLICE)
+
+#define DWC2_LS_PERIODIC_US_PER_FRAME \
+				900
+#define DWC2_LS_PERIODIC_SLICES_PER_FRAME \
+				(DWC2_LS_PERIODIC_US_PER_FRAME / \
+				 DWC2_US_PER_SLICE)
+
+#define DWC2_LS_SCHEDULE_FRAMES	1
+#define DWC2_LS_SCHEDULE_SLICES	(DWC2_LS_SCHEDULE_FRAMES * \
+				 DWC2_LS_PERIODIC_SLICES_PER_FRAME)
+
 /**
  * struct dwc2_hsotg - Holds the state of the driver, including the non-periodic
  * and periodic schedules
@@ -682,7 +760,9 @@ struct dwc2_hregs_backup {
  *                      This value is in microseconds per (micro)frame. The
  *                      assumption is that all periodic transfers may occur in
  *                      the same (micro)frame.
- * @frame_usecs:        Internal variable used by the microframe scheduler
+ * @hs_periodic_bitmap: Bitmap used by the microframe scheduler any time the
+ *                      host is in high speed mode; low speed schedules are
+ *                      stored elsewhere since we need one per TT.
  * @frame_number:       Frame number read from the core at SOF. The value ranges
  *                      from 0 to HFNUM_MAX_FRNUM.
  * @periodic_qh_count:  Count of periodic QHs, if using several eps. Used for
@@ -803,7 +883,8 @@ struct dwc2_hsotg {
 	struct list_head periodic_sched_queued;
 	struct list_head split_order;
 	u16 periodic_usecs;
-	u16 frame_usecs[8];
+	unsigned long hs_periodic_bitmap[
+		DIV_ROUND_UP(DWC2_HS_SCHEDULE_US, BITS_PER_LONG)];
 	u16 frame_number;
 	u16 periodic_qh_count;
 	bool bus_suspended;
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 83dc92da10a0..b2a74034322e 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -2252,6 +2252,90 @@ void dwc2_host_hub_info(struct dwc2_hsotg *hsotg, void *context, int *hub_addr,
 	*hub_port = urb->dev->ttport;
 }
 
+/**
+ * dwc2_host_get_tt_info() - Get the dwc2_tt associated with context
+ *
+ * This will get the dwc2_tt structure (and ttport) associated with the given
+ * context (which is really just a struct urb pointer).
+ *
+ * The first time this is called for a given TT we allocate memory for our
+ * structure.  When everyone is done and has called dwc2_host_put_tt_info()
+ * then the refcount for the structure will go to 0 and we'll free it.
+ *
+ * @hsotg:     The HCD state structure for the DWC OTG controller.
+ * @qh:        The QH structure.
+ * @context:   The priv pointer from a struct dwc2_hcd_urb.
+ * @mem_flags: Flags for allocating memory.
+ * @ttport:    We'll return this device's port number here.  That's used to
+ *             reference into the bitmap if we're on a multi_tt hub.
+ *
+ * Return: a pointer to a struct dwc2_tt.  Don't forget to call
+ *         dwc2_host_put_tt_info()!  Returns NULL upon memory alloc failure.
+ */
+
+struct dwc2_tt *dwc2_host_get_tt_info(struct dwc2_hsotg *hsotg, void *context,
+				      gfp_t mem_flags, int *ttport)
+{
+	struct urb *urb = context;
+	struct dwc2_tt *dwc_tt = NULL;
+
+	if (urb->dev->tt) {
+		*ttport = urb->dev->ttport;
+
+		dwc_tt = urb->dev->tt->hcpriv;
+		if (dwc_tt == NULL) {
+			size_t bitmap_size;
+
+			/*
+			 * For single_tt we need one schedule.  For multi_tt
+			 * we need one per port.
+			 */
+			bitmap_size = DWC2_ELEMENTS_PER_LS_BITMAP *
+				      sizeof(dwc_tt->periodic_bitmaps[0]);
+			if (urb->dev->tt->multi)
+				bitmap_size *= urb->dev->tt->hub->maxchild;
+
+			dwc_tt = kzalloc(sizeof(*dwc_tt) + bitmap_size,
+					 mem_flags);
+			if (dwc_tt == NULL)
+				return NULL;
+
+			dwc_tt->usb_tt = urb->dev->tt;
+			dwc_tt->usb_tt->hcpriv = dwc_tt;
+		}
+
+		dwc_tt->refcount++;
+	}
+
+	return dwc_tt;
+}
+
+/**
+ * dwc2_host_put_tt_info() - Put the dwc2_tt from dwc2_host_get_tt_info()
+ *
+ * Frees resources allocated by dwc2_host_get_tt_info() if all current holders
+ * of the structure are done.
+ *
+ * It's OK to call this with NULL.
+ *
+ * @hsotg:     The HCD state structure for the DWC OTG controller.
+ * @dwc_tt:    The pointer returned by dwc2_host_get_tt_info.
+ */
+void dwc2_host_put_tt_info(struct dwc2_hsotg *hsotg, struct dwc2_tt *dwc_tt)
+{
+	/* Model kfree and make put of NULL a no-op */
+	if (dwc_tt == NULL)
+		return;
+
+	WARN_ON(dwc_tt->refcount < 1);
+
+	dwc_tt->refcount--;
+	if (!dwc_tt->refcount) {
+		dwc_tt->usb_tt->hcpriv = NULL;
+		kfree(dwc_tt);
+	}
+}
+
 int dwc2_host_get_speed(struct dwc2_hsotg *hsotg, void *context)
 {
 	struct urb *urb = context;
@@ -3195,9 +3279,6 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
 		hsotg->hc_ptr_array[i] = channel;
 	}
 
-	if (hsotg->core_params->uframe_sched > 0)
-		dwc2_hcd_init_usecs(hsotg);
-
 	/* Initialize hsotg start work */
 	INIT_DELAYED_WORK(&hsotg->start_work, dwc2_hcd_start_func);
 
diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index fd266ac53a28..140b1511a131 100644
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -212,6 +212,43 @@ enum dwc2_transaction_type {
 	DWC2_TRANSACTION_ALL,
 };
 
+/* The number of elements per LS bitmap (per port on multi_tt) */
+#define DWC2_ELEMENTS_PER_LS_BITMAP	DIV_ROUND_UP(DWC2_LS_SCHEDULE_SLICES, \
+						     BITS_PER_LONG)
+
+/**
+ * struct dwc2_tt - dwc2 data associated with a usb_tt
+ *
+ * @refcount:           Number of Queue Heads (QHs) holding a reference.
+ * @usb_tt:             Pointer back to the official usb_tt.
+ * @periodic_bitmaps:   Bitmap for which parts of the 1ms frame are accounted
+ *                      for already.  Each is DWC2_ELEMENTS_PER_LS_BITMAP
+ *			elements (so sizeof(long) times that in bytes).
+ *
+ * This structure is stored in the hcpriv of the official usb_tt.
+ */
+struct dwc2_tt {
+	int refcount;
+	struct usb_tt *usb_tt;
+	unsigned long periodic_bitmaps[];
+};
+
+/**
+ * struct dwc2_hs_transfer_time - Info about a transfer on the high speed bus.
+ *
+ * @start_schedule_usecs:  The start time on the main bus schedule.  Note that
+ *                         the main bus schedule is tightly packed and this
+ *			   time should be interpreted as tightly packed (so
+ *			   uFrame 0 starts at 0 us, uFrame 1 starts at 100 us
+ *			   instead of 125 us).
+ * @duration_us:           How long this transfer goes.
+ */
+
+struct dwc2_hs_transfer_time {
+	u32 start_schedule_us;
+	u16 duration_us;
+};
+
 /**
  * struct dwc2_qh - Software queue head structure
  *
@@ -237,18 +274,33 @@ enum dwc2_transaction_type {
  * @td_first:           Index of first activated isochronous transfer descriptor
  * @td_last:            Index of last activated isochronous transfer descriptor
  * @host_us:            Bandwidth in microseconds per transfer as seen by host
+ * @device_us:          Bandwidth in microseconds per transfer as seen by device
  * @host_interval:      Interval between transfers as seen by the host.  If
  *                      the host is high speed and the device is low speed this
  *                      will be 8 times device interval.
- * @next_active_frame:  (Micro)frame before we next need to put something on
+ * @device_interval:    Interval between transfers as seen by the device.
+ *                      interval.
+ * @next_active_frame:  (Micro)frame _before_ we next need to put something on
  *                      the bus.  We'll move the qh to active here.  If the
  *                      host is in high speed mode this will be a uframe.  If
  *                      the host is in low speed mode this will be a full frame.
  * @start_active_frame: If we are partway through a split transfer, this will be
  *			what next_active_frame was when we started.  Otherwise
  *			it should always be the same as next_active_frame.
- * @assigned_uframe:    The uframe (0 -7) assigned by dwc2_find_uframe().
- * @frame_usecs:        Internal variable used by the microframe scheduler
+ * @num_hs_transfers:   Number of transfers in hs_transfers.
+ *                      Normally this is 1 but can be more than one for splits.
+ *                      Always >= 1 unless the host is in low/full speed mode.
+ * @hs_transfers:       Transfers that are scheduled as seen by the high speed
+ *                      bus.  Not used if host is in low or full speed mode (but
+ *                      note that it IS USED if the device is low or full speed
+ *                      as long as the HOST is in high speed mode).
+ * @ls_start_schedule_slice: Start time (in slices) on the low speed bus
+ *                           schedule that's being used by this device.  This
+ *			     will be on the periodic_bitmap in a
+ *                           "struct dwc2_tt".  Not used if this device is high
+ *                           speed.  Note that this is in "schedule slice" which
+ *                           is tightly packed.
+ * @ls_duration_us:     Duration on the low speed bus schedule.
  * @ntd:                Actual number of transfer descriptors in a list
  * @qtd_list:           List of QTDs for this QH
  * @channel:            Host channel currently processing transfers for this QH
@@ -261,8 +313,12 @@ enum dwc2_transaction_type {
  *                      descriptor and indicates original XferSize value for the
  *                      descriptor
  * @unreserve_timer:    Timer for releasing periodic reservation.
+ * @dwc2_tt:            Pointer to our tt info (or NULL if no tt).
+ * @ttport:             Port number within our tt.
  * @tt_buffer_dirty     True if clear_tt_buffer_complete is pending
  * @unreserve_pending:  True if we planned to unreserve but haven't yet.
+ * @schedule_low_speed: True if we have a low/full speed component (either the
+ *			host is in low/full speed mode or do_split).
  *
  * A Queue Head (QH) holds the static characteristics of an endpoint and
  * maintains a list of transfers (QTDs) for that endpoint. A QH structure may
@@ -280,11 +336,14 @@ struct dwc2_qh {
 	u8 td_first;
 	u8 td_last;
 	u16 host_us;
+	u16 device_us;
 	u16 host_interval;
+	u16 device_interval;
 	u16 next_active_frame;
 	u16 start_active_frame;
-	u16 assigned_uframe;
-	u16 frame_usecs[8];
+	s16 num_hs_transfers;
+	struct dwc2_hs_transfer_time hs_transfers[DWC2_HS_SCHEDULE_UFRAMES];
+	u32 ls_start_schedule_slice;
 	u16 ntd;
 	struct list_head qtd_list;
 	struct dwc2_host_chan *channel;
@@ -294,8 +353,11 @@ struct dwc2_qh {
 	u32 desc_list_sz;
 	u32 *n_bytes;
 	struct timer_list unreserve_timer;
+	struct dwc2_tt *dwc_tt;
+	int ttport;
 	unsigned tt_buffer_dirty:1;
 	unsigned unreserve_pending:1;
+	unsigned schedule_low_speed:1;
 };
 
 /**
@@ -462,7 +524,6 @@ extern void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg,
 
 /* Schedule Queue Functions */
 /* Implemented in hcd_queue.c */
-extern void dwc2_hcd_init_usecs(struct dwc2_hsotg *hsotg);
 extern struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
 					  struct dwc2_hcd_urb *urb,
 					  gfp_t mem_flags);
@@ -728,6 +789,12 @@ extern void dwc2_host_start(struct dwc2_hsotg *hsotg);
 extern void dwc2_host_disconnect(struct dwc2_hsotg *hsotg);
 extern void dwc2_host_hub_info(struct dwc2_hsotg *hsotg, void *context,
 			       int *hub_addr, int *hub_port);
+extern struct dwc2_tt *dwc2_host_get_tt_info(struct dwc2_hsotg *hsotg,
+					     void *context, gfp_t mem_flags,
+					     int *ttport);
+
+extern void dwc2_host_put_tt_info(struct dwc2_hsotg *hsotg,
+				  struct dwc2_tt *dwc_tt);
 extern int dwc2_host_get_speed(struct dwc2_hsotg *hsotg, void *context);
 extern void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
 			       int status);
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 7f54eeb2bf5f..83c467cd6045 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -136,116 +136,933 @@ static int dwc2_check_periodic_bandwidth(struct dwc2_hsotg *hsotg,
 }
 
 /**
- * Microframe scheduler
- * track the total use in hsotg->frame_usecs
- * keep each qh use in qh->frame_usecs
- * when surrendering the qh then donate the time back
+ * pmap_schedule() - Schedule time in a periodic bitmap (pmap).
+ *
+ * @map:             The bitmap representing the schedule; will be updated
+ *                   upon success.
+ * @bits_per_period: The schedule represents several periods.  This is how many
+ *                   bits are in each period.  It's assumed that the beginning
+ *                   of the schedule will repeat after its end.
+ * @periods_in_map:  The number of periods in the schedule.
+ * @num_bits:        The number of bits we need per period we want to reserve
+ *                   in this function call.
+ * @interval:        How often we need to be scheduled for the reservation this
+ *                   time.  1 means every period.  2 means every other period.
+ *                   ...you get the picture?
+ * @start:           The bit number to start at.  Normally 0.  Must be within
+ *                   the interval or we return failure right away.
+ * @only_one_period: Normally we'll allow picking a start anywhere within the
+ *                   first interval, since we can still make all repetition
+ *                   requirements by doing that.  However, if you pass true
+ *                   here then we'll return failure if we can't fit within
+ *                   the period that "start" is in.
+ *
+ * The idea here is that we want to schedule time for repeating events that all
+ * want the same resource.  The resource is divided into fixed-sized periods
+ * and the events want to repeat every "interval" periods.  The schedule
+ * granularity is one bit.
+ *
+ * To keep things "simple", we'll represent our schedule with a bitmap that
+ * contains a fixed number of periods.  This gets rid of a lot of complexity
+ * but does mean that we need to handle things specially (and non-ideally) if
+ * the number of the periods in the schedule doesn't match well with the
+ * intervals that we're trying to schedule.
+ *
+ * Here's an explanation of the scheme we'll implement, assuming 8 periods.
+ * - If interval is 1, we need to take up space in each of the 8
+ *   periods we're scheduling.  Easy.
+ * - If interval is 2, we need to take up space in half of the
+ *   periods.  Again, easy.
+ * - If interval is 3, we actually need to fall back to interval 1.
+ *   Why?  Because we might need time in any period.  AKA for the
+ *   first 8 periods, we'll be in slot 0, 3, 6.  Then we'll be
+ *   in slot 1, 4, 7.  Then we'll be in 2, 5.  Then we'll be back to
+ *   0, 3, and 6.  Since we could be in any frame we need to reserve
+ *   for all of them.  Sucks, but that's what you gotta do.  Note that
+ *   if we were instead scheduling 8 * 3 = 24 we'd do much better, but
+ *   then we need more memory and time to do scheduling.
+ * - If interval is 4, easy.
+ * - If interval is 5, we again need interval 1.  The schedule will be
+ *   0, 5, 2, 7, 4, 1, 6, 3, 0
+ * - If interval is 6, we need interval 2.  0, 6, 4, 2.
+ * - If interval is 7, we need interval 1.
+ * - If interval is 8, we need interval 8.
+ *
+ * If you do the math, you'll see that we need to pretend that interval is
+ * equal to the greatest_common_divisor(interval, periods_in_map).
+ *
+ * Note that at the moment this function tends to front-pack the schedule.
+ * In some cases that's really non-ideal (it's hard to schedule things that
+ * need to repeat every period).  In other cases it's perfect (you can easily
+ * schedule bigger, less often repeating things).
+ *
+ * Here's the algorithm in action (8 periods, 5 bits per period):
+ *  |**   |     |**   |     |**   |     |**   |     |   OK 2 bits, intv 2 at 0
+ *  |*****|  ***|*****|  ***|*****|  ***|*****|  ***|   OK 3 bits, intv 3 at 2
+ *  |*****|* ***|*****|  ***|*****|* ***|*****|  ***|   OK 1 bits, intv 4 at 5
+ *  |**   |*    |**   |     |**   |*    |**   |     | Remv 3 bits, intv 3 at 2
+ *  |***  |*    |***  |     |***  |*    |***  |     |   OK 1 bits, intv 6 at 2
+ *  |**** |*  * |**** |   * |**** |*  * |**** |   * |   OK 1 bits, intv 1 at 3
+ *  |**** |**** |**** | *** |**** |**** |**** | *** |   OK 2 bits, intv 2 at 6
+ *  |*****|*****|*****| ****|*****|*****|*****| ****|   OK 1 bits, intv 1 at 4
+ *  |*****|*****|*****| ****|*****|*****|*****| ****| FAIL 1 bits, intv 1
+ *  |  ***|*****|  ***| ****|  ***|*****|  ***| ****| Remv 2 bits, intv 2 at 0
+ *  |  ***| ****|  ***| ****|  ***| ****|  ***| ****| Remv 1 bits, intv 4 at 5
+ *  |   **| ****|   **| ****|   **| ****|   **| ****| Remv 1 bits, intv 6 at 2
+ *  |    *| ** *|    *| ** *|    *| ** *|    *| ** *| Remv 1 bits, intv 1 at 3
+ *  |    *|    *|    *|    *|    *|    *|    *|    *| Remv 2 bits, intv 2 at 6
+ *  |     |     |     |     |     |     |     |     | Remv 1 bits, intv 1 at 4
+ *  |**   |     |**   |     |**   |     |**   |     |   OK 2 bits, intv 2 at 0
+ *  |***  |     |**   |     |***  |     |**   |     |   OK 1 bits, intv 4 at 2
+ *  |*****|     |** **|     |*****|     |** **|     |   OK 2 bits, intv 2 at 3
+ *  |*****|*    |** **|     |*****|*    |** **|     |   OK 1 bits, intv 4 at 5
+ *  |*****|***  |** **| **  |*****|***  |** **| **  |   OK 2 bits, intv 2 at 6
+ *  |*****|*****|** **| ****|*****|*****|** **| ****|   OK 2 bits, intv 2 at 8
+ *  |*****|*****|*****| ****|*****|*****|*****| ****|   OK 1 bits, intv 4 at 12
+ *
+ * This function is pretty generic and could be easily abstracted if anything
+ * needed similar scheduling.
+ *
+ * Returns either -ENOSPC or a >= 0 start bit which should be passed to the
+ * unschedule routine.  The map bitmap will be updated on a non-error result.
  */
-static const unsigned short max_uframe_usecs[] = {
-	100, 100, 100, 100, 100, 100, 30, 0
-};
+static int pmap_schedule(unsigned long *map, int bits_per_period,
+			 int periods_in_map, int num_bits,
+			 int interval, int start, bool only_one_period)
+{
+	int interval_bits;
+	int to_reserve;
+	int first_end;
+	int i;
+
+	if (num_bits > bits_per_period)
+		return -ENOSPC;
+
+	/* Adjust interval as per description */
+	interval = gcd(interval, periods_in_map);
+
+	interval_bits = bits_per_period * interval;
+	to_reserve = periods_in_map / interval;
 
-void dwc2_hcd_init_usecs(struct dwc2_hsotg *hsotg)
+	/* If start has gotten us past interval then we can't schedule */
+	if (start >= interval_bits)
+		return -ENOSPC;
+
+	if (only_one_period)
+		/* Must fit within same period as start; end at begin of next */
+		first_end = (start / bits_per_period + 1) * bits_per_period;
+	else
+		/* Can fit anywhere in the first interval */
+		first_end = interval_bits;
+
+	/*
+	 * We'll try to pick the first repetition, then see if that time
+	 * is free for each of the subsequent repetitions.  If it's not
+	 * we'll adjust the start time for the next search of the first
+	 * repetition.
+	 */
+	while (start + num_bits <= first_end) {
+		int end;
+
+		/* Need to stay within this period */
+		end = (start / bits_per_period + 1) * bits_per_period;
+
+		/* Look for num_bits us in this microframe starting at start */
+		start = bitmap_find_next_zero_area(map, end, start, num_bits,
+						   0);
+
+		/*
+		 * We should get start >= end if we fail.  We might be
+		 * able to check the next microframe depending on the
+		 * interval, so continue on (start already updated).
+		 */
+		if (start >= end) {
+			start = end;
+			continue;
+		}
+
+		/* At this point we have a valid point for first one */
+		for (i = 1; i < to_reserve; i++) {
+			int ith_start = start + interval_bits * i;
+			int ith_end = end + interval_bits * i;
+			int ret;
+
+			/* Use this as a dumb "check if bits are 0" */
+			ret = bitmap_find_next_zero_area(
+				map, ith_start + num_bits, ith_start, num_bits,
+				0);
+
+			/* We got the right place, continue checking */
+			if (ret == ith_start)
+				continue;
+
+			/* Move start up for next time and exit for loop */
+			ith_start = bitmap_find_next_zero_area(
+				map, ith_end, ith_start, num_bits, 0);
+			if (ith_start >= ith_end)
+				/* Need a while new period next time */
+				start = end;
+			else
+				start = ith_start - interval_bits * i;
+			break;
+		}
+
+		/* If didn't exit the for loop with a break, we have success */
+		if (i == to_reserve)
+			break;
+	}
+
+	if (start + num_bits > first_end)
+		return -ENOSPC;
+
+	for (i = 0; i < to_reserve; i++) {
+		int ith_start = start + interval_bits * i;
+
+		bitmap_set(map, ith_start, num_bits);
+	}
+
+	return start;
+}
+
+/**
+ * pmap_unschedule() - Undo work done by pmap_schedule()
+ *
+ * @map:             See pmap_schedule().
+ * @bits_per_period: See pmap_schedule().
+ * @periods_in_map:  See pmap_schedule().
+ * @num_bits:        The number of bits that was passed to schedule.
+ * @interval:        The interval that was passed to schedule.
+ * @start:           The return value from pmap_schedule().
+ */
+static void pmap_unschedule(unsigned long *map, int bits_per_period,
+			    int periods_in_map, int num_bits,
+			    int interval, int start)
 {
+	int interval_bits;
+	int to_release;
 	int i;
 
-	for (i = 0; i < 8; i++)
-		hsotg->frame_usecs[i] = max_uframe_usecs[i];
+	/* Adjust interval as per description in pmap_schedule() */
+	interval = gcd(interval, periods_in_map);
+
+	interval_bits = bits_per_period * interval;
+	to_release = periods_in_map / interval;
+
+	for (i = 0; i < to_release; i++) {
+		int ith_start = start + interval_bits * i;
+
+		bitmap_clear(map, ith_start, num_bits);
+	}
 }
 
-static int dwc2_find_single_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+/*
+ * cat_printf() - A printf() + strcat() helper
+ *
+ * This is useful for concatenating a bunch of strings where each string is
+ * constructed using printf.
+ *
+ * @buf:   The destination buffer; will be updated to point after the printed
+ *         data.
+ * @size:  The number of bytes in the buffer (includes space for '\0').
+ * @fmt:   The format for printf.
+ * @...:   The args for printf.
+ */
+static void cat_printf(char **buf, size_t *size, const char *fmt, ...)
 {
-	unsigned short utime = qh->host_us;
+	va_list args;
 	int i;
 
-	for (i = 0; i < 8; i++) {
-		/* At the start hsotg->frame_usecs[i] = max_uframe_usecs[i] */
-		if (utime <= hsotg->frame_usecs[i]) {
-			hsotg->frame_usecs[i] -= utime;
-			qh->frame_usecs[i] += utime;
-			return i;
-		}
+	if (*size == 0)
+		return;
+
+	va_start(args, fmt);
+	i = vsnprintf(*buf, *size, fmt, args);
+	va_end(args);
+
+	if (i >= *size) {
+		*buf[*size - 1] = '\0';
+		*buf += *size;
+		*size = 0;
+	} else {
+		*buf += i;
+		*size -= i;
 	}
-	return -ENOSPC;
 }
 
 /*
- * use this for FS apps that can span multiple uframes
+ * pmap_print() - Print the given periodic map
+ *
+ * Will attempt to print out the periodic schedule.
+ *
+ * @map:             See pmap_schedule().
+ * @bits_per_period: See pmap_schedule().
+ * @periods_in_map:  See pmap_schedule().
+ * @period_name:     The name of 1 period, like "uFrame"
+ * @units:           The name of the units, like "us".
+ * @print_fn:        The function to call for printing.
+ * @print_data:      Opaque data to pass to the print function.
+ */
+static void pmap_print(unsigned long *map, int bits_per_period,
+		       int periods_in_map, const char *period_name,
+		       const char *units,
+		       void (*print_fn)(const char *str, void *data),
+		       void *print_data)
+{
+	int period;
+
+	for (period = 0; period < periods_in_map; period++) {
+		char tmp[64];
+		char *buf = tmp;
+		size_t buf_size = sizeof(tmp);
+		int period_start = period * bits_per_period;
+		int period_end = period_start + bits_per_period;
+		int start = 0;
+		int count = 0;
+		bool printed = false;
+		int i;
+
+		for (i = period_start; i < period_end + 1; i++) {
+			/* Handle case when ith bit is set */
+			if (i < period_end &&
+			    bitmap_find_next_zero_area(map, i + 1,
+						       i, 1, 0) != i) {
+				if (count == 0)
+					start = i - period_start;
+				count++;
+				continue;
+			}
+
+			/* ith bit isn't set; don't care if count == 0 */
+			if (count == 0)
+				continue;
+
+			if (!printed)
+				cat_printf(&buf, &buf_size, "%s %d: ",
+					   period_name, period);
+			else
+				cat_printf(&buf, &buf_size, ", ");
+			printed = true;
+
+			cat_printf(&buf, &buf_size, "%d %s -%3d %s", start,
+				   units, start + count - 1, units);
+			count = 0;
+		}
+
+		if (printed)
+			print_fn(tmp, print_data);
+	}
+}
+
+/**
+ * dwc2_get_ls_map() - Get the map used for the given qh
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller.
+ * @qh:    QH for the periodic transfer.
+ *
+ * We'll always get the periodic map out of our TT.  Note that even if we're
+ * running the host straight in low speed / full speed mode it appears as if
+ * a TT is allocated for us, so we'll use it.  If that ever changes we can
+ * add logic here to get a map out of "hsotg" if !qh->do_split.
+ *
+ * Returns: the map or NULL if a map couldn't be found.
+ */
+static unsigned long *dwc2_get_ls_map(struct dwc2_hsotg *hsotg,
+				      struct dwc2_qh *qh)
+{
+	unsigned long *map;
+
+	/* Don't expect to be missing a TT and be doing low speed scheduling */
+	if (WARN_ON(!qh->dwc_tt))
+		return NULL;
+
+	/* Get the map and adjust if this is a multi_tt hub */
+	map = qh->dwc_tt->periodic_bitmaps;
+	if (qh->dwc_tt->usb_tt->multi)
+		map += DWC2_ELEMENTS_PER_LS_BITMAP * qh->ttport;
+
+	return map;
+}
+
+struct dwc2_qh_print_data {
+	struct dwc2_hsotg *hsotg;
+	struct dwc2_qh *qh;
+};
+
+/**
+ * dwc2_qh_print() - Helper function for dwc2_qh_schedule_print()
+ *
+ * @str:  The string to print
+ * @data: A pointer to a struct dwc2_qh_print_data
  */
-static int dwc2_find_multi_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+static void dwc2_qh_print(const char *str, void *data)
 {
-	unsigned short utime = qh->host_us;
-	unsigned short xtime;
-	int t_left;
+	struct dwc2_qh_print_data *print_data = data;
+
+	dwc2_sch_dbg(print_data->hsotg, "QH=%p ...%s\n", print_data->qh, str);
+}
+
+/**
+ * dwc2_qh_schedule_print() - Print the periodic schedule
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller.
+ * @qh:    QH to print.
+ */
+static void dwc2_qh_schedule_print(struct dwc2_hsotg *hsotg,
+				   struct dwc2_qh *qh)
+{
+	struct dwc2_qh_print_data print_data = { hsotg, qh };
 	int i;
-	int j;
-	int k;
 
-	for (i = 0; i < 8; i++) {
-		if (hsotg->frame_usecs[i] <= 0)
+	/*
+	 * The printing functions are quite slow and inefficient.
+	 * If we don't have tracing turned on, don't run unless the special
+	 * define is turned on.
+	 */
+#ifndef DWC2_PRINT_SCHEDULE
+	return;
+#endif
+
+	if (qh->schedule_low_speed) {
+		unsigned long *map = dwc2_get_ls_map(hsotg, qh);
+
+		dwc2_sch_dbg(hsotg, "QH=%p LS/FS trans: %d=>%d us @ %d us",
+			     qh, qh->device_us,
+			     DWC2_ROUND_US_TO_SLICE(qh->device_us),
+			     DWC2_US_PER_SLICE * qh->ls_start_schedule_slice);
+
+		if (map) {
+			dwc2_sch_dbg(hsotg,
+				     "QH=%p Whole low/full speed map %p now:\n",
+				     qh, map);
+			pmap_print(map, DWC2_LS_PERIODIC_SLICES_PER_FRAME,
+				   DWC2_LS_SCHEDULE_FRAMES, "Frame ", "slices",
+				   dwc2_qh_print, &print_data);
+		}
+	}
+
+	for (i = 0; i < qh->num_hs_transfers; i++) {
+		struct dwc2_hs_transfer_time *trans_time = qh->hs_transfers + i;
+		int uframe = trans_time->start_schedule_us /
+			     DWC2_HS_PERIODIC_US_PER_UFRAME;
+		int rel_us = trans_time->start_schedule_us %
+			     DWC2_HS_PERIODIC_US_PER_UFRAME;
+
+		dwc2_sch_dbg(hsotg,
+			     "QH=%p HS trans #%d: %d us @ uFrame %d + %d us\n",
+			     qh, i, trans_time->duration_us, uframe, rel_us);
+	}
+	if (qh->num_hs_transfers) {
+		dwc2_sch_dbg(hsotg, "QH=%p Whole high speed map now:\n", qh);
+		pmap_print(hsotg->hs_periodic_bitmap,
+			   DWC2_HS_PERIODIC_US_PER_UFRAME,
+			   DWC2_HS_SCHEDULE_UFRAMES, "uFrame", "us",
+			   dwc2_qh_print, &print_data);
+	}
+
+}
+
+/**
+ * dwc2_ls_pmap_schedule() - Schedule a low speed QH
+ *
+ * @hsotg:        The HCD state structure for the DWC OTG controller.
+ * @qh:           QH for the periodic transfer.
+ * @search_slice: We'll start trying to schedule at the passed slice.
+ *                Remember that slices are the units of the low speed
+ *                schedule (think 25us or so).
+ *
+ * Wraps pmap_schedule() with the right parameters for low speed scheduling.
+ *
+ * Normally we schedule low speed devices on the map associated with the TT.
+ *
+ * Returns: 0 for success or an error code.
+ */
+static int dwc2_ls_pmap_schedule(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
+				 int search_slice)
+{
+	int slices = DIV_ROUND_UP(qh->device_us, DWC2_US_PER_SLICE);
+	unsigned long *map = dwc2_get_ls_map(hsotg, qh);
+	int slice;
+
+	if (map == NULL)
+		return -EINVAL;
+
+	/*
+	 * Schedule on the proper low speed map with our low speed scheduling
+	 * parameters.  Note that we use the "device_interval" here since
+	 * we want the low speed interval and the only way we'd be in this
+	 * function is if the device is low speed.
+	 *
+	 * If we happen to be doing low speed and high speed scheduling for the
+	 * same transaction (AKA we have a split) we always do low speed first.
+	 * That means we can always pass "false" for only_one_period (that
+	 * parameters is only useful when we're trying to get one schedule to
+	 * match what we already planned in the other schedule).
+	 */
+	slice = pmap_schedule(map, DWC2_LS_PERIODIC_SLICES_PER_FRAME,
+			      DWC2_LS_SCHEDULE_FRAMES, slices,
+			      qh->device_interval, search_slice, false);
+
+	if (slice < 0)
+		return slice;
+
+	qh->ls_start_schedule_slice = slice;
+	return 0;
+}
+
+/**
+ * dwc2_ls_pmap_unschedule() - Undo work done by dwc2_ls_pmap_schedule()
+ *
+ * @hsotg:       The HCD state structure for the DWC OTG controller.
+ * @qh:          QH for the periodic transfer.
+ */
+static void dwc2_ls_pmap_unschedule(struct dwc2_hsotg *hsotg,
+				    struct dwc2_qh *qh)
+{
+	int slices = DIV_ROUND_UP(qh->device_us, DWC2_US_PER_SLICE);
+	unsigned long *map = dwc2_get_ls_map(hsotg, qh);
+
+	/* Schedule should have failed, so no worries about no error code */
+	if (map == NULL)
+		return;
+
+	pmap_unschedule(map, DWC2_LS_PERIODIC_SLICES_PER_FRAME,
+			DWC2_LS_SCHEDULE_FRAMES, slices, qh->device_interval,
+			qh->ls_start_schedule_slice);
+}
+
+/**
+ * dwc2_hs_pmap_schedule - Schedule in the main high speed schedule
+ *
+ * This will schedule something on the main dwc2 schedule.
+ *
+ * We'll start looking in qh->hs_transfers[index].start_schedule_us.  We'll
+ * update this with the result upon success.  We also use the duration from
+ * the same structure.
+ *
+ * @hsotg:           The HCD state structure for the DWC OTG controller.
+ * @qh:              QH for the periodic transfer.
+ * @only_one_period: If true we will limit ourselves to just looking at
+ *                   one period (aka one 100us chunk).  This is used if we have
+ *                   already scheduled something on the low speed schedule and
+ *                   need to find something that matches on the high speed one.
+ * @index:           The index into qh->hs_transfers that we're working with.
+ *
+ * Returns: 0 for success or an error code.  Upon success the
+ *          dwc2_hs_transfer_time specified by "index" will be updated.
+ */
+static int dwc2_hs_pmap_schedule(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
+				 bool only_one_period, int index)
+{
+	struct dwc2_hs_transfer_time *trans_time = qh->hs_transfers + index;
+	int us;
+
+	us = pmap_schedule(hsotg->hs_periodic_bitmap,
+			   DWC2_HS_PERIODIC_US_PER_UFRAME,
+			   DWC2_HS_SCHEDULE_UFRAMES, trans_time->duration_us,
+			   qh->host_interval, trans_time->start_schedule_us,
+			   only_one_period);
+
+	if (us < 0)
+		return us;
+
+	trans_time->start_schedule_us = us;
+	return 0;
+}
+
+/**
+ * dwc2_ls_pmap_unschedule() - Undo work done by dwc2_hs_pmap_schedule()
+ *
+ * @hsotg:       The HCD state structure for the DWC OTG controller.
+ * @qh:          QH for the periodic transfer.
+ */
+static void dwc2_hs_pmap_unschedule(struct dwc2_hsotg *hsotg,
+				    struct dwc2_qh *qh, int index)
+{
+	struct dwc2_hs_transfer_time *trans_time = qh->hs_transfers + index;
+
+	pmap_unschedule(hsotg->hs_periodic_bitmap,
+			DWC2_HS_PERIODIC_US_PER_UFRAME,
+			DWC2_HS_SCHEDULE_UFRAMES, trans_time->duration_us,
+			qh->host_interval, trans_time->start_schedule_us);
+}
+
+/**
+ * dwc2_uframe_schedule_split - Schedule a QH for a periodic split xfer.
+ *
+ * This is the most complicated thing in USB.  We have to find matching time
+ * in both the global high speed schedule for the port and the low speed
+ * schedule for the TT associated with the given device.
+ *
+ * Being here means that the host must be running in high speed mode and the
+ * device is in low or full speed mode (and behind a hub).
+ *
+ * @hsotg:       The HCD state structure for the DWC OTG controller.
+ * @qh:          QH for the periodic transfer.
+ */
+static int dwc2_uframe_schedule_split(struct dwc2_hsotg *hsotg,
+				      struct dwc2_qh *qh)
+{
+	int bytecount = dwc2_hb_mult(qh->maxp) * dwc2_max_packet(qh->maxp);
+	int ls_search_slice;
+	int err = 0;
+	int host_interval_in_sched;
+
+	/*
+	 * The interval (how often to repeat) in the actual host schedule.
+	 * See pmap_schedule() for gcd() explanation.
+	 */
+	host_interval_in_sched = gcd(qh->host_interval,
+				     DWC2_HS_SCHEDULE_UFRAMES);
+
+	/*
+	 * We always try to find space in the low speed schedule first, then
+	 * try to find high speed time that matches.  If we don't, we'll bump
+	 * up the place we start searching in the low speed schedule and try
+	 * again.  To start we'll look right at the beginning of the low speed
+	 * schedule.
+	 *
+	 * Note that this will tend to front-load the high speed schedule.
+	 * We may eventually want to try to avoid this by either considering
+	 * both schedules together or doing some sort of round robin.
+	 */
+	ls_search_slice = 0;
+
+	while (ls_search_slice < DWC2_LS_SCHEDULE_SLICES) {
+		int start_s_uframe;
+		int ssplit_s_uframe;
+		int second_s_uframe;
+		int rel_uframe;
+		int first_count;
+		int middle_count;
+		int end_count;
+		int first_data_bytes;
+		int other_data_bytes;
+		int i;
+
+		if (qh->schedule_low_speed) {
+			err = dwc2_ls_pmap_schedule(hsotg, qh, ls_search_slice);
+
+			/*
+			 * If we got an error here there's no other magic we
+			 * can do, so bail.  All the looping above is only
+			 * helpful to redo things if we got a low speed slot
+			 * and then couldn't find a matching high speed slot.
+			 */
+			if (err)
+				return err;
+		} else {
+			/* Must be missing the tt structure?  Why? */
+			WARN_ON_ONCE(1);
+		}
+
+		/*
+		 * This will give us a number 0 - 7 if
+		 * DWC2_LS_SCHEDULE_FRAMES == 1, or 0 - 15 if == 2, or ...
+		 */
+		start_s_uframe = qh->ls_start_schedule_slice /
+				 DWC2_SLICES_PER_UFRAME;
+
+		/* Get a number that's always 0 - 7 */
+		rel_uframe = (start_s_uframe % 8);
+
+		/*
+		 * If we were going to start in uframe 7 then we would need to
+		 * issue a start split in uframe 6, which spec says is not OK.
+		 * Move on to the next full frame (assuming there is one).
+		 *
+		 * See 11.18.4 Host Split Transaction Scheduling Requirements
+		 * bullet 1.
+		 */
+		if (rel_uframe == 7) {
+			if (qh->schedule_low_speed)
+				dwc2_ls_pmap_unschedule(hsotg, qh);
+			ls_search_slice =
+				(qh->ls_start_schedule_slice /
+				 DWC2_LS_PERIODIC_SLICES_PER_FRAME + 1) *
+				DWC2_LS_PERIODIC_SLICES_PER_FRAME;
 			continue;
+		}
 
 		/*
-		 * we need n consecutive slots so use j as a start slot
-		 * j plus j+1 must be enough time (for now)
+		 * For ISOC in:
+		 * - start split            (frame -1)
+		 * - complete split w/ data (frame +1)
+		 * - complete split w/ data (frame +2)
+		 * - ...
+		 * - complete split w/ data (frame +num_data_packets)
+		 * - complete split w/ data (frame +num_data_packets+1)
+		 * - complete split w/ data (frame +num_data_packets+2, max 8)
+		 *   ...though if frame was "0" then max is 7...
+		 *
+		 * For ISOC out we might need to do:
+		 * - start split w/ data    (frame -1)
+		 * - start split w/ data    (frame +0)
+		 * - ...
+		 * - start split w/ data    (frame +num_data_packets-2)
+		 *
+		 * For INTERRUPT in we might need to do:
+		 * - start split            (frame -1)
+		 * - complete split w/ data (frame +1)
+		 * - complete split w/ data (frame +2)
+		 * - complete split w/ data (frame +3, max 8)
+		 *
+		 * For INTERRUPT out we might need to do:
+		 * - start split w/ data    (frame -1)
+		 * - complete split         (frame +1)
+		 * - complete split         (frame +2)
+		 * - complete split         (frame +3, max 8)
+		 *
+		 * Start adjusting!
 		 */
-		xtime = hsotg->frame_usecs[i];
-		for (j = i + 1; j < 8; j++) {
-			/*
-			 * if we add this frame remaining time to xtime we may
-			 * be OK, if not we need to test j for a complete frame
-			 */
-			if (xtime + hsotg->frame_usecs[j] < utime) {
-				if (hsotg->frame_usecs[j] <
-							max_uframe_usecs[j])
-					continue;
+		ssplit_s_uframe = (start_s_uframe +
+				   host_interval_in_sched - 1) %
+				  host_interval_in_sched;
+		if (qh->ep_type == USB_ENDPOINT_XFER_ISOC && !qh->ep_is_in)
+			second_s_uframe = start_s_uframe;
+		else
+			second_s_uframe = start_s_uframe + 1;
+
+		/* First data transfer might not be all 188 bytes. */
+		first_data_bytes = 188 -
+			DIV_ROUND_UP(188 * (qh->ls_start_schedule_slice %
+					    DWC2_SLICES_PER_UFRAME),
+				     DWC2_SLICES_PER_UFRAME);
+		if (first_data_bytes > bytecount)
+			first_data_bytes = bytecount;
+		other_data_bytes = bytecount - first_data_bytes;
+
+		/*
+		 * For now, skip OUT xfers where first xfer is partial
+		 *
+		 * Main dwc2 code assumes:
+		 * - INT transfers never get split in two.
+		 * - ISOC transfers can always transfer 188 bytes the first
+		 *   time.
+		 *
+		 * Until that code is fixed, try again if the first transfer
+		 * couldn't transfer everything.
+		 *
+		 * This code can be removed if/when the rest of dwc2 handles
+		 * the above cases.  Until it's fixed we just won't be able
+		 * to schedule quite as tightly.
+		 */
+		if (!qh->ep_is_in &&
+		    (first_data_bytes != min_t(int, 188, bytecount))) {
+			dwc2_sch_dbg(hsotg,
+				     "QH=%p avoiding broken 1st xfer (%d, %d)\n",
+				     qh, first_data_bytes, bytecount);
+			if (qh->schedule_low_speed)
+				dwc2_ls_pmap_unschedule(hsotg, qh);
+			ls_search_slice = (start_s_uframe + 1) *
+				DWC2_SLICES_PER_UFRAME;
+			continue;
+		}
+
+		/* Start by assuming transfers for the bytes */
+		qh->num_hs_transfers = 1 + DIV_ROUND_UP(other_data_bytes, 188);
+
+		/*
+		 * Everything except ISOC OUT has extra transfers.  Rules are
+		 * complicated.  See 11.18.4 Host Split Transaction Scheduling
+		 * Requirements bullet 3.
+		 */
+		if (qh->ep_type == USB_ENDPOINT_XFER_INT) {
+			if (rel_uframe == 6)
+				qh->num_hs_transfers += 2;
+			else
+				qh->num_hs_transfers += 3;
+
+			if (qh->ep_is_in) {
+				/*
+				 * First is start split, middle/end is data.
+				 * Allocate full data bytes for all data.
+				 */
+				first_count = 4;
+				middle_count = bytecount;
+				end_count = bytecount;
+			} else {
+				/*
+				 * First is data, middle/end is complete.
+				 * First transfer and second can have data.
+				 * Rest should just have complete split.
+				 */
+				first_count = first_data_bytes;
+				middle_count = max_t(int, 4, other_data_bytes);
+				end_count = 4;
 			}
-			if (xtime >= utime) {
-				t_left = utime;
-				for (k = i; k < 8; k++) {
-					t_left -= hsotg->frame_usecs[k];
-					if (t_left <= 0) {
-						qh->frame_usecs[k] +=
-							hsotg->frame_usecs[k]
-								+ t_left;
-						hsotg->frame_usecs[k] = -t_left;
-						return i;
-					} else {
-						qh->frame_usecs[k] +=
-							hsotg->frame_usecs[k];
-						hsotg->frame_usecs[k] = 0;
-					}
-				}
+		} else {
+			if (qh->ep_is_in) {
+				int last;
+
+				/* Account for the start split */
+				qh->num_hs_transfers++;
+
+				/* Calculate "L" value from spec */
+				last = rel_uframe + qh->num_hs_transfers + 1;
+
+				/* Start with basic case */
+				if (last <= 6)
+					qh->num_hs_transfers += 2;
+				else
+					qh->num_hs_transfers += 1;
+
+				/* Adjust downwards */
+				if (last >= 6 && rel_uframe == 0)
+					qh->num_hs_transfers--;
+
+				/* 1st = start; rest can contain data */
+				first_count = 4;
+				middle_count = min_t(int, 188, bytecount);
+				end_count = middle_count;
+			} else {
+				/* All contain data, last might be smaller */
+				first_count = first_data_bytes;
+				middle_count = min_t(int, 188,
+						     other_data_bytes);
+				end_count = other_data_bytes % 188;
 			}
-			/* add the frame time to x time */
-			xtime += hsotg->frame_usecs[j];
-			/* we must have a fully available next frame or break */
-			if (xtime < utime &&
-			   hsotg->frame_usecs[j] == max_uframe_usecs[j])
-				continue;
 		}
+
+		/* Assign durations per uFrame */
+		qh->hs_transfers[0].duration_us = HS_USECS_ISO(first_count);
+		for (i = 1; i < qh->num_hs_transfers - 1; i++)
+			qh->hs_transfers[i].duration_us =
+				HS_USECS_ISO(middle_count);
+		if (qh->num_hs_transfers > 1)
+			qh->hs_transfers[qh->num_hs_transfers - 1].duration_us =
+				HS_USECS_ISO(end_count);
+
+		/*
+		 * Assign start us.  The call below to dwc2_hs_pmap_schedule()
+		 * will start with these numbers but may adjust within the same
+		 * microframe.
+		 */
+		qh->hs_transfers[0].start_schedule_us =
+			ssplit_s_uframe * DWC2_HS_PERIODIC_US_PER_UFRAME;
+		for (i = 1; i < qh->num_hs_transfers; i++)
+			qh->hs_transfers[i].start_schedule_us =
+				((second_s_uframe + i - 1) %
+				 DWC2_HS_SCHEDULE_UFRAMES) *
+				DWC2_HS_PERIODIC_US_PER_UFRAME;
+
+		/* Try to schedule with filled in hs_transfers above */
+		for (i = 0; i < qh->num_hs_transfers; i++) {
+			err = dwc2_hs_pmap_schedule(hsotg, qh, true, i);
+			if (err)
+				break;
+		}
+
+		/* If we scheduled all w/out breaking out then we're all good */
+		if (i == qh->num_hs_transfers)
+			break;
+
+		for (; i >= 0; i--)
+			dwc2_hs_pmap_unschedule(hsotg, qh, i);
+
+		if (qh->schedule_low_speed)
+			dwc2_ls_pmap_unschedule(hsotg, qh);
+
+		/* Try again starting in the next microframe */
+		ls_search_slice = (start_s_uframe + 1) * DWC2_SLICES_PER_UFRAME;
 	}
-	return -ENOSPC;
+
+	if (ls_search_slice >= DWC2_LS_SCHEDULE_SLICES)
+		return -ENOSPC;
+
+	return 0;
 }
 
-static int dwc2_find_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+/**
+ * dwc2_uframe_schedule_hs - Schedule a QH for a periodic high speed xfer.
+ *
+ * Basically this just wraps dwc2_hs_pmap_schedule() to provide a clean
+ * interface.
+ *
+ * @hsotg:       The HCD state structure for the DWC OTG controller.
+ * @qh:          QH for the periodic transfer.
+ */
+static int dwc2_uframe_schedule_hs(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+	/* In non-split host and device time are the same */
+	WARN_ON(qh->host_us != qh->device_us);
+	WARN_ON(qh->host_interval != qh->device_interval);
+	WARN_ON(qh->num_hs_transfers != 1);
+
+	/* We'll have one transfer; init start to 0 before calling scheduler */
+	qh->hs_transfers[0].start_schedule_us = 0;
+	qh->hs_transfers[0].duration_us = qh->host_us;
+
+	return dwc2_hs_pmap_schedule(hsotg, qh, false, 0);
+}
+
+/**
+ * dwc2_uframe_schedule_ls - Schedule a QH for a periodic low/full speed xfer.
+ *
+ * Basically this just wraps dwc2_ls_pmap_schedule() to provide a clean
+ * interface.
+ *
+ * @hsotg:       The HCD state structure for the DWC OTG controller.
+ * @qh:          QH for the periodic transfer.
+ */
+static int dwc2_uframe_schedule_ls(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+	/* In non-split host and device time are the same */
+	WARN_ON(qh->host_us != qh->device_us);
+	WARN_ON(qh->host_interval != qh->device_interval);
+	WARN_ON(!qh->schedule_low_speed);
+
+	/* Run on the main low speed schedule (no split = no hub = no TT) */
+	return dwc2_ls_pmap_schedule(hsotg, qh, 0);
+}
+
+/**
+ * dwc2_uframe_schedule - Schedule a QH for a periodic xfer.
+ *
+ * Calls one of the 3 sub-function depending on what type of transfer this QH
+ * is for.  Also adds some printing.
+ *
+ * @hsotg:       The HCD state structure for the DWC OTG controller.
+ * @qh:          QH for the periodic transfer.
+ */
+static int dwc2_uframe_schedule(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
 	int ret;
 
-	if (qh->dev_speed == USB_SPEED_HIGH) {
-		/* if this is a hs transaction we need a full frame */
-		ret = dwc2_find_single_uframe(hsotg, qh);
-	} else {
-		/*
-		 * if this is a fs transaction we may need a sequence
-		 * of frames
-		 */
-		ret = dwc2_find_multi_uframe(hsotg, qh);
-	}
+	if (qh->dev_speed == USB_SPEED_HIGH)
+		ret = dwc2_uframe_schedule_hs(hsotg, qh);
+	else if (!qh->do_split)
+		ret = dwc2_uframe_schedule_ls(hsotg, qh);
+	else
+		ret = dwc2_uframe_schedule_split(hsotg, qh);
+
+	if (ret)
+		dwc2_sch_dbg(hsotg, "QH=%p Failed to schedule %d\n", qh, ret);
+	else
+		dwc2_qh_schedule_print(hsotg, qh);
+
 	return ret;
 }
 
 /**
+ * dwc2_uframe_unschedule - Undoes dwc2_uframe_schedule().
+ *
+ * @hsotg:       The HCD state structure for the DWC OTG controller.
+ * @qh:          QH for the periodic transfer.
+ */
+static void dwc2_uframe_unschedule(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+	int i;
+
+	for (i = 0; i < qh->num_hs_transfers; i++)
+		dwc2_hs_pmap_unschedule(hsotg, qh, i);
+
+	if (qh->schedule_low_speed)
+		dwc2_ls_pmap_unschedule(hsotg, qh);
+
+	dwc2_sch_dbg(hsotg, "QH=%p Unscheduled\n", qh);
+}
+
+/**
  * dwc2_pick_first_frame() - Choose 1st frame for qh that's already scheduled
  *
  * Takes a qh that has already been scheduled (which means we know we have the
@@ -265,6 +1082,7 @@ static void dwc2_pick_first_frame(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 	u16 frame_number;
 	u16 earliest_frame;
 	u16 next_active_frame;
+	u16 relative_frame;
 	u16 interval;
 
 	/*
@@ -292,8 +1110,36 @@ static void dwc2_pick_first_frame(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		goto exit;
 	}
 
-	/* Adjust interval as per high speed schedule which has 8 uFrame */
-	interval = gcd(qh->host_interval, 8);
+	if (qh->dev_speed == USB_SPEED_HIGH || qh->do_split) {
+		/*
+		 * We're either at high speed or we're doing a split (which
+		 * means we're talking high speed to a hub).  In any case
+		 * the first frame should be based on when the first scheduled
+		 * event is.
+		 */
+		WARN_ON(qh->num_hs_transfers < 1);
+
+		relative_frame = qh->hs_transfers[0].start_schedule_us /
+				 DWC2_HS_PERIODIC_US_PER_UFRAME;
+
+		/* Adjust interval as per high speed schedule */
+		interval = gcd(qh->host_interval, DWC2_HS_SCHEDULE_UFRAMES);
+
+	} else {
+		/*
+		 * Low or full speed directly on dwc2.  Just about the same
+		 * as high speed but on a different schedule and with slightly
+		 * different adjustments.  Note that this works because when
+		 * the host and device are both low speed then frames in the
+		 * controller tick at low speed.
+		 */
+		relative_frame = qh->ls_start_schedule_slice /
+				 DWC2_LS_PERIODIC_SLICES_PER_FRAME;
+		interval = gcd(qh->host_interval, DWC2_LS_SCHEDULE_FRAMES);
+	}
+
+	/* Scheduler messed up if frame is past interval */
+	WARN_ON(relative_frame >= interval);
 
 	/*
 	 * We know interval must divide (HFNUM_MAX_FRNUM + 1) now that we've
@@ -310,7 +1156,7 @@ static void dwc2_pick_first_frame(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 	 * scheduled for.
 	 */
 	next_active_frame = dwc2_frame_num_inc(next_active_frame,
-					       qh->assigned_uframe);
+					       relative_frame);
 
 	/*
 	 * We actually need 1 frame before since the next_active_frame is
@@ -350,21 +1196,23 @@ static int dwc2_do_reserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
 	int status;
 
-	if (hsotg->core_params->uframe_sched > 0) {
-		status = dwc2_find_uframe(hsotg, qh);
-		if (status >= 0)
-			qh->assigned_uframe = status;
-	} else {
-		status = dwc2_periodic_channel_available(hsotg);
-		if (status) {
-			dev_info(hsotg->dev,
-				 "%s: No host channel available for periodic transfer\n",
-				 __func__);
-			return status;
-		}
+	/*
+	 * NOTE: need to figure out if we're using the microframe scheduler
+	 * whether can we actually use more channels as long as they're not
+	 * all active in the same microframe.  For now, don't allow.
+	 */
+	status = dwc2_periodic_channel_available(hsotg);
+	if (status) {
+		dev_info(hsotg->dev,
+			"%s: No host channel available for periodic transfer\n",
+			__func__);
+		return status;
+	}
 
+	if (hsotg->core_params->uframe_sched <= 0)
 		status = dwc2_check_periodic_bandwidth(hsotg, qh);
-	}
+	else
+		status = dwc2_uframe_schedule(hsotg, qh);
 
 	if (status) {
 		dev_dbg(hsotg->dev,
@@ -373,9 +1221,8 @@ static int dwc2_do_reserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		return status;
 	}
 
-	if (hsotg->core_params->uframe_sched <= 0)
-		/* Reserve periodic channel */
-		hsotg->periodic_channels++;
+	/* Reserve periodic channel */
+	hsotg->periodic_channels++;
 
 	/* Update claimed usecs per (micro)frame */
 	hsotg->periodic_usecs += qh->host_us;
@@ -409,17 +1256,13 @@ static void dwc2_do_unreserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 	/* Update claimed usecs per (micro)frame */
 	hsotg->periodic_usecs -= qh->host_us;
 
-	if (hsotg->core_params->uframe_sched > 0) {
-		int i;
+	/* Release periodic channel reservation */
+	hsotg->periodic_channels--;
 
-		for (i = 0; i < 8; i++) {
-			hsotg->frame_usecs[i] += qh->frame_usecs[i];
-			qh->frame_usecs[i] = 0;
-		}
-	} else {
-		/* Release periodic channel reservation */
-		hsotg->periodic_channels--;
-	}
+	if (hsotg->core_params->uframe_sched <= 0)
+		return;
+
+	dwc2_uframe_unschedule(hsotg, qh);
 }
 
 /**
@@ -604,88 +1447,81 @@ static void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg,
  * @qh:    The QH to init
  * @urb:   Holds the information about the device/endpoint needed to initialize
  *         the QH
+ * @mem_flags: Flags for allocating memory.
  */
 static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
-			 struct dwc2_hcd_urb *urb)
+			 struct dwc2_hcd_urb *urb, gfp_t mem_flags)
 {
-	int dev_speed, hub_addr, hub_port;
+	int dev_speed = dwc2_host_get_speed(hsotg, urb->priv);
+	u8 ep_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
+	bool ep_is_in = !!dwc2_hcd_is_pipe_in(&urb->pipe_info);
+	bool ep_is_isoc = (ep_type == USB_ENDPOINT_XFER_ISOC);
+	bool ep_is_int = (ep_type == USB_ENDPOINT_XFER_INT);
+	u32 hprt = dwc2_readl(hsotg->regs + HPRT0);
+	u32 prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
+	bool do_split = (prtspd == HPRT0_SPD_HIGH_SPEED &&
+			 dev_speed != USB_SPEED_HIGH);
+	int maxp = dwc2_hcd_get_mps(&urb->pipe_info);
+	int bytecount = dwc2_hb_mult(maxp) * dwc2_max_packet(maxp);
 	char *speed, *type;
 
-	dev_vdbg(hsotg->dev, "%s()\n", __func__);
-
 	/* Initialize QH */
 	qh->hsotg = hsotg;
 	setup_timer(&qh->unreserve_timer, dwc2_unreserve_timer_fn,
 		    (unsigned long)qh);
-	qh->ep_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
-	qh->ep_is_in = dwc2_hcd_is_pipe_in(&urb->pipe_info) ? 1 : 0;
+	qh->ep_type = ep_type;
+	qh->ep_is_in = ep_is_in;
 
 	qh->data_toggle = DWC2_HC_PID_DATA0;
-	qh->maxp = dwc2_hcd_get_mps(&urb->pipe_info);
+	qh->maxp = maxp;
 	INIT_LIST_HEAD(&qh->qtd_list);
 	INIT_LIST_HEAD(&qh->qh_list_entry);
 
-	/* FS/LS Endpoint on HS Hub, NOT virtual root hub */
-	dev_speed = dwc2_host_get_speed(hsotg, urb->priv);
+	qh->do_split = do_split;
+	qh->dev_speed = dev_speed;
 
-	dwc2_host_hub_info(hsotg, urb->priv, &hub_addr, &hub_port);
+	if (ep_is_int || ep_is_isoc) {
+		/* Compute scheduling parameters once and save them */
+		int host_speed = do_split ? USB_SPEED_HIGH : dev_speed;
+		struct dwc2_tt *dwc_tt = dwc2_host_get_tt_info(hsotg, urb->priv,
+							       mem_flags,
+							       &qh->ttport);
+		int device_ns;
 
-	if ((dev_speed == USB_SPEED_LOW || dev_speed == USB_SPEED_FULL) &&
-	    hub_addr != 0 && hub_addr != 1) {
-		dev_vdbg(hsotg->dev,
-			 "QH init: EP %d: TT found at hub addr %d, for port %d\n",
-			 dwc2_hcd_get_ep_num(&urb->pipe_info), hub_addr,
-			 hub_port);
-		qh->do_split = 1;
-	}
+		qh->dwc_tt = dwc_tt;
 
-	if (qh->ep_type == USB_ENDPOINT_XFER_INT ||
-	    qh->ep_type == USB_ENDPOINT_XFER_ISOC) {
-		/* Compute scheduling parameters once and save them */
-		u32 hprt, prtspd;
-
-		/* Todo: Account for split transfers in the bus time */
-		int bytecount =
-			dwc2_hb_mult(qh->maxp) * dwc2_max_packet(qh->maxp);
-
-		qh->host_us = NS_TO_US(usb_calc_bus_time(qh->do_split ?
-			      USB_SPEED_HIGH : dev_speed, qh->ep_is_in,
-			      qh->ep_type == USB_ENDPOINT_XFER_ISOC,
-			      bytecount));
-
-		qh->host_interval = urb->interval;
-		dwc2_sch_dbg(hsotg, "QH=%p init nxt=%04x, fn=%04x, int=%#x\n",
-			     qh, qh->next_active_frame, hsotg->frame_number,
-			     qh->host_interval);
-#if 0
-		/* Increase interrupt polling rate for debugging */
-		if (qh->ep_type == USB_ENDPOINT_XFER_INT)
-			qh->host_interval = 8;
-#endif
-		hprt = dwc2_readl(hsotg->regs + HPRT0);
-		prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
-		if (prtspd == HPRT0_SPD_HIGH_SPEED &&
-		    (dev_speed == USB_SPEED_LOW ||
-		     dev_speed == USB_SPEED_FULL)) {
-			qh->host_interval *= 8;
-			dwc2_sch_dbg(hsotg,
-				     "QH=%p init*8 nxt=%04x, fn=%04x, int=%#x\n",
-				     qh, qh->next_active_frame,
-				     hsotg->frame_number, qh->host_interval);
+		qh->host_us = NS_TO_US(usb_calc_bus_time(host_speed, ep_is_in,
+				       ep_is_isoc, bytecount));
+		device_ns = usb_calc_bus_time(dev_speed, ep_is_in,
+					      ep_is_isoc, bytecount);
 
-		}
-		dev_dbg(hsotg->dev, "interval=%d\n", qh->host_interval);
-	}
+		if (do_split && dwc_tt)
+			device_ns += dwc_tt->usb_tt->think_time;
+		qh->device_us = NS_TO_US(device_ns);
 
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH Initialized\n");
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - qh = %p\n", qh);
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Device Address = %d\n",
-		 dwc2_hcd_get_dev_addr(&urb->pipe_info));
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Endpoint %d, %s\n",
-		 dwc2_hcd_get_ep_num(&urb->pipe_info),
-		 dwc2_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT");
 
-	qh->dev_speed = dev_speed;
+		qh->device_interval = urb->interval;
+		qh->host_interval = urb->interval * (do_split ? 8 : 1);
+
+		/*
+		 * Schedule low speed if we're running the host in low or
+		 * full speed OR if we've got a "TT" to deal with to access this
+		 * device.
+		 */
+		qh->schedule_low_speed = prtspd != HPRT0_SPD_HIGH_SPEED ||
+					 dwc_tt;
+
+		if (do_split) {
+			/* We won't know num transfers until we schedule */
+			qh->num_hs_transfers = -1;
+		} else if (dev_speed == USB_SPEED_HIGH) {
+			qh->num_hs_transfers = 1;
+		} else {
+			qh->num_hs_transfers = 0;
+		}
+
+		/* We'll schedule later when we have something to do */
+	}
 
 	switch (dev_speed) {
 	case USB_SPEED_LOW:
@@ -701,7 +1537,6 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 		speed = "?";
 		break;
 	}
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Speed = %s\n", speed);
 
 	switch (qh->ep_type) {
 	case USB_ENDPOINT_XFER_ISOC:
@@ -721,13 +1556,21 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 		break;
 	}
 
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Type = %s\n", type);
-
-	if (qh->ep_type == USB_ENDPOINT_XFER_INT) {
-		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - usecs = %d\n",
-			 qh->host_us);
-		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - interval = %d\n",
-			 qh->host_interval);
+	dwc2_sch_dbg(hsotg, "QH=%p Init %s, %s speed, %d bytes:\n", qh, type,
+		     speed, bytecount);
+	dwc2_sch_dbg(hsotg, "QH=%p ...addr=%d, ep=%d, %s\n", qh,
+		     dwc2_hcd_get_dev_addr(&urb->pipe_info),
+		     dwc2_hcd_get_ep_num(&urb->pipe_info),
+		     ep_is_in ? "IN" : "OUT");
+	if (ep_is_int || ep_is_isoc) {
+		dwc2_sch_dbg(hsotg,
+			     "QH=%p ...duration: host=%d us, device=%d us\n",
+			     qh, qh->host_us, qh->device_us);
+		dwc2_sch_dbg(hsotg, "QH=%p ...interval: host=%d, device=%d\n",
+			     qh, qh->host_interval, qh->device_interval);
+		if (qh->schedule_low_speed)
+			dwc2_sch_dbg(hsotg, "QH=%p ...low speed schedule=%p\n",
+				     qh, dwc2_get_ls_map(hsotg, qh));
 	}
 }
 
@@ -755,7 +1598,7 @@ struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
 	if (!qh)
 		return NULL;
 
-	dwc2_qh_init(hsotg, qh, urb);
+	dwc2_qh_init(hsotg, qh, urb, mem_flags);
 
 	if (hsotg->core_params->dma_desc_enable > 0 &&
 	    dwc2_hcd_qh_init_ddma(hsotg, qh, mem_flags) < 0) {
@@ -787,6 +1630,7 @@ void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		dwc2_do_unreserve(hsotg, qh);
 		spin_unlock_irqrestore(&hsotg->lock, flags);
 	}
+	dwc2_host_put_tt_info(hsotg, qh->dwc_tt);
 
 	if (qh->desc_list)
 		dwc2_hcd_qh_free_ddma(hsotg, qh);
@@ -902,6 +1746,8 @@ static int dwc2_next_for_periodic_split(struct dwc2_hsotg *hsotg,
 	u16 incr;
 
 	/*
+	 * See dwc2_uframe_schedule_split() for split scheduling.
+	 *
 	 * Basically: increment 1 normally, but 2 right after the start split
 	 * (except for ISOC out).
 	 */
@@ -1004,9 +1850,17 @@ static int dwc2_next_periodic_start(struct dwc2_hsotg *hsotg,
 	if (qh->start_active_frame == qh->next_active_frame ||
 	    dwc2_frame_num_gt(prev_frame_number, qh->start_active_frame)) {
 		u16 ideal_start = qh->start_active_frame;
+		int periods_in_map;
 
-		/* Adjust interval as per gcd with plan length. */
-		interval = gcd(interval, 8);
+		/*
+		 * Adjust interval as per gcd with map size.
+		 * See pmap_schedule() for more details here.
+		 */
+		if (qh->do_split || qh->dev_speed == USB_SPEED_HIGH)
+			periods_in_map = DWC2_HS_SCHEDULE_UFRAMES;
+		else
+			periods_in_map = DWC2_LS_SCHEDULE_FRAMES;
+		interval = gcd(interval, periods_in_map);
 
 		do {
 			qh->start_active_frame = dwc2_frame_num_inc(
-- 
2.7.0.rc3.207.g0ac5344



From robh at kernel.org  Fri Jan 22 14:29:24 2016
From: robh at kernel.org (Rob Herring)
Date: Fri, 22 Jan 2016 16:29:24 -0600
Subject: [PATCH v7 2/9] dt/bindings: arm-pl330: add description of arm,
 pl330-broken-no-flushp
In-Reply-To: <1453460812-8498-3-git-send-email-wxt@rock-chips.com>
References: <1453460812-8498-1-git-send-email-wxt@rock-chips.com>
 <1453460812-8498-3-git-send-email-wxt@rock-chips.com>
Message-ID: <20160122222924.GA21881@rob-hp-laptop>

On Fri, Jan 22, 2016 at 07:06:45PM +0800, Caesar Wang wrote:
> From: Shawn Lin <shawn.lin at rock-chips.com>
> 
> This patch adds the "arm, pl330-broken-no-flushp" for arm-pl330.
> 
> Reviewed-by: Doug Anderson <dianders at chromium.org>
> Reviewed-by: Sonny Rao <sonnyrao at chromium.org>
> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
> 
> ---
> 
> Changes in v7:
> - modify the subject to be better match.
> 
> Changes in v6: None
> Changes in v5: None
> Changes in v4: None
> Changes in v3:
> - add Reviewed-by: Sonny Rao <sonnyrao at chromium.org>
> 
> Changes in v2:
> - add Reviewed-by: Doug Anderson <dianders at chromium.org>
> 
> Changes in v1:
> - rename broken-no-flushp to "arm,pl330-broken-no-flushp" suggested
>   by Krzysztof.
> 
>  Documentation/devicetree/bindings/dma/arm-pl330.txt | 1 +
>  1 file changed, 1 insertion(+)

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


From dianders at chromium.org  Fri Jan 22 15:56:10 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Fri, 22 Jan 2016 15:56:10 -0800
Subject: [PATCH] usb: dwc2: host: Properly set the HFIR
Message-ID: <1453506971-31445-1-git-send-email-dianders@chromium.org>

According to the most up to date version of the dwc2 databook, the FRINT
field of the HFIR register should be programmed to:
* 125 us * (PHY clock freq for HS) - 1
* 1000 us * (PHY clock freq for FS/LS) - 1

This is opposed to older versions of the doc that claimed it should be:
* 125 us * (PHY clock freq for HS)
* 1000 us * (PHY clock freq for FS/LS)

In case you didn't spot it, the difference is the "- 1".

Let's add the "- 1" to match the newest user manual.  It's presumed that
the "- 1" should have always been there and that this was always a
documentation error.  If some hardware needs the "- 1" and other
hardware doesn't, we'll have to add a configuration parameter for it in
the future.

I checked things before and after this patch on rk3288 using a Total
Phase Beagle 5000 analyzer.

Before this patch, a low speed mouse shows constant Frame Timing Jitter
errors.  After this patch errors have gone away.

Before this patch SOF packets move forward about 1 us per 4 ms.  After
this patch the SOF packets move backward about 1 us per 255 ms.  Some
specific SOF timestamps from the analyzer are below.

Before:
  6.603.790
  6.603.916
  6.604.041
  6.604.166
  ...
  6.607.541
  6.607.667
  6.607.792
  6.607.917
  ...
  6.611.417
  6.611.543
  6.611.668
  6.611.793

After:
  6.215.159
  6.215.284
  6.215.408
  6.215.533
  6.215.658
  ...
  6.470.658
  6.470.783
  6.470.907
  ...
  6.726.032
  6.726.157
  6.725.281
  6.725.406

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
 drivers/usb/dwc2/core.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index 39a0fa8a4c0a..c7798476e25b 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -2251,10 +2251,10 @@ u32 dwc2_calc_frame_interval(struct dwc2_hsotg *hsotg)
 
 	if ((hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT == HPRT0_SPD_HIGH_SPEED)
 		/* High speed case */
-		return 125 * clock;
+		return 125 * clock - 1;
 	else
 		/* FS/LS case */
-		return 1000 * clock;
+		return 1000 * clock - 1;
 }
 
 /**
-- 
2.7.0.rc3.207.g0ac5344



From mturquette at baylibre.com  Fri Jan 22 16:00:43 2016
From: mturquette at baylibre.com (Michael Turquette)
Date: Fri, 22 Jan 2016 16:00:43 -0800
Subject: [GIT PULL] clk fixes for 4.5
In-Reply-To: <1578308.XWWZ6XYns5@phil>
References: <1578308.XWWZ6XYns5@phil>
Message-ID: <20160123000043.19367.6646@quark.deferred.io>

Quoting Heiko Stuebner (2016-01-17 11:07:01)
> Hi Mike, Stephen,
> 
> based on the clk-for-linus-4.5 tag that got merged into mainline
> some asorted fixes for Rockchip clocks.
> 
> So, please pull

Pulled.

Regards,
Mike

> 
> 
> Thanks
> Heiko
> 
> The following changes since commit 3da834e3e5a4a5d26882955298b55a9ed37a00bc:
> 
>   clk: remove duplicated COMMON_CLK_NXP record from clk/Kconfig (2016-01-13 18:09:52 -0800)
> 
> are available in the git repository at:
> 
>   git://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git tags/v4.5-rockchip-clkfixes1
> 
> for you to fetch changes up to 8931f8e02979e4180566907019d432527c80abf7:
> 
>   clk: rockchip: rk3368: fix some clock gates (2016-01-16 16:01:21 +0100)
> 
> ----------------------------------------------------------------
> Fixes for wrong register offsets in both rk3036 and rk3368.
> Also rename the external input for the emac on rk3036, which
> should still be ok to do, as that binding was only introduced
> during this merge-window.
> 
> ----------------------------------------------------------------
> Jianqun xu (1):
>       clk: rockchip: rk3368: fix some clock gates
> 
> Xing Zheng (4):
>       clk: rockchip: rk3036: fix the FLAGs for clock mux
>       clk: rockchip: rk3036: fix uarts clock error
>       clk: rockchip: rk3036: fix the div offset for emac clock
>       clk: rockchip: rk3036: rename emac ext source clock
> 
>  .../bindings/clock/rockchip,rk3036-cru.txt         |  2 +-
>  drivers/clk/rockchip/clk-rk3036.c                  | 26 +++++++++++-----------
>  drivers/clk/rockchip/clk-rk3368.c                  | 26 +++++++++++-----------
>  3 files changed, 27 insertions(+), 27 deletions(-)
> 


From heiko at sntech.de  Sat Jan 23 09:52:20 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sat, 23 Jan 2016 18:52:20 +0100
Subject: [PATCH v5 0/21] usb: dwc2: host: Fix and speed up all the stuff,
 especially with splits
In-Reply-To: <1453486736-15358-1-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
Message-ID: <4616032.f74zstHVA0@phil>

Hi,

Am Freitag, 22. Januar 2016, 10:18:35 schrieb Douglas Anderson:
> This is a bit of catchall series for all the bug fix and performance
> patches I've been working on over the last few months.  Note that for
> dwc2 we need to do LOTS in software and need super low interrupt
> latency, so most performance improvements actually fix real bugs.
> 
> Patches are structured to start with no-brainer stuff that could be
> applied ASAP, especially things I've already gotten Acks for.  Things
> get slightly more RFC / RFT like as we get farther down the series.
> Anything that can be landed sooner rather than later (especially those
> Acked long ago) would help in re-posts (I'm not biased, of course).
> 
> It's been a few months since my last post of this series.  In the
> meantime I've added a bunch of small bugfixes to the start of it and
> also TOTALLY REWROTE the microframe scheduler.  I'll say up front: I
> know nothing about USB.  I haven't read the whole spec.  I'm not
> terribly familiar with the OHCI, EHCI, and XHCI drivers in the kernel.
> ...and I'm pretty clueless overall.  Nevertheless, I've attempted to
> write up a fancy scheduler based on the portion of the spec talking
> about microframe scheduling requirements.  This rewritten scheduler does
> seem to help when I start jamming lots of USB things into a hub, so
> presumably the code is a reasonably starting point.  Given my current
> understanding of USB the old code was fairly insane, so presumably even
> if my new patch isn't perfect it's better than what we had.

I've tested this series (+the low-speed fix from today) on the following
usb-tree on a rk3288-veyron-jerry:

/:  Bus 03.Port 1: Dev 1, Class=root_hub, Driver=ehci-platform/1p, 480M
    |__ Port 1: Dev 2, If 0, Class=Video, Driver=, 480M
    |__ Port 1: Dev 2, If 1, Class=Video, Driver=, 480M
/:  Bus 02.Port 1: Dev 1, Class=root_hub, Driver=dwc2/1p, 480M
    |__ Port 1: Dev 2, If 0, Class=Vendor Specific Class, Driver=zd1211rw, 480M
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=dwc2/1p, 480M
    |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/5p, 480M
        |__ Port 1: Dev 3, If 0, Class=Vendor Specific Class, Driver=smsc95xx, 480M
        |__ Port 3: Dev 4, If 0, Class=Hub, Driver=hub/4p, 480M
            |__ Port 1: Dev 6, If 0, Class=Vendor Specific Class, Driver=, 12M
            |__ Port 3: Dev 8, If 0, Class=Hub, Driver=hub/4p, 480M
                |__ Port 1: Dev 10, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M
                |__ Port 1: Dev 10, If 1, Class=Human Interface Device, Driver=usbhid, 1.5M
                |__ Port 2: Dev 11, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M
                |__ Port 2: Dev 11, If 1, Class=Human Interface Device, Driver=usbhid, 1.5M
                |__ Port 3: Dev 12, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M
                |__ Port 4: Dev 13, If 0, Class=Mass Storage, Driver=usb-storage, 480M
            |__ Port 4: Dev 9, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M
        |__ Port 4: Dev 5, If 0, Class=Mass Storage, Driver=usb-storage, 480M
        |__ Port 5: Dev 7, If 0, Class=Mass Storage, Driver=usb-storage, 480M


At first we were maxing out the 9 channels of the otg port, which the
driver didn't seem to care about in 4.4-rc6, but after realizing that and
switching over to the 16ch host-port it ran really nicely, especially wrt
faster handling of all the keyboard input.

When looking over to the other desk and the person using that veyron-
jerry not cursing anymore, it seem to be running fine in a real-world use
for 2 hours now ;-)

So,
Tested-by: Heiko Stuebner <heiko at sntech.de>


> The patches here will speed up the interrupt controller significantly.
> After this series, I have a hard time seeing the interrupt controller
> taking > 20 us and I don't ever see it taking > 30 us ever in my tests
> unless I bring the cpufreq back down.  With the cpufreq at 126 MHz I can
> still see the interrupt handler take > 50 us, so I'm sure we could
> improve this further.  ...but hey, it's a start.

In the configuration described above, we were still able to run the
interrupt handlers capabilities when the cpufreq is at 126MHz, but
once set to a sane minimum it is running really nice now.


Heiko


From heiko at sntech.de  Sat Jan 23 09:53:37 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sat, 23 Jan 2016 18:53:37 +0100
Subject: [PATCH] usb: dwc2: host: Properly set the HFIR
In-Reply-To: <1453506971-31445-1-git-send-email-dianders@chromium.org>
References: <1453506971-31445-1-git-send-email-dianders@chromium.org>
Message-ID: <39287239.yyU0LGezZt@phil>

Am Freitag, 22. Januar 2016, 15:56:10 schrieb Douglas Anderson:
> According to the most up to date version of the dwc2 databook, the FRINT
> field of the HFIR register should be programmed to:
> * 125 us * (PHY clock freq for HS) - 1
> * 1000 us * (PHY clock freq for FS/LS) - 1
> 
> This is opposed to older versions of the doc that claimed it should be:
> * 125 us * (PHY clock freq for HS)
> * 1000 us * (PHY clock freq for FS/LS)
> 
> In case you didn't spot it, the difference is the "- 1".
> 
> Let's add the "- 1" to match the newest user manual.  It's presumed that
> the "- 1" should have always been there and that this was always a
> documentation error.  If some hardware needs the "- 1" and other
> hardware doesn't, we'll have to add a configuration parameter for it in
> the future.

Same setup and description as in Doug's 21-patch series, so on a
veyron-jerry:

Tested-by: Heiko Stuebner <heiko at sntech.de>


Heiko


From dianders at chromium.org  Sat Jan 23 15:09:55 2016
From: dianders at chromium.org (Doug Anderson)
Date: Sat, 23 Jan 2016 15:09:55 -0800
Subject: [PATCH v5 0/21] usb: dwc2: host: Fix and speed up all the stuff, 
 especially with splits
In-Reply-To: <4616032.f74zstHVA0@phil>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
 <4616032.f74zstHVA0@phil>
Message-ID: <CAD=FV=W29_nxn3Ga0ND-LzLGFiybhEni7aUFpfQMgp=AME=a=A@mail.gmail.com>

Heiko,

On Sat, Jan 23, 2016 at 9:52 AM, Heiko Stuebner <heiko at sntech.de> wrote:
> Hi,
>
> Am Freitag, 22. Januar 2016, 10:18:35 schrieb Douglas Anderson:
>> This is a bit of catchall series for all the bug fix and performance
>> patches I've been working on over the last few months.  Note that for
>> dwc2 we need to do LOTS in software and need super low interrupt
>> latency, so most performance improvements actually fix real bugs.
>>
>> Patches are structured to start with no-brainer stuff that could be
>> applied ASAP, especially things I've already gotten Acks for.  Things
>> get slightly more RFC / RFT like as we get farther down the series.
>> Anything that can be landed sooner rather than later (especially those
>> Acked long ago) would help in re-posts (I'm not biased, of course).
>>
>> It's been a few months since my last post of this series.  In the
>> meantime I've added a bunch of small bugfixes to the start of it and
>> also TOTALLY REWROTE the microframe scheduler.  I'll say up front: I
>> know nothing about USB.  I haven't read the whole spec.  I'm not
>> terribly familiar with the OHCI, EHCI, and XHCI drivers in the kernel.
>> ...and I'm pretty clueless overall.  Nevertheless, I've attempted to
>> write up a fancy scheduler based on the portion of the spec talking
>> about microframe scheduling requirements.  This rewritten scheduler does
>> seem to help when I start jamming lots of USB things into a hub, so
>> presumably the code is a reasonably starting point.  Given my current
>> understanding of USB the old code was fairly insane, so presumably even
>> if my new patch isn't perfect it's better than what we had.
>
> I've tested this series (+the low-speed fix from today) on the following
> usb-tree on a rk3288-veyron-jerry:
>
> /:  Bus 03.Port 1: Dev 1, Class=root_hub, Driver=ehci-platform/1p, 480M
>     |__ Port 1: Dev 2, If 0, Class=Video, Driver=, 480M
>     |__ Port 1: Dev 2, If 1, Class=Video, Driver=, 480M
> /:  Bus 02.Port 1: Dev 1, Class=root_hub, Driver=dwc2/1p, 480M
>     |__ Port 1: Dev 2, If 0, Class=Vendor Specific Class, Driver=zd1211rw, 480M
> /:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=dwc2/1p, 480M
>     |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/5p, 480M
>         |__ Port 1: Dev 3, If 0, Class=Vendor Specific Class, Driver=smsc95xx, 480M
>         |__ Port 3: Dev 4, If 0, Class=Hub, Driver=hub/4p, 480M
>             |__ Port 1: Dev 6, If 0, Class=Vendor Specific Class, Driver=, 12M
>             |__ Port 3: Dev 8, If 0, Class=Hub, Driver=hub/4p, 480M
>                 |__ Port 1: Dev 10, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M
>                 |__ Port 1: Dev 10, If 1, Class=Human Interface Device, Driver=usbhid, 1.5M
>                 |__ Port 2: Dev 11, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M
>                 |__ Port 2: Dev 11, If 1, Class=Human Interface Device, Driver=usbhid, 1.5M
>                 |__ Port 3: Dev 12, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M
>                 |__ Port 4: Dev 13, If 0, Class=Mass Storage, Driver=usb-storage, 480M
>             |__ Port 4: Dev 9, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M
>         |__ Port 4: Dev 5, If 0, Class=Mass Storage, Driver=usb-storage, 480M
>         |__ Port 5: Dev 7, If 0, Class=Mass Storage, Driver=usb-storage, 480M
>
>
> At first we were maxing out the 9 channels of the otg port, which the
> driver didn't seem to care about in 4.4-rc6, but after realizing that and
> switching over to the 16ch host-port it ran really nicely, especially wrt
> faster handling of all the keyboard input.

Actually, I should probably fix this in the patch ("usb: dwc2: host:
Totally redo the microframe scheduler").  While trying to make things
work originally I had moved the "dwc2_periodic_channel_available()" to
always happen even with the microframe scheduler, but I don't think
there's any strong reason for that.  I think we could just undo it and
everything would work just as well or better.

I'll try to do some testing with that fix next week and then try to
send up a spun series.  Note that the reason why ("usb: dwc2: host:
Totally redo the microframe scheduler") was near the end of the series
was that I'd expected that things earlier than it were in a pretty
good shape to land while I was expecting that the microframe rewrite
might need a spin or two.


> When looking over to the other desk and the person using that veyron-
> jerry not cursing anymore, it seem to be running fine in a real-world use
> for 2 hours now ;-)

Great news!  Thank you for testing!  :)

-Doug


From dianders at chromium.org  Sat Jan 23 21:36:15 2016
From: dianders at chromium.org (Doug Anderson)
Date: Sat, 23 Jan 2016 21:36:15 -0800
Subject: [PATCH v5 0/21] usb: dwc2: host: Fix and speed up all the stuff, 
 especially with splits
In-Reply-To: <CAD=FV=W29_nxn3Ga0ND-LzLGFiybhEni7aUFpfQMgp=AME=a=A@mail.gmail.com>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
 <4616032.f74zstHVA0@phil>
 <CAD=FV=W29_nxn3Ga0ND-LzLGFiybhEni7aUFpfQMgp=AME=a=A@mail.gmail.com>
Message-ID: <CAD=FV=WX+f6rYfdvPZ4Nz0j6-vJnORnb1vF0eRsrjY1QB3KveQ@mail.gmail.com>

Hi,

On Sat, Jan 23, 2016 at 3:09 PM, Doug Anderson <dianders at chromium.org> wrote:
> Heiko,
>
> On Sat, Jan 23, 2016 at 9:52 AM, Heiko Stuebner <heiko at sntech.de> wrote:
>> Hi,
>>
>> Am Freitag, 22. Januar 2016, 10:18:35 schrieb Douglas Anderson:
>>> This is a bit of catchall series for all the bug fix and performance
>>> patches I've been working on over the last few months.  Note that for
>>> dwc2 we need to do LOTS in software and need super low interrupt
>>> latency, so most performance improvements actually fix real bugs.
>>>
>>> Patches are structured to start with no-brainer stuff that could be
>>> applied ASAP, especially things I've already gotten Acks for.  Things
>>> get slightly more RFC / RFT like as we get farther down the series.
>>> Anything that can be landed sooner rather than later (especially those
>>> Acked long ago) would help in re-posts (I'm not biased, of course).
>>>
>>> It's been a few months since my last post of this series.  In the
>>> meantime I've added a bunch of small bugfixes to the start of it and
>>> also TOTALLY REWROTE the microframe scheduler.  I'll say up front: I
>>> know nothing about USB.  I haven't read the whole spec.  I'm not
>>> terribly familiar with the OHCI, EHCI, and XHCI drivers in the kernel.
>>> ...and I'm pretty clueless overall.  Nevertheless, I've attempted to
>>> write up a fancy scheduler based on the portion of the spec talking
>>> about microframe scheduling requirements.  This rewritten scheduler does
>>> seem to help when I start jamming lots of USB things into a hub, so
>>> presumably the code is a reasonably starting point.  Given my current
>>> understanding of USB the old code was fairly insane, so presumably even
>>> if my new patch isn't perfect it's better than what we had.
>>
>> I've tested this series (+the low-speed fix from today) on the following
>> usb-tree on a rk3288-veyron-jerry:
>>
>> /:  Bus 03.Port 1: Dev 1, Class=root_hub, Driver=ehci-platform/1p, 480M
>>     |__ Port 1: Dev 2, If 0, Class=Video, Driver=, 480M
>>     |__ Port 1: Dev 2, If 1, Class=Video, Driver=, 480M
>> /:  Bus 02.Port 1: Dev 1, Class=root_hub, Driver=dwc2/1p, 480M
>>     |__ Port 1: Dev 2, If 0, Class=Vendor Specific Class, Driver=zd1211rw, 480M
>> /:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=dwc2/1p, 480M
>>     |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/5p, 480M
>>         |__ Port 1: Dev 3, If 0, Class=Vendor Specific Class, Driver=smsc95xx, 480M
>>         |__ Port 3: Dev 4, If 0, Class=Hub, Driver=hub/4p, 480M
>>             |__ Port 1: Dev 6, If 0, Class=Vendor Specific Class, Driver=, 12M
>>             |__ Port 3: Dev 8, If 0, Class=Hub, Driver=hub/4p, 480M
>>                 |__ Port 1: Dev 10, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M
>>                 |__ Port 1: Dev 10, If 1, Class=Human Interface Device, Driver=usbhid, 1.5M
>>                 |__ Port 2: Dev 11, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M
>>                 |__ Port 2: Dev 11, If 1, Class=Human Interface Device, Driver=usbhid, 1.5M
>>                 |__ Port 3: Dev 12, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M
>>                 |__ Port 4: Dev 13, If 0, Class=Mass Storage, Driver=usb-storage, 480M
>>             |__ Port 4: Dev 9, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M
>>         |__ Port 4: Dev 5, If 0, Class=Mass Storage, Driver=usb-storage, 480M
>>         |__ Port 5: Dev 7, If 0, Class=Mass Storage, Driver=usb-storage, 480M
>>
>>
>> At first we were maxing out the 9 channels of the otg port, which the
>> driver didn't seem to care about in 4.4-rc6, but after realizing that and
>> switching over to the 16ch host-port it ran really nicely, especially wrt
>> faster handling of all the keyboard input.
>
> Actually, I should probably fix this in the patch ("usb: dwc2: host:
> Totally redo the microframe scheduler").  While trying to make things
> work originally I had moved the "dwc2_periodic_channel_available()" to
> always happen even with the microframe scheduler, but I don't think
> there's any strong reason for that.  I think we could just undo it and
> everything would work just as well or better.
>
> I'll try to do some testing with that fix next week and then try to
> send up a spun series.  Note that the reason why ("usb: dwc2: host:
> Totally redo the microframe scheduler") was near the end of the series
> was that I'd expected that things earlier than it were in a pretty
> good shape to land while I was expecting that the microframe rewrite
> might need a spin or two.

I can't do more than cursory testing without being at work and able to
plug / unplug peripherals, but I'd expect that this should fix the
problem:

https://chromium-review.googlesource.com/#/c/323326/

I won't plan on spinning the series with that patch until at least a
week has passed.  I believe that John is out right now and I don't
want to spam a 21-part series too many times.

-Doug


From dianders at chromium.org  Sat Jan 23 21:44:58 2016
From: dianders at chromium.org (Doug Anderson)
Date: Sat, 23 Jan 2016 21:44:58 -0800
Subject: [PATCH v5 20/21] usb: dwc2: host: Totally redo the microframe
 scheduler
In-Reply-To: <1453486736-15358-21-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
 <1453486736-15358-21-git-send-email-dianders@chromium.org>
Message-ID: <CAD=FV=XJ1h1nOmFMNXUkjnrk=CGk27GHNvYAAQvnOz6Fv0eKWA@mail.gmail.com>

Hi,

On Fri, Jan 22, 2016 at 10:18 AM, Douglas Anderson
<dianders at chromium.org> wrote:
> This totally reimplements the microframe scheduler in dwc2 to attempt to
> handle periodic splits properly.  The old code didn't even try, so this
> was a significant effort since periodic splits are one of the most
> complicated things in USB.
>
> I've attempted to keep the old "don't use the microframe" schduler
> around for now, but not sure it's needed.  It has also only been lightly
> tested.
>
> I think it's pretty certain that this scheduler isn't perfect and might
> have some bugs, but it seems much better than what was there before.
> With this change my stressful USB test (USB webcam + USB audio + some
> keyboards) crackles less.
>
> Signed-off-by: Douglas Anderson <dianders at chromium.org>
> ---
> Changes in v5:
> - Moved defines outside of ifdef to avoid gadget-only compile error.
>
> Changes in v4:
> - Figured out what the microframe scheduler was supposed to do.
> - Microframe rewrite is totally different from v3, hopefully more right.
> - Microframe rewrite is later in the series now.
>
> Changes in v3:
> - The uframe scheduler patch is folded into optimization series.
> - Optimize uframe scheduler "single uframe" case a little.
> - uframe scheduler now atop logging patches.
> - uframe scheduler now before delayed bandwidth release patches.
> - Add defines like EARLY_FRAME_USEC
> - Reorder dwc2_deschedule_periodic() in prep for future patches.
> - uframe scheduler now shows real usefulness w/ future patches!
> - Assuming single_tt is new for v3; not terribly well tested (yet).
> - Keep track and use our uframe new for v3.
>
> Changes in v2:
> - Totally rewrote uframe scheduler again after writing test code.
> - uframe scheduler atop delayed bandwidth release patches.
>
>  drivers/usb/dwc2/core.h      |   85 ++-
>  drivers/usb/dwc2/hcd.c       |   87 ++-
>  drivers/usb/dwc2/hcd.h       |   79 ++-
>  drivers/usb/dwc2/hcd_queue.c | 1208 +++++++++++++++++++++++++++++++++++-------
>  4 files changed, 1271 insertions(+), 188 deletions(-)

Needs more testing, but possibly the next version of this patch will
include <https://chromium-review.googlesource.com/#/c/323326/>.  If
you happen to be testing / reviewing this patch, please consider it
with the proposed fixup.  I'm happy to send out a squashed version if
that makes someone's review easier.

-Doug


From caesar.upstream at gmail.com  Sat Jan 23 23:34:07 2016
From: caesar.upstream at gmail.com (Caesar Wang)
Date: Sun, 24 Jan 2016 15:34:07 +0800
Subject: [PATCH v3 1/2] drm: rockchip/hdmi: add Innosilicon HDMI support
In-Reply-To: <569C3E36.10103@rock-chips.com>
References: <1452850598-30859-1-git-send-email-ykk@rock-chips.com>
 <1452850690-31760-1-git-send-email-ykk@rock-chips.com>
 <569C3E36.10103@rock-chips.com>
Message-ID: <56A47E6F.8050104@gmail.com>

Hi

? 2016?01?18? 09:21, Mark yao ??:
> Hi Yakir
>
> I'd like you can change your patch title into "drm/rockchip/hdmi", so 
> when I search patches use "drm/rockchip" can find your patch.
>
> and I have some advices mail inline.
>
> Thanks:-)
>
> On 2016?01?15? 17:38, Yakir Yang wrote:
>> The Innosilicon HDMI is a low power HDMI 1.4 transmitter
>> IP, and it have been integrated on some rockchip CPUs
>> (like RK3036, RK312x).
>>
>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
>> ---
>> Changes in v3:
>> - Use encoder enable/disable function, and remove the encoder DPMS 
>> function
>> - Keep HDMI PLL power on in standby mode
>>
>> Changes in v2:
>> - Using DRM atomic helper functions for connector init (Mark)
>> - Remove "hdmi->connector.encoder = encoder;" (Mark)
>>
>>   drivers/gpu/drm/rockchip/Kconfig     |   8 +
>>   drivers/gpu/drm/rockchip/Makefile    |   1 +
>>   drivers/gpu/drm/rockchip/inno_hdmi.c | 999 
>> +++++++++++++++++++++++++++++++++++
>>   drivers/gpu/drm/rockchip/inno_hdmi.h | 364 +++++++++++++
>>   4 files changed, 1372 insertions(+)
>>   create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.c
>>   create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.h
>>
>> diff --git a/drivers/gpu/drm/rockchip/Kconfig 
>> b/drivers/gpu/drm/rockchip/Kconfig
>> index 35215f6..a5014e0 100644
>> --- a/drivers/gpu/drm/rockchip/Kconfig
>> +++ b/drivers/gpu/drm/rockchip/Kconfig
>> @@ -25,3 +25,11 @@ config ROCKCHIP_DW_HDMI
>>         for the Synopsys DesignWare HDMI driver. If you want to
>>         enable HDMI on RK3288 based SoC, you should selet this
>>         option.
>> +
>> +config ROCKCHIP_INNO_HDMI
>> +    tristate "Rockchip specific extensions for Innosilicon HDMI"
>> +        depends on DRM_ROCKCHIP
>> +        help
>> +      This selects support for Rockchip SoC specific extensions
>> +      for the Innosilicon HDMI driver. If you want to enable
>> +      HDMI on RK3036 based SoC, you should selet this option.

That's seem has some conflicts since the MIPI driver land in mainline.
So you need update it based on the lastest kernel.

>> diff --git a/drivers/gpu/drm/rockchip/Makefile 
>> b/drivers/gpu/drm/rockchip/Makefile
>> index a9d380f..da2bf76 100644
>> --- a/drivers/gpu/drm/rockchip/Makefile
>> +++ b/drivers/gpu/drm/rockchip/Makefile
>> @@ -6,6 +6,7 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o 
>> rockchip_drm_fbdev.o \
>>           rockchip_drm_gem.o
>>     obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
>> +obj-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
>>     obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_drm_vop.o \
>>                   rockchip_vop_reg.o
>> diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c 
>> b/drivers/gpu/drm/rockchip/inno_hdmi.c
>> new file mode 100644
>> index 0000000..dc98179
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
>> @@ -0,0 +1,999 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + *    Zheng Yang <zhengyang at rock-chips.com>
>> + *    Yakir Yang <ykk at rock-chips.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/irq.h>
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/err.h>
>> +#include <linux/hdmi.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <linux/module.h>
>> +#include <linux/mutex.h>
>> +#include <linux/of_device.h>
>> +
>> +#include <drm/drm_of.h>
>> +#include <drm/drmP.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_crtc_helper.h>
>> +#include <drm/drm_edid.h>
>> +#include <drm/drm_encoder_slave.h>
>> +
>> +#include "rockchip_drm_drv.h"
>> +#include "rockchip_drm_vop.h"
>> +
>> +#include "inno_hdmi.h"
>> +
>> +#define to_inno_hdmi(x)    container_of(x, struct inno_hdmi, x)
>> +
>> +struct hdmi_data_info {
>> +    int vic;
>> +    bool sink_is_hdmi;
>> +    bool sink_has_audio;
>> +    unsigned int enc_in_format;
>> +    unsigned int enc_out_format;
>> +    unsigned int colorimetry;
>> +};
>> +
>> +struct inno_hdmi_i2c {
>> +    struct i2c_adapter adap;
>> +
>> +    u8 ddc_addr;
>> +    u8 segment_addr;
>> +
>> +    struct mutex lock;
>> +    struct completion cmp;
>> +};
>> +
>> +struct inno_hdmi {
>> +    struct device *dev;
>> +    struct drm_device *drm_dev;
>> +
>> +    int irq;
>> +    struct clk *pclk;
>> +    void __iomem *regs;
>> +
>> +    struct drm_connector    connector;
>> +    struct drm_encoder    encoder;
>> +
>> +    struct inno_hdmi_i2c *i2c;
>> +    struct i2c_adapter *ddc;
>> +
>> +    unsigned int tmds_rate;
>> +
>> +    struct hdmi_data_info    hdmi_data;
>> +    struct drm_display_mode previous_mode;
>> +};
>> +
>> +enum {
>> +    CSC_ITU601_16_235_TO_RGB_0_255_8BIT,
>> +    CSC_ITU601_0_255_TO_RGB_0_255_8BIT,
>> +    CSC_ITU709_16_235_TO_RGB_0_255_8BIT,
>> +    CSC_RGB_0_255_TO_ITU601_16_235_8BIT,
>> +    CSC_RGB_0_255_TO_ITU709_16_235_8BIT,
>> +    CSC_RGB_0_255_TO_RGB_16_235_8BIT,
>> +};
>> +
>> +static const char coeff_csc[][24] = {
>> +    /*
>> +     * YUV2RGB:601 SD mode(Y[16:235], UV[16:240], RGB[0:255]):
>> +     *   R = 1.164*Y + 1.596*V - 204
>> +     *   G = 1.164*Y - 0.391*U - 0.813*V + 154
>> +     *   B = 1.164*Y + 2.018*U - 258
>> +     */
>> +    {
>> +        0x04, 0xa7, 0x00, 0x00, 0x06, 0x62, 0x02, 0xcc,
>> +        0x04, 0xa7, 0x11, 0x90, 0x13, 0x40, 0x00, 0x9a,
>> +        0x04, 0xa7, 0x08, 0x12, 0x00, 0x00, 0x03, 0x02
>> +    },
>> +    /*
>> +     * YUV2RGB:601 SD mode(YUV[0:255],RGB[0:255]):
>> +     *   R = Y + 1.402*V - 248
>> +     *   G = Y - 0.344*U - 0.714*V + 135
>> +     *   B = Y + 1.772*U - 227
>> +     */
>> +    {
>> +        0x04, 0x00, 0x00, 0x00, 0x05, 0x9b, 0x02, 0xf8,
>> +        0x04, 0x00, 0x11, 0x60, 0x12, 0xdb, 0x00, 0x87,
>> +        0x04, 0x00, 0x07, 0x16, 0x00, 0x00, 0x02, 0xe3
>> +    },
>> +    /*
>> +     * YUV2RGB:709 HD mode(Y[16:235],UV[16:240],RGB[0:255]):
>> +     *   R = 1.164*Y + 1.793*V - 248
>> +     *   G = 1.164*Y - 0.213*U - 0.534*V + 77
>> +     *   B = 1.164*Y + 2.115*U - 289
>> +     */
>> +    {
>> +        0x04, 0xa7, 0x00, 0x00, 0x07, 0x2c, 0x02, 0xf8,
>> +        0x04, 0xa7, 0x10, 0xda, 0x12, 0x22, 0x00, 0x4d,
>> +        0x04, 0xa7, 0x08, 0x74, 0x00, 0x00, 0x03, 0x21
>> +    },
>> +
>> +    /*
>> +     * RGB2YUV:601 SD mode:
>> +     *   Cb = -0.291G - 0.148R + 0.439B + 128
>> +     *   Y  = 0.504G  + 0.257R + 0.098B + 16
>> +     *   Cr = -0.368G + 0.439R - 0.071B + 128
>> +     */
>> +    {
>> +        0x11, 0x5f, 0x01, 0x82, 0x10, 0x23, 0x00, 0x80,
>> +        0x02, 0x1c, 0x00, 0xa1, 0x00, 0x36, 0x00, 0x1e,
>> +        0x11, 0x29, 0x10, 0x59, 0x01, 0x82, 0x00, 0x80
>> +    },
>> +    /*
>> +     * RGB2YUV:709 HD mode:
>> +     *   Cb = - 0.338G - 0.101R + 0.439B + 128
>> +     *   Y  = 0.614G   + 0.183R + 0.062B + 16
>> +     *   Cr = - 0.399G + 0.439R - 0.040B + 128
>> +     */
>> +    {
>> +        0x11, 0x98, 0x01, 0xc1, 0x10, 0x28, 0x00, 0x80,
>> +        0x02, 0x74, 0x00, 0xbb, 0x00, 0x3f, 0x00, 0x10,
>> +        0x11, 0x5a, 0x10, 0x67, 0x01, 0xc1, 0x00, 0x80
>> +    },
>> +    /*
>> +     * RGB[0:255]2RGB[16:235]:
>> +     *   R' = R x (235-16)/255 + 16;
>> +     *   G' = G x (235-16)/255 + 16;
>> +     *   B' = B x (235-16)/255 + 16;
>> +     */
>> +    {
>> +        0x00, 0x00, 0x03, 0x6F, 0x00, 0x00, 0x00, 0x10,
>> +        0x03, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
>> +        0x00, 0x00, 0x00, 0x00, 0x03, 0x6F, 0x00, 0x10
>> +    },
>> +};
>> +
>> +static inline u8 hdmi_readb(struct inno_hdmi *hdmi, u16 offset)
>> +{
>> +    return readl_relaxed(hdmi->regs + (offset) * 0x04);
>> +}
>> +
>> +static inline void hdmi_writeb(struct inno_hdmi *hdmi, u16 offset, 
>> u32 val)
>> +{
>> +    writel_relaxed(val, hdmi->regs + (offset) * 0x04);
>> +}
>> +
>> +static inline void hdmi_modb(struct inno_hdmi *hdmi, u16 offset,
>> +                 u32 msk, u32 val)
>> +{
>> +    u8 temp = hdmi_readb(hdmi, offset) & ~msk;
>> +
>> +    temp |= val & msk;
>> +    hdmi_writeb(hdmi, offset, temp);
>> +}
>> +
>> +static void inno_hdmi_i2c_init(struct inno_hdmi *hdmi)
>> +{
>> +    int ddc_bus_freq;
>> +
>> +    ddc_bus_freq = (hdmi->tmds_rate >> 2) / HDMI_SCL_RATE;
>> +
>> +    hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
>> +    hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
>> +
>> +    /* Clear the EDID interrupt flag and mute the interrupt */
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
>> +}
>> +
>> +static void inno_hdmi_sys_power(struct inno_hdmi *hdmi, bool enable)
>> +{
>> +    if (enable)
>> +        hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON);
>> +    else
>> +        hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
>> +}
>> +
>> +static void inno_hdmi_set_pwr_mode(struct inno_hdmi *hdmi, int mode)
>> +{
>> +    switch (mode) {
>> +    case NORMAL:
>> +        inno_hdmi_sys_power(hdmi, false);
>> +
>> +        hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x6f);
>> +        hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0xbb);
>> +
>> +        hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
>> +        hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x14);
>> +        hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x10);
>> +        hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x0f);
>> +        hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x00);
>> +        hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x01);
>> +
>> +        inno_hdmi_sys_power(hdmi, true);
>> +        break;
>> +
>> +    case LOWER_PWR:
>> +        inno_hdmi_sys_power(hdmi, false);
>> +        hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0x00);
>> +        hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x00);
>> +        hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x00);
>> +        hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
>> +
>> +        break;
>> +
>> +    default:
>> +        dev_err(hdmi->dev, "Unknown power mode %d\n", mode);
>> +    }
>> +}
>> +
>> +static void inno_hdmi_reset(struct inno_hdmi *hdmi)
>> +{
>> +    u32 val;
>> +    u32 msk;
>> +
>> +    hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_DIGITAL, v_NOT_RST_DIGITAL);
>> +    udelay(100);
>> +
>> +    hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_ANALOG, v_NOT_RST_ANALOG);
>> +    udelay(100);
>> +
>> +    msk = m_REG_CLK_INV | m_REG_CLK_SOURCE | m_POWER | m_INT_POL;
>> +    val = v_REG_CLK_INV | v_REG_CLK_SOURCE_SYS | v_PWR_ON | 
>> v_INT_POL_HIGH;
>> +    hdmi_modb(hdmi, HDMI_SYS_CTRL, msk, val);
>> +
>> +    inno_hdmi_set_pwr_mode(hdmi, NORMAL);
>> +}
>> +
>> +static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi)
>> +{
>> +    char info[HDMI_SIZE_AVI_INFOFRAME] = {0};
>> +    int avi_color_mode;
>> +    int i;
>> +
>> +    hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_AVI);
>> +
>> +    info[0] = 0x82;
>> +    info[1] = 0x02;
>> +    info[2] = 0x0D;
>> +    info[3] = info[0] + info[1] + info[2];
>> +
>> +    if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_RGB)
>> +        avi_color_mode = AVI_COLOR_MODE_RGB;
>> +    else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV444)
>> +        avi_color_mode = AVI_COLOR_MODE_YCBCR444;
>> +    else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV422)
>> +        avi_color_mode = AVI_COLOR_MODE_YCBCR422;
>> +    else
>> +        avi_color_mode = AVI_COLOR_MODE_RGB;
>> +
>> +    info[4] = (avi_color_mode << 5);
>> +    info[5] = (AVI_COLORIMETRY_NO_DATA << 6) |
>> +          (AVI_CODED_FRAME_ASPECT_NO_DATA << 4) |
>> +          ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME;
>> +
>> +    info[6] = 0;
>> +    info[7] = hdmi->hdmi_data.vic;
>> +
>> +    if (hdmi->hdmi_data.vic == 6 || hdmi->hdmi_data.vic == 7 ||
>> +        hdmi->hdmi_data.vic == 21 || hdmi->hdmi_data.vic == 22)
>> +        info[8] = 1;
>> +    else
>> +        info[8] = 0;
>> +
>> +    /* Calculate avi info frame checKsum */
>> +    for (i = 4; i < HDMI_SIZE_AVI_INFOFRAME; i++)
>> +        info[3] += info[i];
>> +    info[3] = 0x100 - info[3];
>> +
>> +    for (i = 0; i < HDMI_SIZE_AVI_INFOFRAME; i++)
>> +        hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i, info[i]);
>> +
>> +    return 0;
>> +}
>> +
>> +static int inno_hdmi_config_video_vsi(struct inno_hdmi *hdmi)
>> +{
>> +    char info[HDMI_SIZE_VSI_INFOFRAME] = {0};
>> +    int i;
>> +
>> +    hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, m_PACKET_VSI_EN,
>> +          v_PACKET_VSI_EN(0));
>> +
>> +    hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_VSI);
>> +
>> +    /* Header Bytes */
>> +    info[0] = 0x81;
>> +    info[1] = 0x01;
>> +
>> +    /* PB1 - PB3 contain the 24bit IEEE Registration Identifier */
>> +    info[4] = 0x03;
>> +    info[5] = 0x0c;
>> +    info[6] = 0x00;
>> +
>> +    /* PB4 - HDMI_Video_Format into bits 7:5 */
>> +    info[7] = 0;
>> +
>> +    /*
>> +     * PB5 - Depending on the video format, this byte will contain
>> +     * either the HDMI_VIC code in buts 7:0, OR the 3D_Structure in
>> +     * bits 7:4.
>> +     */
>> +    info[2] = 0x06 - 2;
>> +    info[8] = 0;
>> +    info[9] = 0;
>> +
>> +    info[3] = info[0] + info[1] + info[2];
>> +
>> +    /* Calculate info frame checKsum */
>> +    for (i = 4; i < HDMI_SIZE_VSI_INFOFRAME; i++)
>> +        info[3] += info[i];
>> +    info[3] = 0x100 - info[3];
>> +
>> +    for (i = 0; i < HDMI_SIZE_VSI_INFOFRAME; i++)
>> +        hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i, info[i]);
>> +
>> +    hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, m_PACKET_VSI_EN,
>> +          v_PACKET_VSI_EN(1));
>> +
>> +    return 0;
>> +}
>> +
>> +static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
>> +{
>> +    struct hdmi_data_info *data = &hdmi->hdmi_data;
>> +    int c0_c2_change = 0;
>> +    int csc_enable = 0;
>> +    int csc_mode = 0;
>> +    int auto_csc = 0;
>> +    int value;
>> +    int i;
>> +
>> +    /* Input video mode is SDR RGB24bit, data enable signal from 
>> external */
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL1, v_DE_EXTERNAL |
>> +            v_VIDEO_INPUT_FORMAT(VIDEO_INPUT_SDR_RGB444));
>> +
>> +    /* Input color hardcode to RGB, and output color hardcode to 
>> RGB888 */
>> +    value = v_VIDEO_INPUT_BITS(VIDEO_INPUT_8BITS) |
>> +        v_VIDEO_OUTPUT_COLOR(0) |
>> +        v_VIDEO_INPUT_CSP(0);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL2, value);
>> +
>> +    if (data->enc_out_format == data->enc_out_format) {
>> +        if ((data->enc_in_format == HDMI_COLORSPACE_RGB) ||
>> +            (data->enc_in_format >= HDMI_COLORSPACE_YUV444)) {
>> +            value = v_SOF_DISABLE | v_COLOR_DEPTH_NOT_INDICATED(1);
>> +            hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
>> +
>> +            hdmi_modb(hdmi, HDMI_VIDEO_CONTRL,
>> +                  m_VIDEO_AUTO_CSC | m_VIDEO_C0_C2_SWAP,
>> +                  v_VIDEO_AUTO_CSC(AUTO_CSC_DISABLE) |
>> +                  v_VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE));
>> +            return 0;
>> +        }
>> +    }
>> +
>> +    if (data->colorimetry == HDMI_COLORIMETRY_ITU_601) {
>> +        if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
>> +            (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
>> +            csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT;
>> +            auto_csc = AUTO_CSC_DISABLE;
>> +            c0_c2_change = C0_C2_CHANGE_DISABLE;
>> +            csc_enable = v_CSC_ENABLE;
>> +        } else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
>> +               (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
>> +            csc_mode = CSC_ITU601_16_235_TO_RGB_0_255_8BIT;
>> +            auto_csc = AUTO_CSC_ENABLE;
>> +            c0_c2_change = C0_C2_CHANGE_DISABLE;
>> +            csc_enable = v_CSC_DISABLE;
>> +        }
>> +    } else {
>> +        if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
>> +            (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
>> +            csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT;
>> +            auto_csc = AUTO_CSC_DISABLE;
>> +            c0_c2_change = C0_C2_CHANGE_DISABLE;
>> +            csc_enable = v_CSC_ENABLE;
>> +        } else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
>> +               (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
>> +            csc_mode = CSC_ITU709_16_235_TO_RGB_0_255_8BIT;
>> +            auto_csc = AUTO_CSC_ENABLE;
>> +            c0_c2_change = C0_C2_CHANGE_DISABLE;
>> +            csc_enable = v_CSC_DISABLE;
>> +        }
>> +    }
>> +
>> +    for (i = 0; i < 24; i++)
>> +        hdmi_writeb(hdmi, HDMI_VIDEO_CSC_COEF + i,
>> +                coeff_csc[csc_mode][i]);
>> +
>> +    value = v_SOF_DISABLE | csc_enable | 
>> v_COLOR_DEPTH_NOT_INDICATED(1);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
>> +    hdmi_modb(hdmi, HDMI_VIDEO_CONTRL, m_VIDEO_AUTO_CSC |
>> +          m_VIDEO_C0_C2_SWAP, v_VIDEO_AUTO_CSC(auto_csc) |
>> +          v_VIDEO_C0_C2_SWAP(c0_c2_change));
>> +
>> +    return 0;
>> +}
>> +
>> +static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi,
>> +                     struct drm_display_mode *mode)
>> +{
>> +    int value;
>> +
>> +    /* Set detail external video timing polarity and interlace mode */
>> +    value = v_EXTERANL_VIDEO(1);
>> +    value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
>> +         v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
>> +    value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
>> +         v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
>> +    value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
>> +         v_INETLACE(1) : v_INETLACE(0);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
>> +
>> +    /* Set detail external video timing */
>> +    value = mode->htotal;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
>> +
>> +    value = mode->htotal - mode->hdisplay;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
>> +
>> +    value = mode->hsync_start - mode->hdisplay;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
>> +
>> +    value = mode->hsync_end - mode->hsync_start;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
>> +
>> +    value = mode->vtotal;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
>> +
>> +    value = mode->vtotal - mode->vdisplay;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
>> +
>> +    value = mode->vsync_start - mode->vdisplay;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
>> +
>> +    value = mode->vsync_end - mode->vsync_start;
>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
>> +
>> +    hdmi_writeb(hdmi, HDMI_PHY_PRE_DIV_RATIO, 0x1e);
>> +    hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_LOW, 0x2c);
>> +    hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH, 0x01);
>> +
>> +    return 0;
>> +}
>> +
>> +static int inno_hdmi_setup(struct inno_hdmi *hdmi,
>> +               struct drm_display_mode *mode)
>> +{
>> +    int value;

Where be used?
I guess you should remove it.



-
Caesar
>> +
>> +    hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
>> +
>> +    hdmi->hdmi_data.enc_in_format = HDMI_COLORSPACE_RGB;
>> +    hdmi->hdmi_data.enc_out_format = HDMI_COLORSPACE_RGB;
>> +
>> +    if ((hdmi->hdmi_data.vic == 6) || (hdmi->hdmi_data.vic == 7) ||
>> +        (hdmi->hdmi_data.vic == 21) || (hdmi->hdmi_data.vic == 22) ||
>> +        (hdmi->hdmi_data.vic == 2) || (hdmi->hdmi_data.vic == 3) ||
>> +        (hdmi->hdmi_data.vic == 17) || (hdmi->hdmi_data.vic == 18))
>> +        hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601;
>> +    else
>> +        hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
>> +
>> +    /* Mute video and audio output */
>> +    hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
>> +          v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1));
>> +
>> +    /* Set HDMI Mode */
>> +    hdmi_writeb(hdmi, HDMI_HDCP_CTRL,
>> +            v_HDMI_DVI(hdmi->hdmi_data.sink_is_hdmi));
>> +
>> +    inno_hdmi_config_video_timing(hdmi, mode);
>> +
>> +    inno_hdmi_config_video_csc(hdmi);
>> +
>> +    if (hdmi->hdmi_data.sink_is_hdmi) {
>> +        inno_hdmi_config_video_avi(hdmi);
>> +        inno_hdmi_config_video_vsi(hdmi);
>> +    }
>> +
>> +    /*
>> +     * When IP controller have configured to an accurate video
>> +     * timing, then the TMDS clock source would be switched to
>> +     * DCLK_LCDC, so we need to init the TMDS rate to mode pixel
>> +     * clock rate, and reconfigure the DDC clock.
>> +     */
>> +    hdmi->tmds_rate = mode->clock * 1000;
>> +    inno_hdmi_i2c_init(hdmi);
>> +
>> +    /* Unmute video and audio output */
>> +    hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
>> +          v_AUDIO_MUTE(0) | v_VIDEO_MUTE(0));
>> +
>> +    return 0;
>> +}
>> +
>> +static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder,
>> +                       struct drm_display_mode *mode,
>> +                       struct drm_display_mode *adj_mode)
>> +{
>> +    struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
>> +
>> +    inno_hdmi_setup(hdmi, adj_mode);
>> +
>> +    /* Store the display mode for plugin/DKMS poweron events */
>
> I think DKMS should be DPMS.
>
>> +    memcpy(&hdmi->previous_mode, adj_mode, 
>> sizeof(hdmi->previous_mode));
>> +}
>> +
>> +static void inno_hdmi_encoder_enable(struct drm_encoder *encoder)
>> +{
>> +    struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
>> +
>> +    inno_hdmi_set_pwr_mode(hdmi, NORMAL);
>> +}
>> +
>> +static void inno_hdmi_encoder_disable(struct drm_encoder *encoder)
>> +{
>> +    struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
>> +
>> +    inno_hdmi_set_pwr_mode(hdmi, LOWER_PWR);
>> +}
>> +
>> +static void inno_hdmi_encoder_commit(struct drm_encoder *encoder)
>> +{
>> +}
>> +
>> +static void inno_hdmi_encoder_prepare(struct drm_encoder *encoder)
>> +{
>> +    rockchip_drm_crtc_mode_config(encoder->crtc, 
>> DRM_MODE_CONNECTOR_HDMIA,
>> +                      ROCKCHIP_OUT_MODE_P888);
>
> Can you move mode_config into inno_hdmi_encoder_enable, and remove 
> .prepare and .commit?
>
>> +}
>> +
>> +static bool inno_hdmi_encoder_mode_fixup(struct drm_encoder *encoder,
>> +                     const struct drm_display_mode *mode,
>> +                     struct drm_display_mode *adj_mode)
>> +{
>> +    return true;
>> +}
>> +
>> +static struct drm_encoder_helper_funcs 
>> inno_hdmi_encoder_helper_funcs = {
>> +    .enable     = inno_hdmi_encoder_enable,
>> +    .disable    = inno_hdmi_encoder_disable,
>> +    .mode_fixup = inno_hdmi_encoder_mode_fixup,
>> +    .mode_set   = inno_hdmi_encoder_mode_set,
>> +    .prepare    = inno_hdmi_encoder_prepare,
>> +    .commit     = inno_hdmi_encoder_commit,
>
> On drm atomic, I think if support .enable and .disable callbacks, then 
> .prepare and .commit is not needed.
>
>> +};
>> +
>> +static struct drm_encoder_funcs inno_hdmi_encoder_funcs = {
>> +    .destroy = drm_encoder_cleanup,
>> +};
>> +
>> +static enum drm_connector_status
>> +inno_hdmi_connector_detect(struct drm_connector *connector, bool force)
>> +{
>> +    struct inno_hdmi *hdmi = to_inno_hdmi(connector);
>> +
>> +    return (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
>> +        connector_status_connected : connector_status_disconnected;
>> +}
>> +
>> +static int inno_hdmi_connector_get_modes(struct drm_connector 
>> *connector)
>> +{
>> +    struct inno_hdmi *hdmi = to_inno_hdmi(connector);
>> +    struct edid *edid;
>> +    int ret = 0;
>> +
>> +    if (!hdmi->ddc)
>> +        return 0;
>> +
>> +    edid = drm_get_edid(connector, hdmi->ddc);
>> +    if (edid) {
>> +        hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
>> +        hdmi->hdmi_data.sink_has_audio = 
>> drm_detect_monitor_audio(edid);
>> +        drm_mode_connector_update_edid_property(connector, edid);
>> +        ret = drm_add_edid_modes(connector, edid);
>> +        kfree(edid);
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +static enum drm_mode_status
>> +inno_hdmi_connector_mode_valid(struct drm_connector *connector,
>> +                   struct drm_display_mode *mode)
>> +{
>> +    return MODE_OK;
>> +}
>> +
>> +static struct drm_encoder *
>> +inno_hdmi_connector_best_encoder(struct drm_connector *connector)
>> +{
>> +    struct inno_hdmi *hdmi = to_inno_hdmi(connector);
>> +
>> +    return &hdmi->encoder;
>> +}
>> +
>> +static int
>> +inno_hdmi_probe_single_connector_modes(struct drm_connector *connector,
>> +                       uint32_t maxX, uint32_t maxY)
>> +{
>> +    return drm_helper_probe_single_connector_modes(connector, 1920, 
>> 1080);
>> +}
>> +
>> +static void inno_hdmi_connector_destroy(struct drm_connector 
>> *connector)
>> +{
>> +    drm_connector_unregister(connector);
>> +    drm_connector_cleanup(connector);
>> +}
>> +
>> +static struct drm_connector_funcs inno_hdmi_connector_funcs = {
>> +    .dpms = drm_atomic_helper_connector_dpms,
>> +    .fill_modes = inno_hdmi_probe_single_connector_modes,
>> +    .detect = inno_hdmi_connector_detect,
>> +    .destroy = inno_hdmi_connector_destroy,
>> +    .reset = drm_atomic_helper_connector_reset,
>> +    .atomic_duplicate_state = 
>> drm_atomic_helper_connector_duplicate_state,
>> +    .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
>> +};
>> +
>> +static struct drm_connector_helper_funcs 
>> inno_hdmi_connector_helper_funcs = {
>> +    .get_modes = inno_hdmi_connector_get_modes,
>> +    .mode_valid = inno_hdmi_connector_mode_valid,
>> +    .best_encoder = inno_hdmi_connector_best_encoder,
>> +};
>> +
>> +static int inno_hdmi_register(struct drm_device *drm, struct 
>> inno_hdmi *hdmi)
>> +{
>> +    struct drm_encoder *encoder = &hdmi->encoder;
>> +
>> +    encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, 
>> hdmi->dev->of_node);
>
> Keep 80 characters :-) .
>
>> +    /*
>> +     * If we failed to find the CRTC(s) which this encoder is
>> +     * supposed to be connected to, it's because the CRTC has
>> +     * not been registered yet.  Defer probing, and hope that
>> +     * the required CRTC is added later.
>> +     */
>> +    if (encoder->possible_crtcs == 0)
>> +        return -EPROBE_DEFER;
>> +
>> +    drm_encoder_helper_add(encoder, &inno_hdmi_encoder_helper_funcs);
>> +    drm_encoder_init(drm, encoder, &inno_hdmi_encoder_funcs,
>> +             DRM_MODE_ENCODER_TMDS, NULL);
>> +
>> +    hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
>> +
>> +    drm_connector_helper_add(&hdmi->connector,
>> +                 &inno_hdmi_connector_helper_funcs);
>> +    drm_connector_init(drm, &hdmi->connector, 
>> &inno_hdmi_connector_funcs,
>> +               DRM_MODE_CONNECTOR_HDMIA);
>> +
>> +    drm_mode_connector_attach_encoder(&hdmi->connector, encoder);
>> +
>> +    return 0;
>> +}
>> +
>> +static irqreturn_t inno_hdmi_i2c_irq(struct inno_hdmi *hdmi)
>> +{
>> +    struct inno_hdmi_i2c *i2c = hdmi->i2c;
>> +    u8 stat;
>> +
>> +    stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
>> +    if (!(stat & m_INT_EDID_READY))
>> +        return IRQ_NONE;
>> +
>> +    /* Clear HDMI EDID interrupt flag */
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
>> +
>> +    complete(&i2c->cmp);
>> +
>> +    return IRQ_HANDLED;
>> +}
>> +
>> +static irqreturn_t inno_hdmi_hardirq(int irq, void *dev_id)
>> +{
>> +    struct inno_hdmi *hdmi = dev_id;
>> +    irqreturn_t ret = IRQ_NONE;
>> +    u8 interrupt;
>> +
>> +    if (hdmi->i2c)
>> +        ret = inno_hdmi_i2c_irq(hdmi);
>> +
>> +    interrupt = hdmi_readb(hdmi, HDMI_STATUS);
>> +    if (interrupt & m_INT_HOTPLUG) {
>> +        hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG);
>> +        ret = IRQ_WAKE_THREAD;
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +static irqreturn_t inno_hdmi_irq(int irq, void *dev_id)
>> +{
>> +    struct inno_hdmi *hdmi = dev_id;
>> +
>> +    drm_helper_hpd_irq_event(hdmi->connector.dev);
>> +
>> +    return IRQ_HANDLED;
>> +}
>> +
>> +static int inno_hdmi_i2c_wait(struct inno_hdmi *hdmi)
>> +{
>> +    struct inno_hdmi_i2c *i2c = hdmi->i2c;
>> +    int stat;
>> +
>> +    stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
>> +    if (!stat) {
>> +        stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
>> +        if (!stat)
>> +            return -EAGAIN;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static int inno_hdmi_i2c_read(struct inno_hdmi *hdmi, struct i2c_msg 
>> *msgs)
>> +{
>> +    int length = msgs->len;
>> +    u8 *buf = msgs->buf;
>> +    int ret;
>> +
>> +    ret = inno_hdmi_i2c_wait(hdmi);
>> +    if (ret)
>> +        return ret;
>> +
>> +    while (length--)
>> +        *buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
>> +
>> +    return 0;
>> +}
>> +
>> +static int inno_hdmi_i2c_write(struct inno_hdmi *hdmi, struct 
>> i2c_msg *msgs)
>> +{
>> +    struct inno_hdmi_i2c *i2c = hdmi->i2c;
>> +
>> +    /*
>> +     * The DDC module only support read EDID message, so
>> +     * we assume that each word write to this i2c adapter
>> +     * should be the offset of EDID word address.
>> +     */
>> +    if ((msgs->len != 1) ||
>> +        ((msgs->addr != DDC_ADDR) && (msgs->addr != DDC_SEGMENT_ADDR)))
>> +        return -EINVAL;
>> +
>> +    reinit_completion(&i2c->cmp);
>> +
>> +    if (msgs->addr == DDC_SEGMENT_ADDR)
>> +        hdmi->i2c->segment_addr = msgs->buf[0];
>> +    if (msgs->addr == DDC_ADDR)
>> +        hdmi->i2c->ddc_addr = msgs->buf[0];
>> +
>> +    /* Set edid fifo first addr */
>> +    hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
>> +
>> +    /* Set edid word address 0x00/0x80 */
>> +    hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
>> +
>> +    /* Set edid segment pointer */
>> +    hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, 
>> hdmi->i2c->segment_addr);
>> +
>> +    return 0;
>> +}
>> +
>> +static int inno_hdmi_i2c_xfer(struct i2c_adapter *adap,
>> +                  struct i2c_msg *msgs, int num)
>> +{
>> +    struct inno_hdmi *hdmi = i2c_get_adapdata(adap);
>> +    struct inno_hdmi_i2c *i2c = hdmi->i2c;
>> +    int i, ret = 0;
>> +
>> +    mutex_lock(&i2c->lock);
>> +
>> +    /* Clear the EDID interrupt flag and unmute the interrupt */
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY);
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
>> +
>> +    for (i = 0; i < num; i++) {
>> +        dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n",
>> +            i + 1, num, msgs[i].len, msgs[i].flags);
>> +
>> +        if (msgs[i].flags & I2C_M_RD)
>> +            ret = inno_hdmi_i2c_read(hdmi, &msgs[i]);
>> +        else
>> +            ret = inno_hdmi_i2c_write(hdmi, &msgs[i]);
>> +
>> +        if (ret < 0)
>> +            break;
>> +    }
>> +
>> +    if (!ret)
>> +        ret = num;
>> +
>> +    /* Mute HDMI EDID interrupt */
>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
>> +
>> +    mutex_unlock(&i2c->lock);
>> +
>> +    return ret;
>> +}
>> +
>> +static u32 inno_hdmi_i2c_func(struct i2c_adapter *adapter)
>> +{
>> +    return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
>> +}
>> +
>> +static const struct i2c_algorithm inno_hdmi_algorithm = {
>> +    .master_xfer    = inno_hdmi_i2c_xfer,
>> +    .functionality    = inno_hdmi_i2c_func,
>> +};
>> +
>> +static struct i2c_adapter *inno_hdmi_i2c_adapter(struct inno_hdmi 
>> *hdmi)
>> +{
>> +    struct i2c_adapter *adap;
>> +    struct inno_hdmi_i2c *i2c;
>> +    int ret;
>> +
>> +    i2c = devm_kzalloc(hdmi->dev, sizeof(*i2c), GFP_KERNEL);
>> +    if (!i2c)
>> +        return ERR_PTR(-ENOMEM);
>> +
>> +    mutex_init(&i2c->lock);
>> +    init_completion(&i2c->cmp);
>> +
>> +    adap = &i2c->adap;
>> +    adap->class = I2C_CLASS_DDC;
>> +    adap->owner = THIS_MODULE;
>> +    adap->dev.parent = hdmi->dev;
>> +    adap->dev.of_node = hdmi->dev->of_node;
>> +    adap->algo = &inno_hdmi_algorithm;
>> +    strlcpy(adap->name, "Inno HDMI", sizeof(adap->name));
>> +    i2c_set_adapdata(adap, hdmi);
>> +
>> +    ret = i2c_add_adapter(adap);
>> +    if (ret) {
>> +        dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name);
>> +        devm_kfree(hdmi->dev, i2c);
>> +        return ERR_PTR(ret);
>> +    }
>> +
>> +    hdmi->i2c = i2c;
>> +
>> +    dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name);
>> +
>> +    return adap;
>> +}
>> +
>> +static int inno_hdmi_bind(struct device *dev, struct device *master,
>> +                 void *data)
>> +{
>> +    struct platform_device *pdev = to_platform_device(dev);
>> +    struct drm_device *drm = data;
>> +    struct inno_hdmi *hdmi;
>> +    struct resource *iores;
>> +    int irq;
>> +    int ret;
>> +
>> +    hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
>> +    if (!hdmi)
>> +        return -ENOMEM;
>> +
>> +    hdmi->dev = dev;
>> +    hdmi->drm_dev = drm;
>> +
>> +    iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +    if (!iores)
>> +        return -ENXIO;
>> +
>> +    hdmi->regs = devm_ioremap_resource(dev, iores);
>> +    if (IS_ERR(hdmi->regs))
>> +        return PTR_ERR(hdmi->regs);
>> +
>> +    hdmi->pclk = devm_clk_get(hdmi->dev, "pclk");
>> +    if (IS_ERR(hdmi->pclk)) {
>> +        dev_err(hdmi->dev, "Unable to get HDMI pclk clk\n");
>> +        return PTR_ERR(hdmi->pclk);
>> +    }
>> +
>> +    ret = clk_prepare_enable(hdmi->pclk);
>> +    if (ret) {
>> +        dev_err(hdmi->dev, "Cannot enable HDMI pclk clock: %d\n", ret);
>> +        return ret;
>> +    }
>> +
>> +    irq = platform_get_irq(pdev, 0);
>> +    if (irq < 0)
>> +        return irq;
>> +
>> +    inno_hdmi_reset(hdmi);
>> +
>> +    hdmi->ddc = inno_hdmi_i2c_adapter(hdmi);
>> +    if (IS_ERR(hdmi->ddc)) {
>> +        hdmi->ddc = NULL;
>> +        return PTR_ERR(hdmi->ddc);
>> +    }
>> +
>> +    /*
>> +     * When IP controller haven't configured to an accurate video
>> +     * timing, then the TMDS clock source would be switched to
>> +     * PCLK_HDMI, so we need to init the TMDS rate to PCLK rate,
>> +     * and reconfigure the DDC clock.
>> +     */
>> +    hdmi->tmds_rate = clk_get_rate(hdmi->pclk);
>> +    inno_hdmi_i2c_init(hdmi);
>> +
>> +    ret = inno_hdmi_register(drm, hdmi);
>> +    if (ret)
>> +        return ret;
>> +
>> +    dev_set_drvdata(dev, hdmi);
>> +
>> +    /* Unmute hotplug interrupt */
>> +    hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, 
>> v_MASK_INT_HOTPLUG(1));
>> +
>> +    ret = devm_request_threaded_irq(dev, irq, inno_hdmi_hardirq,
>> +                    inno_hdmi_irq, IRQF_SHARED,
>> +                    dev_name(dev), hdmi);
>> +
>> +    return ret;
>> +}
>> +
>> +static void inno_hdmi_unbind(struct device *dev, struct device *master,
>> +                 void *data)
>> +{
>> +    struct inno_hdmi *hdmi = dev_get_drvdata(dev);
>> +
>> + hdmi->connector.funcs->destroy(&hdmi->connector);
>> +    hdmi->encoder.funcs->destroy(&hdmi->encoder);
>> +
>> +    clk_disable_unprepare(hdmi->pclk);
>> +    i2c_put_adapter(hdmi->ddc);
>> +}
>> +
>> +static const struct component_ops inno_hdmi_ops = {
>> +    .bind    = inno_hdmi_bind,
>> +    .unbind    = inno_hdmi_unbind,
>> +};
>> +
>> +static int inno_hdmi_probe(struct platform_device *pdev)
>> +{
>> +    return component_add(&pdev->dev, &inno_hdmi_ops);
>> +}
>> +
>> +static int inno_hdmi_remove(struct platform_device *pdev)
>> +{
>> +    component_del(&pdev->dev, &inno_hdmi_ops);
>> +
>> +    return 0;
>> +}
>> +
>> +static const struct of_device_id inno_hdmi_dt_ids[] = {
>> +    { .compatible = "rockchip,rk3036-inno-hdmi",
>> +    },
>> +    {},
>> +};
>> +MODULE_DEVICE_TABLE(of, inno_hdmi_dt_ids);
>> +
>> +static struct platform_driver inno_hdmi_driver = {
>> +    .probe  = inno_hdmi_probe,
>> +    .remove = inno_hdmi_remove,
>> +    .driver = {
>> +        .name = "innohdmi-rockchip",
>> +        .of_match_table = inno_hdmi_dt_ids,
>> +    },
>> +};
>> +
>> +module_platform_driver(inno_hdmi_driver);
>> +
>> +MODULE_AUTHOR("Zheng Yang <zhengyang at rock-chips.com>");
>> +MODULE_AUTHOR("Yakir Yang <ykk at rock-chips.com>");
>> +MODULE_DESCRIPTION("Rockchip Specific INNO-HDMI Driver");
>> +MODULE_LICENSE("GPL");
>> +MODULE_ALIAS("platform:innohdmi-rockchip");
>> diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.h 
>> b/drivers/gpu/drm/rockchip/inno_hdmi.h
>> new file mode 100644
>> index 0000000..4ff17ad
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/inno_hdmi.h
>> @@ -0,0 +1,364 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + *    Zheng Yang <zhengyang at rock-chips.com>
>> + *    Yakir Yang <ykk at rock-chips.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#ifndef __INNO_HDMI_H__
>> +#define __INNO_HDMI_H__
>> +
>> +#define DDC_SEGMENT_ADDR        0x30
>> +
>> +enum PWR_MODE {
>> +    NORMAL,
>> +    LOWER_PWR,
>> +};
>> +
>> +#define HDMI_SCL_RATE            (100*1000)
>> +#define DDC_BUS_FREQ_L            0x4b
>> +#define DDC_BUS_FREQ_H            0x4c
>> +
>> +#define HDMI_SYS_CTRL            0x00
>> +#define m_RST_ANALOG            (1 << 6)
>> +#define v_RST_ANALOG            (0 << 6)
>> +#define v_NOT_RST_ANALOG        (1 << 6)
>> +#define m_RST_DIGITAL            (1 << 5)
>> +#define v_RST_DIGITAL            (0 << 5)
>> +#define v_NOT_RST_DIGITAL        (1 << 5)
>> +#define m_REG_CLK_INV            (1 << 4)
>> +#define v_REG_CLK_NOT_INV        (0 << 4)
>> +#define v_REG_CLK_INV            (1 << 4)
>> +#define m_VCLK_INV            (1 << 3)
>> +#define v_VCLK_NOT_INV            (0 << 3)
>> +#define v_VCLK_INV            (1 << 3)
>> +#define m_REG_CLK_SOURCE        (1 << 2)
>> +#define v_REG_CLK_SOURCE_TMDS        (0 << 2)
>> +#define v_REG_CLK_SOURCE_SYS        (1 << 2)
>> +#define m_POWER                (1 << 1)
>> +#define v_PWR_ON            (0 << 1)
>> +#define v_PWR_OFF            (1 << 1)
>> +#define m_INT_POL            (1 << 0)
>> +#define v_INT_POL_HIGH            1
>> +#define v_INT_POL_LOW            0
>> +
>> +#define HDMI_VIDEO_CONTRL1        0x01
>> +#define m_VIDEO_INPUT_FORMAT        (7 << 1)
>> +#define m_DE_SOURCE            (1 << 0)
>> +#define v_VIDEO_INPUT_FORMAT(n)        (n << 1)
>> +#define v_DE_EXTERNAL            1
>> +#define v_DE_INTERNAL            0
>> +enum {
>> +    VIDEO_INPUT_SDR_RGB444 = 0,
>> +    VIDEO_INPUT_DDR_RGB444 = 5,
>> +    VIDEO_INPUT_DDR_YCBCR422 = 6
>> +};
>> +
>> +#define HDMI_VIDEO_CONTRL2        0x02
>> +#define m_VIDEO_OUTPUT_COLOR        (3 << 6)
>> +#define m_VIDEO_INPUT_BITS        (3 << 4)
>> +#define m_VIDEO_INPUT_CSP        (1 << 0)
>> +#define v_VIDEO_OUTPUT_COLOR(n)        (((n) & 0x3) << 6)
>> +#define v_VIDEO_INPUT_BITS(n)        (n << 4)
>> +#define v_VIDEO_INPUT_CSP(n)        (n << 0)
>> +enum {
>> +    VIDEO_INPUT_12BITS = 0,
>> +    VIDEO_INPUT_10BITS = 1,
>> +    VIDEO_INPUT_REVERT = 2,
>> +    VIDEO_INPUT_8BITS = 3,
>> +};
>> +
>> +#define HDMI_VIDEO_CONTRL        0x03
>> +#define m_VIDEO_AUTO_CSC        (1 << 7)
>> +#define v_VIDEO_AUTO_CSC(n)        (n << 7)
>> +#define m_VIDEO_C0_C2_SWAP        (1 << 0)
>> +#define v_VIDEO_C0_C2_SWAP(n)        (n << 0)
>> +enum {
>> +    C0_C2_CHANGE_ENABLE = 0,
>> +    C0_C2_CHANGE_DISABLE = 1,
>> +    AUTO_CSC_DISABLE = 0,
>> +    AUTO_CSC_ENABLE = 1,
>> +};
>> +
>> +#define HDMI_VIDEO_CONTRL3        0x04
>> +#define m_COLOR_DEPTH_NOT_INDICATED    (1 << 4)
>> +#define m_SOF                (1 << 3)
>> +#define m_COLOR_RANGE            (1 << 2)
>> +#define m_CSC                (1 << 0)
>> +#define v_COLOR_DEPTH_NOT_INDICATED(n)    ((n) << 4)
>> +#define v_SOF_ENABLE            (0 << 3)
>> +#define v_SOF_DISABLE            (1 << 3)
>> +#define v_COLOR_RANGE_FULL        (1 << 2)
>> +#define v_COLOR_RANGE_LIMITED        (0 << 2)
>> +#define v_CSC_ENABLE            1
>> +#define v_CSC_DISABLE            0
>> +
>> +#define HDMI_AV_MUTE            0x05
>> +#define m_AVMUTE_CLEAR            (1 << 7)
>> +#define m_AVMUTE_ENABLE            (1 << 6)
>> +#define m_AUDIO_MUTE            (1 << 1)
>> +#define m_VIDEO_BLACK            (1 << 0)
>> +#define v_AVMUTE_CLEAR(n)        (n << 7)
>> +#define v_AVMUTE_ENABLE(n)        (n << 6)
>> +#define v_AUDIO_MUTE(n)            (n << 1)
>> +#define v_VIDEO_MUTE(n)            (n << 0)
>> +
>> +#define HDMI_VIDEO_TIMING_CTL        0x08
>> +#define v_HSYNC_POLARITY(n)        (n << 3)
>> +#define v_VSYNC_POLARITY(n)        (n << 2)
>> +#define v_INETLACE(n)            (n << 1)
>> +#define v_EXTERANL_VIDEO(n)        (n << 0)
>> +
>> +#define HDMI_VIDEO_EXT_HTOTAL_L        0x09
>> +#define HDMI_VIDEO_EXT_HTOTAL_H        0x0a
>> +#define HDMI_VIDEO_EXT_HBLANK_L        0x0b
>> +#define HDMI_VIDEO_EXT_HBLANK_H        0x0c
>> +#define HDMI_VIDEO_EXT_HDELAY_L        0x0d
>> +#define HDMI_VIDEO_EXT_HDELAY_H        0x0e
>> +#define HDMI_VIDEO_EXT_HDURATION_L    0x0f
>> +#define HDMI_VIDEO_EXT_HDURATION_H    0x10
>> +#define HDMI_VIDEO_EXT_VTOTAL_L        0x11
>> +#define HDMI_VIDEO_EXT_VTOTAL_H        0x12
>> +#define HDMI_VIDEO_EXT_VBLANK        0x13
>> +#define HDMI_VIDEO_EXT_VDELAY        0x14
>> +#define HDMI_VIDEO_EXT_VDURATION    0x15
>> +
>> +#define HDMI_VIDEO_CSC_COEF        0x18
>> +
>> +#define HDMI_AUDIO_CTRL1        0x35
>> +enum {
>> +    CTS_SOURCE_INTERNAL = 0,
>> +    CTS_SOURCE_EXTERNAL = 1,
>> +};
>> +#define v_CTS_SOURCE(n)            (n << 7)
>> +
>> +enum {
>> +    DOWNSAMPLE_DISABLE = 0,
>> +    DOWNSAMPLE_1_2 = 1,
>> +    DOWNSAMPLE_1_4 = 2,
>> +};
>> +#define v_DOWN_SAMPLE(n)        (n << 5)
>> +
>> +enum {
>> +    AUDIO_SOURCE_IIS = 0,
>> +    AUDIO_SOURCE_SPDIF = 1,
>> +};
>> +#define v_AUDIO_SOURCE(n)        (n << 3)
>> +
>> +#define v_MCLK_ENABLE(n)        (n << 2)
>> +enum {
>> +    MCLK_128FS = 0,
>> +    MCLK_256FS = 1,
>> +    MCLK_384FS = 2,
>> +    MCLK_512FS = 3,
>> +};
>> +#define v_MCLK_RATIO(n)            (n)
>> +
>> +#define AUDIO_SAMPLE_RATE        0x37
>> +enum {
>> +    AUDIO_32K = 0x3,
>> +    AUDIO_441K = 0x0,
>> +    AUDIO_48K = 0x2,
>> +    AUDIO_882K = 0x8,
>> +    AUDIO_96K = 0xa,
>> +    AUDIO_1764K = 0xc,
>> +    AUDIO_192K = 0xe,
>> +};
>> +
>> +#define AUDIO_I2S_MODE            0x38
>> +enum {
>> +    I2S_CHANNEL_1_2 = 1,
>> +    I2S_CHANNEL_3_4 = 3,
>> +    I2S_CHANNEL_5_6 = 7,
>> +    I2S_CHANNEL_7_8 = 0xf
>> +};
>> +#define v_I2S_CHANNEL(n)        ((n) << 2)
>> +enum {
>> +    I2S_STANDARD = 0,
>> +    I2S_LEFT_JUSTIFIED = 1,
>> +    I2S_RIGHT_JUSTIFIED = 2,
>> +};
>> +#define v_I2S_MODE(n)            (n)
>> +
>> +#define AUDIO_I2S_MAP            0x39
>> +#define AUDIO_I2S_SWAPS_SPDIF        0x3a
>> +#define v_SPIDF_FREQ(n)            (n)
>> +
>> +#define N_32K                0x1000
>> +#define N_441K                0x1880
>> +#define N_882K                0x3100
>> +#define N_1764K                0x6200
>> +#define N_48K                0x1800
>> +#define N_96K                0x3000
>> +#define N_192K                0x6000
>> +
>> +#define HDMI_AUDIO_CHANNEL_STATUS    0x3e
>> +#define m_AUDIO_STATUS_NLPCM        (1 << 7)
>> +#define m_AUDIO_STATUS_USE        (1 << 6)
>> +#define m_AUDIO_STATUS_COPYRIGHT    (1 << 5)
>> +#define m_AUDIO_STATUS_ADDITION        (3 << 2)
>> +#define m_AUDIO_STATUS_CLK_ACCURACY    (2 << 0)
>> +#define v_AUDIO_STATUS_NLPCM(n)        ((n & 1) << 7)
>> +#define AUDIO_N_H            0x3f
>> +#define AUDIO_N_M            0x40
>> +#define AUDIO_N_L            0x41
>> +
>> +#define HDMI_AUDIO_CTS_H        0x45
>> +#define HDMI_AUDIO_CTS_M        0x46
>> +#define HDMI_AUDIO_CTS_L        0x47
>> +
>> +#define HDMI_DDC_CLK_L            0x4b
>> +#define HDMI_DDC_CLK_H            0x4c
>> +
>> +#define HDMI_EDID_SEGMENT_POINTER    0x4d
>> +#define HDMI_EDID_WORD_ADDR        0x4e
>> +#define HDMI_EDID_FIFO_OFFSET        0x4f
>> +#define HDMI_EDID_FIFO_ADDR        0x50
>> +
>> +#define HDMI_PACKET_SEND_MANUAL        0x9c
>> +#define HDMI_PACKET_SEND_AUTO        0x9d
>> +#define m_PACKET_GCP_EN            (1 << 7)
>> +#define m_PACKET_MSI_EN            (1 << 6)
>> +#define m_PACKET_SDI_EN            (1 << 5)
>> +#define m_PACKET_VSI_EN            (1 << 4)
>> +#define v_PACKET_GCP_EN(n)        ((n & 1) << 7)
>> +#define v_PACKET_MSI_EN(n)        ((n & 1) << 6)
>> +#define v_PACKET_SDI_EN(n)        ((n & 1) << 5)
>> +#define v_PACKET_VSI_EN(n)        ((n & 1) << 4)
>> +
>> +#define HDMI_CONTROL_PACKET_BUF_INDEX    0x9f
>> +enum {
>> +    INFOFRAME_VSI = 0x05,
>> +    INFOFRAME_AVI = 0x06,
>> +    INFOFRAME_AAI = 0x08,
>> +};
>> +
>> +#define HDMI_CONTROL_PACKET_ADDR    0xa0
>> +#define HDMI_SIZE_VSI_INFOFRAME        0x0A
>> +#define HDMI_SIZE_AVI_INFOFRAME        0x11
>> +#define HDMI_SIZE_AUDIO_INFOFRAME    0x0F
>> +enum {
>> +    AVI_COLOR_MODE_RGB = 0,
>> +    AVI_COLOR_MODE_YCBCR422 = 1,
>> +    AVI_COLOR_MODE_YCBCR444 = 2,
>> +    AVI_COLORIMETRY_NO_DATA = 0,
>> +
>> +    AVI_COLORIMETRY_SMPTE_170M = 1,
>> +    AVI_COLORIMETRY_ITU709 = 2,
>> +    AVI_COLORIMETRY_EXTENDED = 3,
>> +
>> +    AVI_CODED_FRAME_ASPECT_NO_DATA = 0,
>> +    AVI_CODED_FRAME_ASPECT_4_3 = 1,
>> +    AVI_CODED_FRAME_ASPECT_16_9 = 2,
>> +
>> +    ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME = 0x08,
>> +    ACTIVE_ASPECT_RATE_4_3 = 0x09,
>> +    ACTIVE_ASPECT_RATE_16_9 = 0x0A,
>> +    ACTIVE_ASPECT_RATE_14_9 = 0x0B,
>> +};
>> +
>> +#define HDMI_HDCP_CTRL            0x52
>> +#define m_HDMI_DVI            (1 << 1)
>> +#define v_HDMI_DVI(n)            (n << 1)
>> +
>> +#define HDMI_INTERRUPT_MASK1        0xc0
>> +#define HDMI_INTERRUPT_STATUS1        0xc1
>> +#define    m_INT_ACTIVE_VSYNC        (1 << 5)
>> +#define m_INT_EDID_READY        (1 << 2)
>> +
>> +#define HDMI_INTERRUPT_MASK2        0xc2
>> +#define HDMI_INTERRUPT_STATUS2        0xc3
>> +#define m_INT_HDCP_ERR            (1 << 7)
>> +#define m_INT_BKSV_FLAG            (1 << 6)
>> +#define m_INT_HDCP_OK            (1 << 4)
>> +
>> +#define HDMI_STATUS            0xc8
>> +#define m_HOTPLUG            (1 << 7)
>> +#define m_MASK_INT_HOTPLUG        (1 << 5)
>> +#define m_INT_HOTPLUG            (1 << 1)
>> +#define v_MASK_INT_HOTPLUG(n)        ((n & 0x1) << 5)
>> +
>> +#define HDMI_COLORBAR                   0xc9
>> +
>> +#define HDMI_PHY_SYNC            0xce
>> +#define HDMI_PHY_SYS_CTL        0xe0
>> +#define m_TMDS_CLK_SOURCE        (1 << 5)
>> +#define v_TMDS_FROM_PLL            (0 << 5)
>> +#define v_TMDS_FROM_GEN            (1 << 5)
>> +#define m_PHASE_CLK            (1 << 4)
>> +#define v_DEFAULT_PHASE            (0 << 4)
>> +#define v_SYNC_PHASE            (1 << 4)
>> +#define m_TMDS_CURRENT_PWR        (1 << 3)
>> +#define v_TURN_ON_CURRENT        (0 << 3)
>> +#define v_CAT_OFF_CURRENT        (1 << 3)
>> +#define m_BANDGAP_PWR            (1 << 2)
>> +#define v_BANDGAP_PWR_UP        (0 << 2)
>> +#define v_BANDGAP_PWR_DOWN        (1 << 2)
>> +#define m_PLL_PWR            (1 << 1)
>> +#define v_PLL_PWR_UP            (0 << 1)
>> +#define v_PLL_PWR_DOWN            (1 << 1)
>> +#define m_TMDS_CHG_PWR            (1 << 0)
>> +#define v_TMDS_CHG_PWR_UP        (0 << 0)
>> +#define v_TMDS_CHG_PWR_DOWN        (1 << 0)
>> +
>> +#define HDMI_PHY_CHG_PWR        0xe1
>> +#define v_CLK_CHG_PWR(n)        ((n & 1) << 3)
>> +#define v_DATA_CHG_PWR(n)        ((n & 7) << 0)
>> +
>> +#define HDMI_PHY_DRIVER            0xe2
>> +#define v_CLK_MAIN_DRIVER(n)        (n << 4)
>> +#define v_DATA_MAIN_DRIVER(n)        (n << 0)
>> +
>> +#define HDMI_PHY_PRE_EMPHASIS        0xe3
>> +#define v_PRE_EMPHASIS(n)        ((n & 7) << 4)
>> +#define v_CLK_PRE_DRIVER(n)        ((n & 3) << 2)
>> +#define v_DATA_PRE_DRIVER(n)        ((n & 3) << 0)
>> +
>> +#define HDMI_PHY_FEEDBACK_DIV_RATIO_LOW        0xe7
>> +#define v_FEEDBACK_DIV_LOW(n)            (n & 0xff)
>> +#define HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH    0xe8
>> +#define v_FEEDBACK_DIV_HIGH(n)            (n & 1)
>> +
>> +#define HDMI_PHY_PRE_DIV_RATIO        0xed
>> +#define v_PRE_DIV_RATIO(n)        (n & 0x1f)
>> +
>> +#define HDMI_CEC_CTRL            0xd0
>> +#define m_ADJUST_FOR_HISENSE        (1 << 6)
>> +#define m_REJECT_RX_BROADCAST        (1 << 5)
>> +#define m_BUSFREETIME_ENABLE        (1 << 2)
>> +#define m_REJECT_RX            (1 << 1)
>> +#define m_START_TX            (1 << 0)
>> +
>> +#define HDMI_CEC_DATA            0xd1
>> +#define HDMI_CEC_TX_OFFSET        0xd2
>> +#define HDMI_CEC_RX_OFFSET        0xd3
>> +#define HDMI_CEC_CLK_H            0xd4
>> +#define HDMI_CEC_CLK_L            0xd5
>> +#define HDMI_CEC_TX_LENGTH        0xd6
>> +#define HDMI_CEC_RX_LENGTH        0xd7
>> +#define HDMI_CEC_TX_INT_MASK        0xd8
>> +#define m_TX_DONE            (1 << 3)
>> +#define m_TX_NOACK            (1 << 2)
>> +#define m_TX_BROADCAST_REJ        (1 << 1)
>> +#define m_TX_BUSNOTFREE            (1 << 0)
>> +
>> +#define HDMI_CEC_RX_INT_MASK        0xd9
>> +#define m_RX_LA_ERR            (1 << 4)
>> +#define m_RX_GLITCH            (1 << 3)
>> +#define m_RX_DONE            (1 << 0)
>> +
>> +#define HDMI_CEC_TX_INT            0xda
>> +#define HDMI_CEC_RX_INT            0xdb
>> +#define HDMI_CEC_BUSFREETIME_L        0xdc
>> +#define HDMI_CEC_BUSFREETIME_H        0xdd
>> +#define HDMI_CEC_LOGICADDR        0xde
>> +
>> +#endif /* __INNO_HDMI_H__ */
>
>


-- 
Thanks,
Caesar



From heiko at sntech.de  Sun Jan 24 14:52:20 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Sun, 24 Jan 2016 23:52:20 +0100
Subject: [PATCH v3 0/3] SoC: rockchip: support power domain for RK3368
In-Reply-To: <1452508600-3512-1-git-send-email-zhangqing@rock-chips.com>
References: <1452508600-3512-1-git-send-email-zhangqing@rock-chips.com>
Message-ID: <25506007.ZijpbXfFGZ@diego>

Hi Elaine,

Am Montag, 11. Januar 2016, 02:36:37 schrieb zhangqing:
> add pd id in rk3368-power.h
> modify pm_domains.c to support RK3368 SoC
> add RK3368 document for Rockchip power domains
> 
> zhangqing (3):
>   dt-bindings: add power-domain header for RK3368 SoCs
>   soc: rockchip: power-domain: Modify power domain driver for rk3368
>   dt-bindings: modify document of Rockchip power domains

applied all 3 to appropriate branches for 4.6


Thanks
Heiko


From jay.xu at rock-chips.com  Sun Jan 24 17:15:31 2016
From: jay.xu at rock-chips.com (Jianqun Xu)
Date: Mon, 25 Jan 2016 09:15:31 +0800
Subject: [PATCH v3 3/9] ASoC: rockchip: i2s: add support for grabbing
 output clock to codec
In-Reply-To: <20160122171815.GD6588@sirena.org.uk>
References: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
 <1452865796-23527-4-git-send-email-wxt@rock-chips.com>
 <20160115174623.GZ6588@sirena.org.uk>
 <CAPz6YkWt-iXS6P4QBBoq3v3i7ZAGGu6=_9AHw4qgevEwDwbdTA@mail.gmail.com>
 <20160122171815.GD6588@sirena.org.uk>
Message-ID: <56A57733.8020200@rock-chips.com>

Hi Mark

? 23/01/2016 01:18, Mark Brown ??:
> On Fri, Jan 15, 2016 at 01:48:04PM -0800, Sonny Rao wrote:
>> On Fri, Jan 15, 2016 at 9:46 AM, Mark Brown <broonie at kernel.org> wrote:
>
>>> If the I2S block is providing a clock to the CODEC then that's what the
>>> software should do so that the CODEC can gate and ungate the clock as
>>> required.  This patch has the I2S block using a clock, not providing
>>> one.
>
>>  From my read of the clock diagram for RK3288  there is a single clock
>> signal (labeled "clk_i2s0") that comes out of a fractional divider,
>> and it is split such that one path gets sent to the I2S block and the
>> second path is sent to a mux after which that signal is sent to an
>> external pin that goes to the codec.
>
>> There are separate clock gates for the two paths: one for the I2S
>> block and one after that mux before the external pin.
>
>> I'm not sure if it's being modeled that way in the Linux code or not,
>> but at least physically I don't think this clock signal actually goes
>> through the I2S block before being sent to the codec.
>
> That's not really the issue here, the issue is that it's not the I2S
> controller that is consuming the clock so it should not be the I2S
> controller driver that ensures that the clock is enabled.  The driver
> that manages the clock should be the one that uses it, like I say this
> means you should add the code to enable the clock to the CODEC driver if
> the CODEC driver needs the clock enabled.
>
Agree, now we almost use the simple-card for the CODEC driver, so I 
think we should enable the mclk(i2s-outclk) in the simple-card driver, 
is it ?

I found a subnode property from simple-card document:
- mclk-fs                               : Multiplication factor between 
stream
                                           rate and codec mclk, applied 
only for
                                           the dai-link.
But the property responsible to the factor, not care if the mclk source 
clock is enabled or not. So does the simple-card driver can add support 
to enable/disable mclk ?

>> Does that help clarify?
>
> The problem here isn't a lack of clarity in the situation.
>



From ykk at rock-chips.com  Sun Jan 24 17:22:31 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Mon, 25 Jan 2016 09:22:31 +0800
Subject: [PATCH v3 1/2] drm: rockchip/hdmi: add Innosilicon HDMI support
In-Reply-To: <56A47E6F.8050104@gmail.com>
References: <1452850598-30859-1-git-send-email-ykk@rock-chips.com>
 <1452850690-31760-1-git-send-email-ykk@rock-chips.com>
 <569C3E36.10103@rock-chips.com> <56A47E6F.8050104@gmail.com>
Message-ID: <56A578D7.5070101@rock-chips.com>

Hi Caesar,


On 01/24/2016 03:34 PM, Caesar Wang wrote:
> Hi
>
> ? 2016?01?18? 09:21, Mark yao ??:
>> Hi Yakir
>>
>> I'd like you can change your patch title into "drm/rockchip/hdmi", so 
>> when I search patches use "drm/rockchip" can find your patch.
>>
>> and I have some advices mail inline.
>>
>> Thanks:-)
>>
>> On 2016?01?15? 17:38, Yakir Yang wrote:
>>> The Innosilicon HDMI is a low power HDMI 1.4 transmitter
>>> IP, and it have been integrated on some rockchip CPUs
>>> (like RK3036, RK312x).
>>>
>>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
>>> ---
>>> Changes in v3:
>>> - Use encoder enable/disable function, and remove the encoder DPMS 
>>> function
>>> - Keep HDMI PLL power on in standby mode
>>>
>>> Changes in v2:
>>> - Using DRM atomic helper functions for connector init (Mark)
>>> - Remove "hdmi->connector.encoder = encoder;" (Mark)
>>>
>>>   drivers/gpu/drm/rockchip/Kconfig     |   8 +
>>>   drivers/gpu/drm/rockchip/Makefile    |   1 +
>>>   drivers/gpu/drm/rockchip/inno_hdmi.c | 999 
>>> +++++++++++++++++++++++++++++++++++
>>>   drivers/gpu/drm/rockchip/inno_hdmi.h | 364 +++++++++++++
>>>   4 files changed, 1372 insertions(+)
>>>   create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.c
>>>   create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.h
>>>
>>> diff --git a/drivers/gpu/drm/rockchip/Kconfig 
>>> b/drivers/gpu/drm/rockchip/Kconfig
>>> index 35215f6..a5014e0 100644
>>> --- a/drivers/gpu/drm/rockchip/Kconfig
>>> +++ b/drivers/gpu/drm/rockchip/Kconfig
>>> @@ -25,3 +25,11 @@ config ROCKCHIP_DW_HDMI
>>>         for the Synopsys DesignWare HDMI driver. If you want to
>>>         enable HDMI on RK3288 based SoC, you should selet this
>>>         option.
>>> +
>>> +config ROCKCHIP_INNO_HDMI
>>> +    tristate "Rockchip specific extensions for Innosilicon HDMI"
>>> +        depends on DRM_ROCKCHIP
>>> +        help
>>> +      This selects support for Rockchip SoC specific extensions
>>> +      for the Innosilicon HDMI driver. If you want to enable
>>> +      HDMI on RK3036 based SoC, you should selet this option.
>
> That's seem has some conflicts since the MIPI driver land in mainline.
> So you need update it based on the lastest kernel.
>

Yep, thanks for your reminder, need to update the patch. And by the way
the latest version of this patch have been updated to v5  :)
     https://patchwork.kernel.org/patch/8058061/

>>> diff --git a/drivers/gpu/drm/rockchip/Makefile 
>>> b/drivers/gpu/drm/rockchip/Makefile
>>> index a9d380f..da2bf76 100644
>>> --- a/drivers/gpu/drm/rockchip/Makefile
>>> +++ b/drivers/gpu/drm/rockchip/Makefile
>>> @@ -6,6 +6,7 @@ rockchipdrm-y := rockchip_drm_drv.o 
>>> rockchip_drm_fb.o rockchip_drm_fbdev.o \
>>>           rockchip_drm_gem.o
>>>     obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
>>> +obj-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
>>>     obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_drm_vop.o \
>>>                   rockchip_vop_reg.o
>>> diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c 
>>> b/drivers/gpu/drm/rockchip/inno_hdmi.c
>>> new file mode 100644
>>> index 0000000..dc98179
>>> --- /dev/null
>>> +++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
>>> @@ -0,0 +1,999 @@
>>> +/*
>>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>>> + *    Zheng Yang <zhengyang at rock-chips.com>
>>> + *    Yakir Yang <ykk at rock-chips.com>
>>> + *
>>> + * This software is licensed under the terms of the GNU General Public
>>> + * License version 2, as published by the Free Software Foundation, 
>>> and
>>> + * may be copied, distributed, and modified under those terms.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>> + * GNU General Public License for more details.
>>> + */
>>> +
>>> +#include <linux/irq.h>
>>> +#include <linux/clk.h>
>>> +#include <linux/delay.h>
>>> +#include <linux/err.h>
>>> +#include <linux/hdmi.h>
>>> +#include <linux/mfd/syscon.h>
>>> +#include <linux/module.h>
>>> +#include <linux/mutex.h>
>>> +#include <linux/of_device.h>
>>> +
>>> +#include <drm/drm_of.h>
>>> +#include <drm/drmP.h>
>>> +#include <drm/drm_atomic_helper.h>
>>> +#include <drm/drm_crtc_helper.h>
>>> +#include <drm/drm_edid.h>
>>> +#include <drm/drm_encoder_slave.h>
>>> +
>>> +#include "rockchip_drm_drv.h"
>>> +#include "rockchip_drm_vop.h"
>>> +
>>> +#include "inno_hdmi.h"
>>> +
>>> +#define to_inno_hdmi(x)    container_of(x, struct inno_hdmi, x)
>>> +
>>> +struct hdmi_data_info {
>>> +    int vic;
>>> +    bool sink_is_hdmi;
>>> +    bool sink_has_audio;
>>> +    unsigned int enc_in_format;
>>> +    unsigned int enc_out_format;
>>> +    unsigned int colorimetry;
>>> +};
>>> +
>>> +struct inno_hdmi_i2c {
>>> +    struct i2c_adapter adap;
>>> +
>>> +    u8 ddc_addr;
>>> +    u8 segment_addr;
>>> +
>>> +    struct mutex lock;
>>> +    struct completion cmp;
>>> +};
>>> +
>>> +struct inno_hdmi {
>>> +    struct device *dev;
>>> +    struct drm_device *drm_dev;
>>> +
>>> +    int irq;
>>> +    struct clk *pclk;
>>> +    void __iomem *regs;
>>> +
>>> +    struct drm_connector    connector;
>>> +    struct drm_encoder    encoder;
>>> +
>>> +    struct inno_hdmi_i2c *i2c;
>>> +    struct i2c_adapter *ddc;
>>> +
>>> +    unsigned int tmds_rate;
>>> +
>>> +    struct hdmi_data_info    hdmi_data;
>>> +    struct drm_display_mode previous_mode;
>>> +};
>>> +
>>> +enum {
>>> +    CSC_ITU601_16_235_TO_RGB_0_255_8BIT,
>>> +    CSC_ITU601_0_255_TO_RGB_0_255_8BIT,
>>> +    CSC_ITU709_16_235_TO_RGB_0_255_8BIT,
>>> +    CSC_RGB_0_255_TO_ITU601_16_235_8BIT,
>>> +    CSC_RGB_0_255_TO_ITU709_16_235_8BIT,
>>> +    CSC_RGB_0_255_TO_RGB_16_235_8BIT,
>>> +};
>>> +
>>> +static const char coeff_csc[][24] = {
>>> +    /*
>>> +     * YUV2RGB:601 SD mode(Y[16:235], UV[16:240], RGB[0:255]):
>>> +     *   R = 1.164*Y + 1.596*V - 204
>>> +     *   G = 1.164*Y - 0.391*U - 0.813*V + 154
>>> +     *   B = 1.164*Y + 2.018*U - 258
>>> +     */
>>> +    {
>>> +        0x04, 0xa7, 0x00, 0x00, 0x06, 0x62, 0x02, 0xcc,
>>> +        0x04, 0xa7, 0x11, 0x90, 0x13, 0x40, 0x00, 0x9a,
>>> +        0x04, 0xa7, 0x08, 0x12, 0x00, 0x00, 0x03, 0x02
>>> +    },
>>> +    /*
>>> +     * YUV2RGB:601 SD mode(YUV[0:255],RGB[0:255]):
>>> +     *   R = Y + 1.402*V - 248
>>> +     *   G = Y - 0.344*U - 0.714*V + 135
>>> +     *   B = Y + 1.772*U - 227
>>> +     */
>>> +    {
>>> +        0x04, 0x00, 0x00, 0x00, 0x05, 0x9b, 0x02, 0xf8,
>>> +        0x04, 0x00, 0x11, 0x60, 0x12, 0xdb, 0x00, 0x87,
>>> +        0x04, 0x00, 0x07, 0x16, 0x00, 0x00, 0x02, 0xe3
>>> +    },
>>> +    /*
>>> +     * YUV2RGB:709 HD mode(Y[16:235],UV[16:240],RGB[0:255]):
>>> +     *   R = 1.164*Y + 1.793*V - 248
>>> +     *   G = 1.164*Y - 0.213*U - 0.534*V + 77
>>> +     *   B = 1.164*Y + 2.115*U - 289
>>> +     */
>>> +    {
>>> +        0x04, 0xa7, 0x00, 0x00, 0x07, 0x2c, 0x02, 0xf8,
>>> +        0x04, 0xa7, 0x10, 0xda, 0x12, 0x22, 0x00, 0x4d,
>>> +        0x04, 0xa7, 0x08, 0x74, 0x00, 0x00, 0x03, 0x21
>>> +    },
>>> +
>>> +    /*
>>> +     * RGB2YUV:601 SD mode:
>>> +     *   Cb = -0.291G - 0.148R + 0.439B + 128
>>> +     *   Y  = 0.504G  + 0.257R + 0.098B + 16
>>> +     *   Cr = -0.368G + 0.439R - 0.071B + 128
>>> +     */
>>> +    {
>>> +        0x11, 0x5f, 0x01, 0x82, 0x10, 0x23, 0x00, 0x80,
>>> +        0x02, 0x1c, 0x00, 0xa1, 0x00, 0x36, 0x00, 0x1e,
>>> +        0x11, 0x29, 0x10, 0x59, 0x01, 0x82, 0x00, 0x80
>>> +    },
>>> +    /*
>>> +     * RGB2YUV:709 HD mode:
>>> +     *   Cb = - 0.338G - 0.101R + 0.439B + 128
>>> +     *   Y  = 0.614G   + 0.183R + 0.062B + 16
>>> +     *   Cr = - 0.399G + 0.439R - 0.040B + 128
>>> +     */
>>> +    {
>>> +        0x11, 0x98, 0x01, 0xc1, 0x10, 0x28, 0x00, 0x80,
>>> +        0x02, 0x74, 0x00, 0xbb, 0x00, 0x3f, 0x00, 0x10,
>>> +        0x11, 0x5a, 0x10, 0x67, 0x01, 0xc1, 0x00, 0x80
>>> +    },
>>> +    /*
>>> +     * RGB[0:255]2RGB[16:235]:
>>> +     *   R' = R x (235-16)/255 + 16;
>>> +     *   G' = G x (235-16)/255 + 16;
>>> +     *   B' = B x (235-16)/255 + 16;
>>> +     */
>>> +    {
>>> +        0x00, 0x00, 0x03, 0x6F, 0x00, 0x00, 0x00, 0x10,
>>> +        0x03, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
>>> +        0x00, 0x00, 0x00, 0x00, 0x03, 0x6F, 0x00, 0x10
>>> +    },
>>> +};
>>> +
>>> +static inline u8 hdmi_readb(struct inno_hdmi *hdmi, u16 offset)
>>> +{
>>> +    return readl_relaxed(hdmi->regs + (offset) * 0x04);
>>> +}
>>> +
>>> +static inline void hdmi_writeb(struct inno_hdmi *hdmi, u16 offset, 
>>> u32 val)
>>> +{
>>> +    writel_relaxed(val, hdmi->regs + (offset) * 0x04);
>>> +}
>>> +
>>> +static inline void hdmi_modb(struct inno_hdmi *hdmi, u16 offset,
>>> +                 u32 msk, u32 val)
>>> +{
>>> +    u8 temp = hdmi_readb(hdmi, offset) & ~msk;
>>> +
>>> +    temp |= val & msk;
>>> +    hdmi_writeb(hdmi, offset, temp);
>>> +}
>>> +
>>> +static void inno_hdmi_i2c_init(struct inno_hdmi *hdmi)
>>> +{
>>> +    int ddc_bus_freq;
>>> +
>>> +    ddc_bus_freq = (hdmi->tmds_rate >> 2) / HDMI_SCL_RATE;
>>> +
>>> +    hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
>>> +    hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
>>> +
>>> +    /* Clear the EDID interrupt flag and mute the interrupt */
>>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
>>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
>>> +}
>>> +
>>> +static void inno_hdmi_sys_power(struct inno_hdmi *hdmi, bool enable)
>>> +{
>>> +    if (enable)
>>> +        hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON);
>>> +    else
>>> +        hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
>>> +}
>>> +
>>> +static void inno_hdmi_set_pwr_mode(struct inno_hdmi *hdmi, int mode)
>>> +{
>>> +    switch (mode) {
>>> +    case NORMAL:
>>> +        inno_hdmi_sys_power(hdmi, false);
>>> +
>>> +        hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x6f);
>>> +        hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0xbb);
>>> +
>>> +        hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
>>> +        hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x14);
>>> +        hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x10);
>>> +        hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x0f);
>>> +        hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x00);
>>> +        hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x01);
>>> +
>>> +        inno_hdmi_sys_power(hdmi, true);
>>> +        break;
>>> +
>>> +    case LOWER_PWR:
>>> +        inno_hdmi_sys_power(hdmi, false);
>>> +        hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0x00);
>>> +        hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x00);
>>> +        hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x00);
>>> +        hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
>>> +
>>> +        break;
>>> +
>>> +    default:
>>> +        dev_err(hdmi->dev, "Unknown power mode %d\n", mode);
>>> +    }
>>> +}
>>> +
>>> +static void inno_hdmi_reset(struct inno_hdmi *hdmi)
>>> +{
>>> +    u32 val;
>>> +    u32 msk;
>>> +
>>> +    hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_DIGITAL, v_NOT_RST_DIGITAL);
>>> +    udelay(100);
>>> +
>>> +    hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_ANALOG, v_NOT_RST_ANALOG);
>>> +    udelay(100);
>>> +
>>> +    msk = m_REG_CLK_INV | m_REG_CLK_SOURCE | m_POWER | m_INT_POL;
>>> +    val = v_REG_CLK_INV | v_REG_CLK_SOURCE_SYS | v_PWR_ON | 
>>> v_INT_POL_HIGH;
>>> +    hdmi_modb(hdmi, HDMI_SYS_CTRL, msk, val);
>>> +
>>> +    inno_hdmi_set_pwr_mode(hdmi, NORMAL);
>>> +}
>>> +
>>> +static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi)
>>> +{
>>> +    char info[HDMI_SIZE_AVI_INFOFRAME] = {0};
>>> +    int avi_color_mode;
>>> +    int i;
>>> +
>>> +    hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_AVI);
>>> +
>>> +    info[0] = 0x82;
>>> +    info[1] = 0x02;
>>> +    info[2] = 0x0D;
>>> +    info[3] = info[0] + info[1] + info[2];
>>> +
>>> +    if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_RGB)
>>> +        avi_color_mode = AVI_COLOR_MODE_RGB;
>>> +    else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV444)
>>> +        avi_color_mode = AVI_COLOR_MODE_YCBCR444;
>>> +    else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV422)
>>> +        avi_color_mode = AVI_COLOR_MODE_YCBCR422;
>>> +    else
>>> +        avi_color_mode = AVI_COLOR_MODE_RGB;
>>> +
>>> +    info[4] = (avi_color_mode << 5);
>>> +    info[5] = (AVI_COLORIMETRY_NO_DATA << 6) |
>>> +          (AVI_CODED_FRAME_ASPECT_NO_DATA << 4) |
>>> +          ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME;
>>> +
>>> +    info[6] = 0;
>>> +    info[7] = hdmi->hdmi_data.vic;
>>> +
>>> +    if (hdmi->hdmi_data.vic == 6 || hdmi->hdmi_data.vic == 7 ||
>>> +        hdmi->hdmi_data.vic == 21 || hdmi->hdmi_data.vic == 22)
>>> +        info[8] = 1;
>>> +    else
>>> +        info[8] = 0;
>>> +
>>> +    /* Calculate avi info frame checKsum */
>>> +    for (i = 4; i < HDMI_SIZE_AVI_INFOFRAME; i++)
>>> +        info[3] += info[i];
>>> +    info[3] = 0x100 - info[3];
>>> +
>>> +    for (i = 0; i < HDMI_SIZE_AVI_INFOFRAME; i++)
>>> +        hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i, info[i]);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int inno_hdmi_config_video_vsi(struct inno_hdmi *hdmi)
>>> +{
>>> +    char info[HDMI_SIZE_VSI_INFOFRAME] = {0};
>>> +    int i;
>>> +
>>> +    hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, m_PACKET_VSI_EN,
>>> +          v_PACKET_VSI_EN(0));
>>> +
>>> +    hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_VSI);
>>> +
>>> +    /* Header Bytes */
>>> +    info[0] = 0x81;
>>> +    info[1] = 0x01;
>>> +
>>> +    /* PB1 - PB3 contain the 24bit IEEE Registration Identifier */
>>> +    info[4] = 0x03;
>>> +    info[5] = 0x0c;
>>> +    info[6] = 0x00;
>>> +
>>> +    /* PB4 - HDMI_Video_Format into bits 7:5 */
>>> +    info[7] = 0;
>>> +
>>> +    /*
>>> +     * PB5 - Depending on the video format, this byte will contain
>>> +     * either the HDMI_VIC code in buts 7:0, OR the 3D_Structure in
>>> +     * bits 7:4.
>>> +     */
>>> +    info[2] = 0x06 - 2;
>>> +    info[8] = 0;
>>> +    info[9] = 0;
>>> +
>>> +    info[3] = info[0] + info[1] + info[2];
>>> +
>>> +    /* Calculate info frame checKsum */
>>> +    for (i = 4; i < HDMI_SIZE_VSI_INFOFRAME; i++)
>>> +        info[3] += info[i];
>>> +    info[3] = 0x100 - info[3];
>>> +
>>> +    for (i = 0; i < HDMI_SIZE_VSI_INFOFRAME; i++)
>>> +        hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i, info[i]);
>>> +
>>> +    hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, m_PACKET_VSI_EN,
>>> +          v_PACKET_VSI_EN(1));
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
>>> +{
>>> +    struct hdmi_data_info *data = &hdmi->hdmi_data;
>>> +    int c0_c2_change = 0;
>>> +    int csc_enable = 0;
>>> +    int csc_mode = 0;
>>> +    int auto_csc = 0;
>>> +    int value;
>>> +    int i;
>>> +
>>> +    /* Input video mode is SDR RGB24bit, data enable signal from 
>>> external */
>>> +    hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL1, v_DE_EXTERNAL |
>>> +            v_VIDEO_INPUT_FORMAT(VIDEO_INPUT_SDR_RGB444));
>>> +
>>> +    /* Input color hardcode to RGB, and output color hardcode to 
>>> RGB888 */
>>> +    value = v_VIDEO_INPUT_BITS(VIDEO_INPUT_8BITS) |
>>> +        v_VIDEO_OUTPUT_COLOR(0) |
>>> +        v_VIDEO_INPUT_CSP(0);
>>> +    hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL2, value);
>>> +
>>> +    if (data->enc_out_format == data->enc_out_format) {
>>> +        if ((data->enc_in_format == HDMI_COLORSPACE_RGB) ||
>>> +            (data->enc_in_format >= HDMI_COLORSPACE_YUV444)) {
>>> +            value = v_SOF_DISABLE | v_COLOR_DEPTH_NOT_INDICATED(1);
>>> +            hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
>>> +
>>> +            hdmi_modb(hdmi, HDMI_VIDEO_CONTRL,
>>> +                  m_VIDEO_AUTO_CSC | m_VIDEO_C0_C2_SWAP,
>>> +                  v_VIDEO_AUTO_CSC(AUTO_CSC_DISABLE) |
>>> +                  v_VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE));
>>> +            return 0;
>>> +        }
>>> +    }
>>> +
>>> +    if (data->colorimetry == HDMI_COLORIMETRY_ITU_601) {
>>> +        if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
>>> +            (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
>>> +            csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT;
>>> +            auto_csc = AUTO_CSC_DISABLE;
>>> +            c0_c2_change = C0_C2_CHANGE_DISABLE;
>>> +            csc_enable = v_CSC_ENABLE;
>>> +        } else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
>>> +               (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
>>> +            csc_mode = CSC_ITU601_16_235_TO_RGB_0_255_8BIT;
>>> +            auto_csc = AUTO_CSC_ENABLE;
>>> +            c0_c2_change = C0_C2_CHANGE_DISABLE;
>>> +            csc_enable = v_CSC_DISABLE;
>>> +        }
>>> +    } else {
>>> +        if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
>>> +            (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
>>> +            csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT;
>>> +            auto_csc = AUTO_CSC_DISABLE;
>>> +            c0_c2_change = C0_C2_CHANGE_DISABLE;
>>> +            csc_enable = v_CSC_ENABLE;
>>> +        } else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
>>> +               (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
>>> +            csc_mode = CSC_ITU709_16_235_TO_RGB_0_255_8BIT;
>>> +            auto_csc = AUTO_CSC_ENABLE;
>>> +            c0_c2_change = C0_C2_CHANGE_DISABLE;
>>> +            csc_enable = v_CSC_DISABLE;
>>> +        }
>>> +    }
>>> +
>>> +    for (i = 0; i < 24; i++)
>>> +        hdmi_writeb(hdmi, HDMI_VIDEO_CSC_COEF + i,
>>> +                coeff_csc[csc_mode][i]);
>>> +
>>> +    value = v_SOF_DISABLE | csc_enable | 
>>> v_COLOR_DEPTH_NOT_INDICATED(1);
>>> +    hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
>>> +    hdmi_modb(hdmi, HDMI_VIDEO_CONTRL, m_VIDEO_AUTO_CSC |
>>> +          m_VIDEO_C0_C2_SWAP, v_VIDEO_AUTO_CSC(auto_csc) |
>>> +          v_VIDEO_C0_C2_SWAP(c0_c2_change));
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi,
>>> +                     struct drm_display_mode *mode)
>>> +{
>>> +    int value;
>>> +
>>> +    /* Set detail external video timing polarity and interlace mode */
>>> +    value = v_EXTERANL_VIDEO(1);
>>> +    value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
>>> +         v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
>>> +    value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
>>> +         v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
>>> +    value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
>>> +         v_INETLACE(1) : v_INETLACE(0);
>>> +    hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
>>> +
>>> +    /* Set detail external video timing */
>>> +    value = mode->htotal;
>>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
>>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
>>> +
>>> +    value = mode->htotal - mode->hdisplay;
>>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
>>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
>>> +
>>> +    value = mode->hsync_start - mode->hdisplay;
>>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
>>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
>>> +
>>> +    value = mode->hsync_end - mode->hsync_start;
>>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
>>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 
>>> 0xFF);
>>> +
>>> +    value = mode->vtotal;
>>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
>>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
>>> +
>>> +    value = mode->vtotal - mode->vdisplay;
>>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
>>> +
>>> +    value = mode->vsync_start - mode->vdisplay;
>>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
>>> +
>>> +    value = mode->vsync_end - mode->vsync_start;
>>> +    hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
>>> +
>>> +    hdmi_writeb(hdmi, HDMI_PHY_PRE_DIV_RATIO, 0x1e);
>>> +    hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_LOW, 0x2c);
>>> +    hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH, 0x01);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int inno_hdmi_setup(struct inno_hdmi *hdmi,
>>> +               struct drm_display_mode *mode)
>>> +{
>>> +    int value;
>
> Where be used?
> I guess you should remove it.
>
>
>
> -
> Caesar
>>> +
>>> +    hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
>>> +
>>> +    hdmi->hdmi_data.enc_in_format = HDMI_COLORSPACE_RGB;
>>> +    hdmi->hdmi_data.enc_out_format = HDMI_COLORSPACE_RGB;
>>> +
>>> +    if ((hdmi->hdmi_data.vic == 6) || (hdmi->hdmi_data.vic == 7) ||
>>> +        (hdmi->hdmi_data.vic == 21) || (hdmi->hdmi_data.vic == 22) ||
>>> +        (hdmi->hdmi_data.vic == 2) || (hdmi->hdmi_data.vic == 3) ||
>>> +        (hdmi->hdmi_data.vic == 17) || (hdmi->hdmi_data.vic == 18))
>>> +        hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601;
>>> +    else
>>> +        hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
>>> +
>>> +    /* Mute video and audio output */
>>> +    hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
>>> +          v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1));
>>> +
>>> +    /* Set HDMI Mode */
>>> +    hdmi_writeb(hdmi, HDMI_HDCP_CTRL,
>>> +            v_HDMI_DVI(hdmi->hdmi_data.sink_is_hdmi));
>>> +
>>> +    inno_hdmi_config_video_timing(hdmi, mode);
>>> +
>>> +    inno_hdmi_config_video_csc(hdmi);
>>> +
>>> +    if (hdmi->hdmi_data.sink_is_hdmi) {
>>> +        inno_hdmi_config_video_avi(hdmi);
>>> +        inno_hdmi_config_video_vsi(hdmi);
>>> +    }
>>> +
>>> +    /*
>>> +     * When IP controller have configured to an accurate video
>>> +     * timing, then the TMDS clock source would be switched to
>>> +     * DCLK_LCDC, so we need to init the TMDS rate to mode pixel
>>> +     * clock rate, and reconfigure the DDC clock.
>>> +     */
>>> +    hdmi->tmds_rate = mode->clock * 1000;
>>> +    inno_hdmi_i2c_init(hdmi);
>>> +
>>> +    /* Unmute video and audio output */
>>> +    hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
>>> +          v_AUDIO_MUTE(0) | v_VIDEO_MUTE(0));
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder,
>>> +                       struct drm_display_mode *mode,
>>> +                       struct drm_display_mode *adj_mode)
>>> +{
>>> +    struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
>>> +
>>> +    inno_hdmi_setup(hdmi, adj_mode);
>>> +
>>> +    /* Store the display mode for plugin/DKMS poweron events */
>>
>> I think DKMS should be DPMS.
>>
>>> +    memcpy(&hdmi->previous_mode, adj_mode, 
>>> sizeof(hdmi->previous_mode));
>>> +}
>>> +
>>> +static void inno_hdmi_encoder_enable(struct drm_encoder *encoder)
>>> +{
>>> +    struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
>>> +
>>> +    inno_hdmi_set_pwr_mode(hdmi, NORMAL);
>>> +}
>>> +
>>> +static void inno_hdmi_encoder_disable(struct drm_encoder *encoder)
>>> +{
>>> +    struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
>>> +
>>> +    inno_hdmi_set_pwr_mode(hdmi, LOWER_PWR);
>>> +}
>>> +
>>> +static void inno_hdmi_encoder_commit(struct drm_encoder *encoder)
>>> +{
>>> +}
>>> +
>>> +static void inno_hdmi_encoder_prepare(struct drm_encoder *encoder)
>>> +{
>>> +    rockchip_drm_crtc_mode_config(encoder->crtc, 
>>> DRM_MODE_CONNECTOR_HDMIA,
>>> +                      ROCKCHIP_OUT_MODE_P888);
>>
>> Can you move mode_config into inno_hdmi_encoder_enable, and remove 
>> .prepare and .commit?
>>
>>> +}
>>> +
>>> +static bool inno_hdmi_encoder_mode_fixup(struct drm_encoder *encoder,
>>> +                     const struct drm_display_mode *mode,
>>> +                     struct drm_display_mode *adj_mode)
>>> +{
>>> +    return true;
>>> +}
>>> +
>>> +static struct drm_encoder_helper_funcs 
>>> inno_hdmi_encoder_helper_funcs = {
>>> +    .enable     = inno_hdmi_encoder_enable,
>>> +    .disable    = inno_hdmi_encoder_disable,
>>> +    .mode_fixup = inno_hdmi_encoder_mode_fixup,
>>> +    .mode_set   = inno_hdmi_encoder_mode_set,
>>> +    .prepare    = inno_hdmi_encoder_prepare,
>>> +    .commit     = inno_hdmi_encoder_commit,
>>
>> On drm atomic, I think if support .enable and .disable callbacks, 
>> then .prepare and .commit is not needed.
>>
>>> +};
>>> +
>>> +static struct drm_encoder_funcs inno_hdmi_encoder_funcs = {
>>> +    .destroy = drm_encoder_cleanup,
>>> +};
>>> +
>>> +static enum drm_connector_status
>>> +inno_hdmi_connector_detect(struct drm_connector *connector, bool 
>>> force)
>>> +{
>>> +    struct inno_hdmi *hdmi = to_inno_hdmi(connector);
>>> +
>>> +    return (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
>>> +        connector_status_connected : connector_status_disconnected;
>>> +}
>>> +
>>> +static int inno_hdmi_connector_get_modes(struct drm_connector 
>>> *connector)
>>> +{
>>> +    struct inno_hdmi *hdmi = to_inno_hdmi(connector);
>>> +    struct edid *edid;
>>> +    int ret = 0;
>>> +
>>> +    if (!hdmi->ddc)
>>> +        return 0;
>>> +
>>> +    edid = drm_get_edid(connector, hdmi->ddc);
>>> +    if (edid) {
>>> +        hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
>>> +        hdmi->hdmi_data.sink_has_audio = 
>>> drm_detect_monitor_audio(edid);
>>> +        drm_mode_connector_update_edid_property(connector, edid);
>>> +        ret = drm_add_edid_modes(connector, edid);
>>> +        kfree(edid);
>>> +    }
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static enum drm_mode_status
>>> +inno_hdmi_connector_mode_valid(struct drm_connector *connector,
>>> +                   struct drm_display_mode *mode)
>>> +{
>>> +    return MODE_OK;
>>> +}
>>> +
>>> +static struct drm_encoder *
>>> +inno_hdmi_connector_best_encoder(struct drm_connector *connector)
>>> +{
>>> +    struct inno_hdmi *hdmi = to_inno_hdmi(connector);
>>> +
>>> +    return &hdmi->encoder;
>>> +}
>>> +
>>> +static int
>>> +inno_hdmi_probe_single_connector_modes(struct drm_connector 
>>> *connector,
>>> +                       uint32_t maxX, uint32_t maxY)
>>> +{
>>> +    return drm_helper_probe_single_connector_modes(connector, 1920, 
>>> 1080);
>>> +}
>>> +
>>> +static void inno_hdmi_connector_destroy(struct drm_connector 
>>> *connector)
>>> +{
>>> +    drm_connector_unregister(connector);
>>> +    drm_connector_cleanup(connector);
>>> +}
>>> +
>>> +static struct drm_connector_funcs inno_hdmi_connector_funcs = {
>>> +    .dpms = drm_atomic_helper_connector_dpms,
>>> +    .fill_modes = inno_hdmi_probe_single_connector_modes,
>>> +    .detect = inno_hdmi_connector_detect,
>>> +    .destroy = inno_hdmi_connector_destroy,
>>> +    .reset = drm_atomic_helper_connector_reset,
>>> +    .atomic_duplicate_state = 
>>> drm_atomic_helper_connector_duplicate_state,
>>> +    .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
>>> +};
>>> +
>>> +static struct drm_connector_helper_funcs 
>>> inno_hdmi_connector_helper_funcs = {
>>> +    .get_modes = inno_hdmi_connector_get_modes,
>>> +    .mode_valid = inno_hdmi_connector_mode_valid,
>>> +    .best_encoder = inno_hdmi_connector_best_encoder,
>>> +};
>>> +
>>> +static int inno_hdmi_register(struct drm_device *drm, struct 
>>> inno_hdmi *hdmi)
>>> +{
>>> +    struct drm_encoder *encoder = &hdmi->encoder;
>>> +
>>> +    encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, 
>>> hdmi->dev->of_node);
>>
>> Keep 80 characters :-) .
>>
>>> +    /*
>>> +     * If we failed to find the CRTC(s) which this encoder is
>>> +     * supposed to be connected to, it's because the CRTC has
>>> +     * not been registered yet.  Defer probing, and hope that
>>> +     * the required CRTC is added later.
>>> +     */
>>> +    if (encoder->possible_crtcs == 0)
>>> +        return -EPROBE_DEFER;
>>> +
>>> +    drm_encoder_helper_add(encoder, &inno_hdmi_encoder_helper_funcs);
>>> +    drm_encoder_init(drm, encoder, &inno_hdmi_encoder_funcs,
>>> +             DRM_MODE_ENCODER_TMDS, NULL);
>>> +
>>> +    hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
>>> +
>>> +    drm_connector_helper_add(&hdmi->connector,
>>> +                 &inno_hdmi_connector_helper_funcs);
>>> +    drm_connector_init(drm, &hdmi->connector, 
>>> &inno_hdmi_connector_funcs,
>>> +               DRM_MODE_CONNECTOR_HDMIA);
>>> +
>>> + drm_mode_connector_attach_encoder(&hdmi->connector, encoder);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static irqreturn_t inno_hdmi_i2c_irq(struct inno_hdmi *hdmi)
>>> +{
>>> +    struct inno_hdmi_i2c *i2c = hdmi->i2c;
>>> +    u8 stat;
>>> +
>>> +    stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
>>> +    if (!(stat & m_INT_EDID_READY))
>>> +        return IRQ_NONE;
>>> +
>>> +    /* Clear HDMI EDID interrupt flag */
>>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
>>> +
>>> +    complete(&i2c->cmp);
>>> +
>>> +    return IRQ_HANDLED;
>>> +}
>>> +
>>> +static irqreturn_t inno_hdmi_hardirq(int irq, void *dev_id)
>>> +{
>>> +    struct inno_hdmi *hdmi = dev_id;
>>> +    irqreturn_t ret = IRQ_NONE;
>>> +    u8 interrupt;
>>> +
>>> +    if (hdmi->i2c)
>>> +        ret = inno_hdmi_i2c_irq(hdmi);
>>> +
>>> +    interrupt = hdmi_readb(hdmi, HDMI_STATUS);
>>> +    if (interrupt & m_INT_HOTPLUG) {
>>> +        hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG);
>>> +        ret = IRQ_WAKE_THREAD;
>>> +    }
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static irqreturn_t inno_hdmi_irq(int irq, void *dev_id)
>>> +{
>>> +    struct inno_hdmi *hdmi = dev_id;
>>> +
>>> +    drm_helper_hpd_irq_event(hdmi->connector.dev);
>>> +
>>> +    return IRQ_HANDLED;
>>> +}
>>> +
>>> +static int inno_hdmi_i2c_wait(struct inno_hdmi *hdmi)
>>> +{
>>> +    struct inno_hdmi_i2c *i2c = hdmi->i2c;
>>> +    int stat;
>>> +
>>> +    stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
>>> +    if (!stat) {
>>> +        stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
>>> +        if (!stat)
>>> +            return -EAGAIN;
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int inno_hdmi_i2c_read(struct inno_hdmi *hdmi, struct 
>>> i2c_msg *msgs)
>>> +{
>>> +    int length = msgs->len;
>>> +    u8 *buf = msgs->buf;
>>> +    int ret;
>>> +
>>> +    ret = inno_hdmi_i2c_wait(hdmi);
>>> +    if (ret)
>>> +        return ret;
>>> +
>>> +    while (length--)
>>> +        *buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int inno_hdmi_i2c_write(struct inno_hdmi *hdmi, struct 
>>> i2c_msg *msgs)
>>> +{
>>> +    struct inno_hdmi_i2c *i2c = hdmi->i2c;
>>> +
>>> +    /*
>>> +     * The DDC module only support read EDID message, so
>>> +     * we assume that each word write to this i2c adapter
>>> +     * should be the offset of EDID word address.
>>> +     */
>>> +    if ((msgs->len != 1) ||
>>> +        ((msgs->addr != DDC_ADDR) && (msgs->addr != 
>>> DDC_SEGMENT_ADDR)))
>>> +        return -EINVAL;
>>> +
>>> +    reinit_completion(&i2c->cmp);
>>> +
>>> +    if (msgs->addr == DDC_SEGMENT_ADDR)
>>> +        hdmi->i2c->segment_addr = msgs->buf[0];
>>> +    if (msgs->addr == DDC_ADDR)
>>> +        hdmi->i2c->ddc_addr = msgs->buf[0];
>>> +
>>> +    /* Set edid fifo first addr */
>>> +    hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
>>> +
>>> +    /* Set edid word address 0x00/0x80 */
>>> +    hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
>>> +
>>> +    /* Set edid segment pointer */
>>> +    hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, 
>>> hdmi->i2c->segment_addr);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int inno_hdmi_i2c_xfer(struct i2c_adapter *adap,
>>> +                  struct i2c_msg *msgs, int num)
>>> +{
>>> +    struct inno_hdmi *hdmi = i2c_get_adapdata(adap);
>>> +    struct inno_hdmi_i2c *i2c = hdmi->i2c;
>>> +    int i, ret = 0;
>>> +
>>> +    mutex_lock(&i2c->lock);
>>> +
>>> +    /* Clear the EDID interrupt flag and unmute the interrupt */
>>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY);
>>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
>>> +
>>> +    for (i = 0; i < num; i++) {
>>> +        dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n",
>>> +            i + 1, num, msgs[i].len, msgs[i].flags);
>>> +
>>> +        if (msgs[i].flags & I2C_M_RD)
>>> +            ret = inno_hdmi_i2c_read(hdmi, &msgs[i]);
>>> +        else
>>> +            ret = inno_hdmi_i2c_write(hdmi, &msgs[i]);
>>> +
>>> +        if (ret < 0)
>>> +            break;
>>> +    }
>>> +
>>> +    if (!ret)
>>> +        ret = num;
>>> +
>>> +    /* Mute HDMI EDID interrupt */
>>> +    hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
>>> +
>>> +    mutex_unlock(&i2c->lock);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static u32 inno_hdmi_i2c_func(struct i2c_adapter *adapter)
>>> +{
>>> +    return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
>>> +}
>>> +
>>> +static const struct i2c_algorithm inno_hdmi_algorithm = {
>>> +    .master_xfer    = inno_hdmi_i2c_xfer,
>>> +    .functionality    = inno_hdmi_i2c_func,
>>> +};
>>> +
>>> +static struct i2c_adapter *inno_hdmi_i2c_adapter(struct inno_hdmi 
>>> *hdmi)
>>> +{
>>> +    struct i2c_adapter *adap;
>>> +    struct inno_hdmi_i2c *i2c;
>>> +    int ret;
>>> +
>>> +    i2c = devm_kzalloc(hdmi->dev, sizeof(*i2c), GFP_KERNEL);
>>> +    if (!i2c)
>>> +        return ERR_PTR(-ENOMEM);
>>> +
>>> +    mutex_init(&i2c->lock);
>>> +    init_completion(&i2c->cmp);
>>> +
>>> +    adap = &i2c->adap;
>>> +    adap->class = I2C_CLASS_DDC;
>>> +    adap->owner = THIS_MODULE;
>>> +    adap->dev.parent = hdmi->dev;
>>> +    adap->dev.of_node = hdmi->dev->of_node;
>>> +    adap->algo = &inno_hdmi_algorithm;
>>> +    strlcpy(adap->name, "Inno HDMI", sizeof(adap->name));
>>> +    i2c_set_adapdata(adap, hdmi);
>>> +
>>> +    ret = i2c_add_adapter(adap);
>>> +    if (ret) {
>>> +        dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", 
>>> adap->name);
>>> +        devm_kfree(hdmi->dev, i2c);
>>> +        return ERR_PTR(ret);
>>> +    }
>>> +
>>> +    hdmi->i2c = i2c;
>>> +
>>> +    dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name);
>>> +
>>> +    return adap;
>>> +}
>>> +
>>> +static int inno_hdmi_bind(struct device *dev, struct device *master,
>>> +                 void *data)
>>> +{
>>> +    struct platform_device *pdev = to_platform_device(dev);
>>> +    struct drm_device *drm = data;
>>> +    struct inno_hdmi *hdmi;
>>> +    struct resource *iores;
>>> +    int irq;
>>> +    int ret;
>>> +
>>> +    hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
>>> +    if (!hdmi)
>>> +        return -ENOMEM;
>>> +
>>> +    hdmi->dev = dev;
>>> +    hdmi->drm_dev = drm;
>>> +
>>> +    iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> +    if (!iores)
>>> +        return -ENXIO;
>>> +
>>> +    hdmi->regs = devm_ioremap_resource(dev, iores);
>>> +    if (IS_ERR(hdmi->regs))
>>> +        return PTR_ERR(hdmi->regs);
>>> +
>>> +    hdmi->pclk = devm_clk_get(hdmi->dev, "pclk");
>>> +    if (IS_ERR(hdmi->pclk)) {
>>> +        dev_err(hdmi->dev, "Unable to get HDMI pclk clk\n");
>>> +        return PTR_ERR(hdmi->pclk);
>>> +    }
>>> +
>>> +    ret = clk_prepare_enable(hdmi->pclk);
>>> +    if (ret) {
>>> +        dev_err(hdmi->dev, "Cannot enable HDMI pclk clock: %d\n", 
>>> ret);
>>> +        return ret;
>>> +    }
>>> +
>>> +    irq = platform_get_irq(pdev, 0);
>>> +    if (irq < 0)
>>> +        return irq;
>>> +
>>> +    inno_hdmi_reset(hdmi);
>>> +
>>> +    hdmi->ddc = inno_hdmi_i2c_adapter(hdmi);
>>> +    if (IS_ERR(hdmi->ddc)) {
>>> +        hdmi->ddc = NULL;
>>> +        return PTR_ERR(hdmi->ddc);
>>> +    }
>>> +
>>> +    /*
>>> +     * When IP controller haven't configured to an accurate video
>>> +     * timing, then the TMDS clock source would be switched to
>>> +     * PCLK_HDMI, so we need to init the TMDS rate to PCLK rate,
>>> +     * and reconfigure the DDC clock.
>>> +     */
>>> +    hdmi->tmds_rate = clk_get_rate(hdmi->pclk);
>>> +    inno_hdmi_i2c_init(hdmi);
>>> +
>>> +    ret = inno_hdmi_register(drm, hdmi);
>>> +    if (ret)
>>> +        return ret;
>>> +
>>> +    dev_set_drvdata(dev, hdmi);
>>> +
>>> +    /* Unmute hotplug interrupt */
>>> +    hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, 
>>> v_MASK_INT_HOTPLUG(1));
>>> +
>>> +    ret = devm_request_threaded_irq(dev, irq, inno_hdmi_hardirq,
>>> +                    inno_hdmi_irq, IRQF_SHARED,
>>> +                    dev_name(dev), hdmi);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static void inno_hdmi_unbind(struct device *dev, struct device 
>>> *master,
>>> +                 void *data)
>>> +{
>>> +    struct inno_hdmi *hdmi = dev_get_drvdata(dev);
>>> +
>>> + hdmi->connector.funcs->destroy(&hdmi->connector);
>>> + hdmi->encoder.funcs->destroy(&hdmi->encoder);
>>> +
>>> +    clk_disable_unprepare(hdmi->pclk);
>>> +    i2c_put_adapter(hdmi->ddc);
>>> +}
>>> +
>>> +static const struct component_ops inno_hdmi_ops = {
>>> +    .bind    = inno_hdmi_bind,
>>> +    .unbind    = inno_hdmi_unbind,
>>> +};
>>> +
>>> +static int inno_hdmi_probe(struct platform_device *pdev)
>>> +{
>>> +    return component_add(&pdev->dev, &inno_hdmi_ops);
>>> +}
>>> +
>>> +static int inno_hdmi_remove(struct platform_device *pdev)
>>> +{
>>> +    component_del(&pdev->dev, &inno_hdmi_ops);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static const struct of_device_id inno_hdmi_dt_ids[] = {
>>> +    { .compatible = "rockchip,rk3036-inno-hdmi",
>>> +    },
>>> +    {},
>>> +};
>>> +MODULE_DEVICE_TABLE(of, inno_hdmi_dt_ids);
>>> +
>>> +static struct platform_driver inno_hdmi_driver = {
>>> +    .probe  = inno_hdmi_probe,
>>> +    .remove = inno_hdmi_remove,
>>> +    .driver = {
>>> +        .name = "innohdmi-rockchip",
>>> +        .of_match_table = inno_hdmi_dt_ids,
>>> +    },
>>> +};
>>> +
>>> +module_platform_driver(inno_hdmi_driver);
>>> +
>>> +MODULE_AUTHOR("Zheng Yang <zhengyang at rock-chips.com>");
>>> +MODULE_AUTHOR("Yakir Yang <ykk at rock-chips.com>");
>>> +MODULE_DESCRIPTION("Rockchip Specific INNO-HDMI Driver");
>>> +MODULE_LICENSE("GPL");
>>> +MODULE_ALIAS("platform:innohdmi-rockchip");
>>> diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.h 
>>> b/drivers/gpu/drm/rockchip/inno_hdmi.h
>>> new file mode 100644
>>> index 0000000..4ff17ad
>>> --- /dev/null
>>> +++ b/drivers/gpu/drm/rockchip/inno_hdmi.h
>>> @@ -0,0 +1,364 @@
>>> +/*
>>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>>> + *    Zheng Yang <zhengyang at rock-chips.com>
>>> + *    Yakir Yang <ykk at rock-chips.com>
>>> + *
>>> + * This software is licensed under the terms of the GNU General Public
>>> + * License version 2, as published by the Free Software Foundation, 
>>> and
>>> + * may be copied, distributed, and modified under those terms.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>> + * GNU General Public License for more details.
>>> + */
>>> +
>>> +#ifndef __INNO_HDMI_H__
>>> +#define __INNO_HDMI_H__
>>> +
>>> +#define DDC_SEGMENT_ADDR        0x30
>>> +
>>> +enum PWR_MODE {
>>> +    NORMAL,
>>> +    LOWER_PWR,
>>> +};
>>> +
>>> +#define HDMI_SCL_RATE            (100*1000)
>>> +#define DDC_BUS_FREQ_L            0x4b
>>> +#define DDC_BUS_FREQ_H            0x4c
>>> +
>>> +#define HDMI_SYS_CTRL            0x00
>>> +#define m_RST_ANALOG            (1 << 6)
>>> +#define v_RST_ANALOG            (0 << 6)
>>> +#define v_NOT_RST_ANALOG        (1 << 6)
>>> +#define m_RST_DIGITAL            (1 << 5)
>>> +#define v_RST_DIGITAL            (0 << 5)
>>> +#define v_NOT_RST_DIGITAL        (1 << 5)
>>> +#define m_REG_CLK_INV            (1 << 4)
>>> +#define v_REG_CLK_NOT_INV        (0 << 4)
>>> +#define v_REG_CLK_INV            (1 << 4)
>>> +#define m_VCLK_INV            (1 << 3)
>>> +#define v_VCLK_NOT_INV            (0 << 3)
>>> +#define v_VCLK_INV            (1 << 3)
>>> +#define m_REG_CLK_SOURCE        (1 << 2)
>>> +#define v_REG_CLK_SOURCE_TMDS        (0 << 2)
>>> +#define v_REG_CLK_SOURCE_SYS        (1 << 2)
>>> +#define m_POWER                (1 << 1)
>>> +#define v_PWR_ON            (0 << 1)
>>> +#define v_PWR_OFF            (1 << 1)
>>> +#define m_INT_POL            (1 << 0)
>>> +#define v_INT_POL_HIGH            1
>>> +#define v_INT_POL_LOW            0
>>> +
>>> +#define HDMI_VIDEO_CONTRL1        0x01
>>> +#define m_VIDEO_INPUT_FORMAT        (7 << 1)
>>> +#define m_DE_SOURCE            (1 << 0)
>>> +#define v_VIDEO_INPUT_FORMAT(n)        (n << 1)
>>> +#define v_DE_EXTERNAL            1
>>> +#define v_DE_INTERNAL            0
>>> +enum {
>>> +    VIDEO_INPUT_SDR_RGB444 = 0,
>>> +    VIDEO_INPUT_DDR_RGB444 = 5,
>>> +    VIDEO_INPUT_DDR_YCBCR422 = 6
>>> +};
>>> +
>>> +#define HDMI_VIDEO_CONTRL2        0x02
>>> +#define m_VIDEO_OUTPUT_COLOR        (3 << 6)
>>> +#define m_VIDEO_INPUT_BITS        (3 << 4)
>>> +#define m_VIDEO_INPUT_CSP        (1 << 0)
>>> +#define v_VIDEO_OUTPUT_COLOR(n)        (((n) & 0x3) << 6)
>>> +#define v_VIDEO_INPUT_BITS(n)        (n << 4)
>>> +#define v_VIDEO_INPUT_CSP(n)        (n << 0)
>>> +enum {
>>> +    VIDEO_INPUT_12BITS = 0,
>>> +    VIDEO_INPUT_10BITS = 1,
>>> +    VIDEO_INPUT_REVERT = 2,
>>> +    VIDEO_INPUT_8BITS = 3,
>>> +};
>>> +
>>> +#define HDMI_VIDEO_CONTRL        0x03
>>> +#define m_VIDEO_AUTO_CSC        (1 << 7)
>>> +#define v_VIDEO_AUTO_CSC(n)        (n << 7)
>>> +#define m_VIDEO_C0_C2_SWAP        (1 << 0)
>>> +#define v_VIDEO_C0_C2_SWAP(n)        (n << 0)
>>> +enum {
>>> +    C0_C2_CHANGE_ENABLE = 0,
>>> +    C0_C2_CHANGE_DISABLE = 1,
>>> +    AUTO_CSC_DISABLE = 0,
>>> +    AUTO_CSC_ENABLE = 1,
>>> +};
>>> +
>>> +#define HDMI_VIDEO_CONTRL3        0x04
>>> +#define m_COLOR_DEPTH_NOT_INDICATED    (1 << 4)
>>> +#define m_SOF                (1 << 3)
>>> +#define m_COLOR_RANGE            (1 << 2)
>>> +#define m_CSC                (1 << 0)
>>> +#define v_COLOR_DEPTH_NOT_INDICATED(n)    ((n) << 4)
>>> +#define v_SOF_ENABLE            (0 << 3)
>>> +#define v_SOF_DISABLE            (1 << 3)
>>> +#define v_COLOR_RANGE_FULL        (1 << 2)
>>> +#define v_COLOR_RANGE_LIMITED        (0 << 2)
>>> +#define v_CSC_ENABLE            1
>>> +#define v_CSC_DISABLE            0
>>> +
>>> +#define HDMI_AV_MUTE            0x05
>>> +#define m_AVMUTE_CLEAR            (1 << 7)
>>> +#define m_AVMUTE_ENABLE            (1 << 6)
>>> +#define m_AUDIO_MUTE            (1 << 1)
>>> +#define m_VIDEO_BLACK            (1 << 0)
>>> +#define v_AVMUTE_CLEAR(n)        (n << 7)
>>> +#define v_AVMUTE_ENABLE(n)        (n << 6)
>>> +#define v_AUDIO_MUTE(n)            (n << 1)
>>> +#define v_VIDEO_MUTE(n)            (n << 0)
>>> +
>>> +#define HDMI_VIDEO_TIMING_CTL        0x08
>>> +#define v_HSYNC_POLARITY(n)        (n << 3)
>>> +#define v_VSYNC_POLARITY(n)        (n << 2)
>>> +#define v_INETLACE(n)            (n << 1)
>>> +#define v_EXTERANL_VIDEO(n)        (n << 0)
>>> +
>>> +#define HDMI_VIDEO_EXT_HTOTAL_L        0x09
>>> +#define HDMI_VIDEO_EXT_HTOTAL_H        0x0a
>>> +#define HDMI_VIDEO_EXT_HBLANK_L        0x0b
>>> +#define HDMI_VIDEO_EXT_HBLANK_H        0x0c
>>> +#define HDMI_VIDEO_EXT_HDELAY_L        0x0d
>>> +#define HDMI_VIDEO_EXT_HDELAY_H        0x0e
>>> +#define HDMI_VIDEO_EXT_HDURATION_L    0x0f
>>> +#define HDMI_VIDEO_EXT_HDURATION_H    0x10
>>> +#define HDMI_VIDEO_EXT_VTOTAL_L        0x11
>>> +#define HDMI_VIDEO_EXT_VTOTAL_H        0x12
>>> +#define HDMI_VIDEO_EXT_VBLANK        0x13
>>> +#define HDMI_VIDEO_EXT_VDELAY        0x14
>>> +#define HDMI_VIDEO_EXT_VDURATION    0x15
>>> +
>>> +#define HDMI_VIDEO_CSC_COEF        0x18
>>> +
>>> +#define HDMI_AUDIO_CTRL1        0x35
>>> +enum {
>>> +    CTS_SOURCE_INTERNAL = 0,
>>> +    CTS_SOURCE_EXTERNAL = 1,
>>> +};
>>> +#define v_CTS_SOURCE(n)            (n << 7)
>>> +
>>> +enum {
>>> +    DOWNSAMPLE_DISABLE = 0,
>>> +    DOWNSAMPLE_1_2 = 1,
>>> +    DOWNSAMPLE_1_4 = 2,
>>> +};
>>> +#define v_DOWN_SAMPLE(n)        (n << 5)
>>> +
>>> +enum {
>>> +    AUDIO_SOURCE_IIS = 0,
>>> +    AUDIO_SOURCE_SPDIF = 1,
>>> +};
>>> +#define v_AUDIO_SOURCE(n)        (n << 3)
>>> +
>>> +#define v_MCLK_ENABLE(n)        (n << 2)
>>> +enum {
>>> +    MCLK_128FS = 0,
>>> +    MCLK_256FS = 1,
>>> +    MCLK_384FS = 2,
>>> +    MCLK_512FS = 3,
>>> +};
>>> +#define v_MCLK_RATIO(n)            (n)
>>> +
>>> +#define AUDIO_SAMPLE_RATE        0x37
>>> +enum {
>>> +    AUDIO_32K = 0x3,
>>> +    AUDIO_441K = 0x0,
>>> +    AUDIO_48K = 0x2,
>>> +    AUDIO_882K = 0x8,
>>> +    AUDIO_96K = 0xa,
>>> +    AUDIO_1764K = 0xc,
>>> +    AUDIO_192K = 0xe,
>>> +};
>>> +
>>> +#define AUDIO_I2S_MODE            0x38
>>> +enum {
>>> +    I2S_CHANNEL_1_2 = 1,
>>> +    I2S_CHANNEL_3_4 = 3,
>>> +    I2S_CHANNEL_5_6 = 7,
>>> +    I2S_CHANNEL_7_8 = 0xf
>>> +};
>>> +#define v_I2S_CHANNEL(n)        ((n) << 2)
>>> +enum {
>>> +    I2S_STANDARD = 0,
>>> +    I2S_LEFT_JUSTIFIED = 1,
>>> +    I2S_RIGHT_JUSTIFIED = 2,
>>> +};
>>> +#define v_I2S_MODE(n)            (n)
>>> +
>>> +#define AUDIO_I2S_MAP            0x39
>>> +#define AUDIO_I2S_SWAPS_SPDIF        0x3a
>>> +#define v_SPIDF_FREQ(n)            (n)
>>> +
>>> +#define N_32K                0x1000
>>> +#define N_441K                0x1880
>>> +#define N_882K                0x3100
>>> +#define N_1764K                0x6200
>>> +#define N_48K                0x1800
>>> +#define N_96K                0x3000
>>> +#define N_192K                0x6000
>>> +
>>> +#define HDMI_AUDIO_CHANNEL_STATUS    0x3e
>>> +#define m_AUDIO_STATUS_NLPCM        (1 << 7)
>>> +#define m_AUDIO_STATUS_USE        (1 << 6)
>>> +#define m_AUDIO_STATUS_COPYRIGHT    (1 << 5)
>>> +#define m_AUDIO_STATUS_ADDITION        (3 << 2)
>>> +#define m_AUDIO_STATUS_CLK_ACCURACY    (2 << 0)
>>> +#define v_AUDIO_STATUS_NLPCM(n)        ((n & 1) << 7)
>>> +#define AUDIO_N_H            0x3f
>>> +#define AUDIO_N_M            0x40
>>> +#define AUDIO_N_L            0x41
>>> +
>>> +#define HDMI_AUDIO_CTS_H        0x45
>>> +#define HDMI_AUDIO_CTS_M        0x46
>>> +#define HDMI_AUDIO_CTS_L        0x47
>>> +
>>> +#define HDMI_DDC_CLK_L            0x4b
>>> +#define HDMI_DDC_CLK_H            0x4c
>>> +
>>> +#define HDMI_EDID_SEGMENT_POINTER    0x4d
>>> +#define HDMI_EDID_WORD_ADDR        0x4e
>>> +#define HDMI_EDID_FIFO_OFFSET        0x4f
>>> +#define HDMI_EDID_FIFO_ADDR        0x50
>>> +
>>> +#define HDMI_PACKET_SEND_MANUAL        0x9c
>>> +#define HDMI_PACKET_SEND_AUTO        0x9d
>>> +#define m_PACKET_GCP_EN            (1 << 7)
>>> +#define m_PACKET_MSI_EN            (1 << 6)
>>> +#define m_PACKET_SDI_EN            (1 << 5)
>>> +#define m_PACKET_VSI_EN            (1 << 4)
>>> +#define v_PACKET_GCP_EN(n)        ((n & 1) << 7)
>>> +#define v_PACKET_MSI_EN(n)        ((n & 1) << 6)
>>> +#define v_PACKET_SDI_EN(n)        ((n & 1) << 5)
>>> +#define v_PACKET_VSI_EN(n)        ((n & 1) << 4)
>>> +
>>> +#define HDMI_CONTROL_PACKET_BUF_INDEX    0x9f
>>> +enum {
>>> +    INFOFRAME_VSI = 0x05,
>>> +    INFOFRAME_AVI = 0x06,
>>> +    INFOFRAME_AAI = 0x08,
>>> +};
>>> +
>>> +#define HDMI_CONTROL_PACKET_ADDR    0xa0
>>> +#define HDMI_SIZE_VSI_INFOFRAME        0x0A
>>> +#define HDMI_SIZE_AVI_INFOFRAME        0x11
>>> +#define HDMI_SIZE_AUDIO_INFOFRAME    0x0F
>>> +enum {
>>> +    AVI_COLOR_MODE_RGB = 0,
>>> +    AVI_COLOR_MODE_YCBCR422 = 1,
>>> +    AVI_COLOR_MODE_YCBCR444 = 2,
>>> +    AVI_COLORIMETRY_NO_DATA = 0,
>>> +
>>> +    AVI_COLORIMETRY_SMPTE_170M = 1,
>>> +    AVI_COLORIMETRY_ITU709 = 2,
>>> +    AVI_COLORIMETRY_EXTENDED = 3,
>>> +
>>> +    AVI_CODED_FRAME_ASPECT_NO_DATA = 0,
>>> +    AVI_CODED_FRAME_ASPECT_4_3 = 1,
>>> +    AVI_CODED_FRAME_ASPECT_16_9 = 2,
>>> +
>>> +    ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME = 0x08,
>>> +    ACTIVE_ASPECT_RATE_4_3 = 0x09,
>>> +    ACTIVE_ASPECT_RATE_16_9 = 0x0A,
>>> +    ACTIVE_ASPECT_RATE_14_9 = 0x0B,
>>> +};
>>> +
>>> +#define HDMI_HDCP_CTRL            0x52
>>> +#define m_HDMI_DVI            (1 << 1)
>>> +#define v_HDMI_DVI(n)            (n << 1)
>>> +
>>> +#define HDMI_INTERRUPT_MASK1        0xc0
>>> +#define HDMI_INTERRUPT_STATUS1        0xc1
>>> +#define    m_INT_ACTIVE_VSYNC        (1 << 5)
>>> +#define m_INT_EDID_READY        (1 << 2)
>>> +
>>> +#define HDMI_INTERRUPT_MASK2        0xc2
>>> +#define HDMI_INTERRUPT_STATUS2        0xc3
>>> +#define m_INT_HDCP_ERR            (1 << 7)
>>> +#define m_INT_BKSV_FLAG            (1 << 6)
>>> +#define m_INT_HDCP_OK            (1 << 4)
>>> +
>>> +#define HDMI_STATUS            0xc8
>>> +#define m_HOTPLUG            (1 << 7)
>>> +#define m_MASK_INT_HOTPLUG        (1 << 5)
>>> +#define m_INT_HOTPLUG            (1 << 1)
>>> +#define v_MASK_INT_HOTPLUG(n)        ((n & 0x1) << 5)
>>> +
>>> +#define HDMI_COLORBAR                   0xc9
>>> +
>>> +#define HDMI_PHY_SYNC            0xce
>>> +#define HDMI_PHY_SYS_CTL        0xe0
>>> +#define m_TMDS_CLK_SOURCE        (1 << 5)
>>> +#define v_TMDS_FROM_PLL            (0 << 5)
>>> +#define v_TMDS_FROM_GEN            (1 << 5)
>>> +#define m_PHASE_CLK            (1 << 4)
>>> +#define v_DEFAULT_PHASE            (0 << 4)
>>> +#define v_SYNC_PHASE            (1 << 4)
>>> +#define m_TMDS_CURRENT_PWR        (1 << 3)
>>> +#define v_TURN_ON_CURRENT        (0 << 3)
>>> +#define v_CAT_OFF_CURRENT        (1 << 3)
>>> +#define m_BANDGAP_PWR            (1 << 2)
>>> +#define v_BANDGAP_PWR_UP        (0 << 2)
>>> +#define v_BANDGAP_PWR_DOWN        (1 << 2)
>>> +#define m_PLL_PWR            (1 << 1)
>>> +#define v_PLL_PWR_UP            (0 << 1)
>>> +#define v_PLL_PWR_DOWN            (1 << 1)
>>> +#define m_TMDS_CHG_PWR            (1 << 0)
>>> +#define v_TMDS_CHG_PWR_UP        (0 << 0)
>>> +#define v_TMDS_CHG_PWR_DOWN        (1 << 0)
>>> +
>>> +#define HDMI_PHY_CHG_PWR        0xe1
>>> +#define v_CLK_CHG_PWR(n)        ((n & 1) << 3)
>>> +#define v_DATA_CHG_PWR(n)        ((n & 7) << 0)
>>> +
>>> +#define HDMI_PHY_DRIVER            0xe2
>>> +#define v_CLK_MAIN_DRIVER(n)        (n << 4)
>>> +#define v_DATA_MAIN_DRIVER(n)        (n << 0)
>>> +
>>> +#define HDMI_PHY_PRE_EMPHASIS        0xe3
>>> +#define v_PRE_EMPHASIS(n)        ((n & 7) << 4)
>>> +#define v_CLK_PRE_DRIVER(n)        ((n & 3) << 2)
>>> +#define v_DATA_PRE_DRIVER(n)        ((n & 3) << 0)
>>> +
>>> +#define HDMI_PHY_FEEDBACK_DIV_RATIO_LOW        0xe7
>>> +#define v_FEEDBACK_DIV_LOW(n)            (n & 0xff)
>>> +#define HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH    0xe8
>>> +#define v_FEEDBACK_DIV_HIGH(n)            (n & 1)
>>> +
>>> +#define HDMI_PHY_PRE_DIV_RATIO        0xed
>>> +#define v_PRE_DIV_RATIO(n)        (n & 0x1f)
>>> +
>>> +#define HDMI_CEC_CTRL            0xd0
>>> +#define m_ADJUST_FOR_HISENSE        (1 << 6)
>>> +#define m_REJECT_RX_BROADCAST        (1 << 5)
>>> +#define m_BUSFREETIME_ENABLE        (1 << 2)
>>> +#define m_REJECT_RX            (1 << 1)
>>> +#define m_START_TX            (1 << 0)
>>> +
>>> +#define HDMI_CEC_DATA            0xd1
>>> +#define HDMI_CEC_TX_OFFSET        0xd2
>>> +#define HDMI_CEC_RX_OFFSET        0xd3
>>> +#define HDMI_CEC_CLK_H            0xd4
>>> +#define HDMI_CEC_CLK_L            0xd5
>>> +#define HDMI_CEC_TX_LENGTH        0xd6
>>> +#define HDMI_CEC_RX_LENGTH        0xd7
>>> +#define HDMI_CEC_TX_INT_MASK        0xd8
>>> +#define m_TX_DONE            (1 << 3)
>>> +#define m_TX_NOACK            (1 << 2)
>>> +#define m_TX_BROADCAST_REJ        (1 << 1)
>>> +#define m_TX_BUSNOTFREE            (1 << 0)
>>> +
>>> +#define HDMI_CEC_RX_INT_MASK        0xd9
>>> +#define m_RX_LA_ERR            (1 << 4)
>>> +#define m_RX_GLITCH            (1 << 3)
>>> +#define m_RX_DONE            (1 << 0)
>>> +
>>> +#define HDMI_CEC_TX_INT            0xda
>>> +#define HDMI_CEC_RX_INT            0xdb
>>> +#define HDMI_CEC_BUSFREETIME_L        0xdc
>>> +#define HDMI_CEC_BUSFREETIME_H        0xdd
>>> +#define HDMI_CEC_LOGICADDR        0xde
>>> +
>>> +#endif /* __INNO_HDMI_H__ */
>>
>>
>
>




From ykk at rock-chips.com  Sun Jan 24 17:43:17 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Mon, 25 Jan 2016 09:43:17 +0800
Subject: [PATCH v6 0/1] Introduce Innosilicon HDMI driver on Rockchip platforms
Message-ID: <1453686197-20975-1-git-send-email-ykk@rock-chips.com>


Here are a brief introduction to Innosilicon HDMI IP:
  - Support HDMI 1.4a, HDCP 1.2 and DVI 1.0 standard compliant transmitter
  - Support HDMI1.4 a/b 3D function defined in HDMI 1.4 a/b spec
  - Digital video interface supports a pixel size of 24, 30, 36, 48bits color depth in RGB
  - S/PDIF output supports PCM, Dolby Digital, DTS digital audio transmission
    (32-192kHz Fs) using IEC60958 and IEC 61937
  - The EDID and CEC function are also supported by Innosilicon HDMI Transmitter Controlle


Changes in v6:
- Rebase the Makefile/Kconfig files which add by Chris's rockchip-mipi driver (Caeser)

Changes in v5:
- Use hdmi_infoframe helper functions to packed the infoframe (Russell)
- Remove the unused double wait_for_completion_timeout for ddc transfer (Russell)
- Remove the unused local variable in "inno_hdmi_i2c_write()" function (Russell)

Changes in v4:
- Modify the commit title "drm/rockchip: hdmi: ..." (Mark)
- Correct the "DKMS" to "DPMS" (Mark)
- Fix over 80 characters problems (Mark)
- Remove encoder .prepare/.commit helper functions, and move the vop mode
configure function into encoder .enable helper functions. (Mark)

Changes in v3:
- Use encoder enable/disable function, and remove the encoder DPMS function
- Keep HDMI PLL power on in standby mode

Changes in v2:
- Using DRM atomic helper functions for connector init (Mark)
    - Remove "hdmi->connector.encoder = encoder;" (Mark)

Yakir Yang (1):
  drm/rockchip: hdmi: add Innosilicon HDMI support

 drivers/gpu/drm/rockchip/Kconfig     |   8 +
 drivers/gpu/drm/rockchip/Makefile    |   1 +
 drivers/gpu/drm/rockchip/inno_hdmi.c | 941 +++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/inno_hdmi.h | 362 ++++++++++++++
 4 files changed, 1312 insertions(+)
 create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.c
 create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.h

-- 
1.9.1




From ykk at rock-chips.com  Sun Jan 24 17:47:10 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Mon, 25 Jan 2016 09:47:10 +0800
Subject: [PATCH v6] drm/rockchip: hdmi: add Innosilicon HDMI support
In-Reply-To: <1453686197-20975-1-git-send-email-ykk@rock-chips.com>
References: <1453686197-20975-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1453686430-21470-1-git-send-email-ykk@rock-chips.com>

The Innosilicon HDMI is a low power HDMI 1.4 transmitter
IP, and it have been integrated on some rockchip CPUs
(like RK3036, RK312x).

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v6:
- Rebase the Makefile/Kconfig files which add by Chris's rockchip-mipi driver (Caeser)

Changes in v5:
- Use hdmi_infoframe helper functions to packed the infoframe (Russell)
- Remove the unused double wait_for_completion_timeout for ddc transfer (Russell)
- Remove the unused local variable in "inno_hdmi_i2c_write()" function (Russell)

Changes in v4:
- Modify the commit title "drm/rockchip: hdmi: ..." (Mark)
- Correct the "DKMS" to "DPMS" (Mark)
- Fix over 80 characters problems (Mark)
- Remove encoder .prepare/.commit helper functions, and move the vop mode
configure function into encoder .enable helper functions. (Mark)

Changes in v3:
- Use encoder enable/disable function, and remove the encoder DPMS function
- Keep HDMI PLL power on in standby mode

Changes in v2:
- Using DRM atomic helper functions for connector init (Mark)
    - Remove "hdmi->connector.encoder = encoder;" (Mark)

 drivers/gpu/drm/rockchip/Kconfig     |   8 +
 drivers/gpu/drm/rockchip/Makefile    |   1 +
 drivers/gpu/drm/rockchip/inno_hdmi.c | 941 +++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/inno_hdmi.h | 362 ++++++++++++++
 4 files changed, 1312 insertions(+)
 create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.c
 create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.h

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 8573985..e15bf47 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -35,3 +35,11 @@ config ROCKCHIP_DW_MIPI_DSI
 	 for the Synopsys DesignWare HDMI driver. If you want to
 	 enable MIPI DSI on RK3288 based SoC, you should selet this
 	 option.
+
+config ROCKCHIP_INNO_HDMI
+	tristate "Rockchip specific extensions for Innosilicon HDMI"
+        depends on DRM_ROCKCHIP
+        help
+	  This selects support for Rockchip SoC specific extensions
+	  for the Innosilicon HDMI driver. If you want to enable
+	  HDMI on RK3036 based SoC, you should selet this option.
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index f6a809a..df8fbef 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -8,5 +8,6 @@ rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
 
 obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
 obj-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
+obj-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
 
 obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_vop_reg.o
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
new file mode 100644
index 0000000..4da3020
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -0,0 +1,941 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ *    Zheng Yang <zhengyang at rock-chips.com>
+ *    Yakir Yang <ykk at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/hdmi.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+
+#include <drm/drm_of.h>
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_encoder_slave.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_vop.h"
+
+#include "inno_hdmi.h"
+
+#define to_inno_hdmi(x)	container_of(x, struct inno_hdmi, x)
+
+struct hdmi_data_info {
+	int vic;
+	bool sink_is_hdmi;
+	bool sink_has_audio;
+	unsigned int enc_in_format;
+	unsigned int enc_out_format;
+	unsigned int colorimetry;
+};
+
+struct inno_hdmi_i2c {
+	struct i2c_adapter adap;
+
+	u8 ddc_addr;
+	u8 segment_addr;
+
+	struct mutex lock;
+	struct completion cmp;
+};
+
+struct inno_hdmi {
+	struct device *dev;
+	struct drm_device *drm_dev;
+
+	int irq;
+	struct clk *pclk;
+	void __iomem *regs;
+
+	struct drm_connector	connector;
+	struct drm_encoder	encoder;
+
+	struct inno_hdmi_i2c *i2c;
+	struct i2c_adapter *ddc;
+
+	unsigned int tmds_rate;
+
+	struct hdmi_data_info	hdmi_data;
+	struct drm_display_mode previous_mode;
+};
+
+enum {
+	CSC_ITU601_16_235_TO_RGB_0_255_8BIT,
+	CSC_ITU601_0_255_TO_RGB_0_255_8BIT,
+	CSC_ITU709_16_235_TO_RGB_0_255_8BIT,
+	CSC_RGB_0_255_TO_ITU601_16_235_8BIT,
+	CSC_RGB_0_255_TO_ITU709_16_235_8BIT,
+	CSC_RGB_0_255_TO_RGB_16_235_8BIT,
+};
+
+static const char coeff_csc[][24] = {
+	/*
+	 * YUV2RGB:601 SD mode(Y[16:235], UV[16:240], RGB[0:255]):
+	 *   R = 1.164*Y + 1.596*V - 204
+	 *   G = 1.164*Y - 0.391*U - 0.813*V + 154
+	 *   B = 1.164*Y + 2.018*U - 258
+	 */
+	{
+		0x04, 0xa7, 0x00, 0x00, 0x06, 0x62, 0x02, 0xcc,
+		0x04, 0xa7, 0x11, 0x90, 0x13, 0x40, 0x00, 0x9a,
+		0x04, 0xa7, 0x08, 0x12, 0x00, 0x00, 0x03, 0x02
+	},
+	/*
+	 * YUV2RGB:601 SD mode(YUV[0:255],RGB[0:255]):
+	 *   R = Y + 1.402*V - 248
+	 *   G = Y - 0.344*U - 0.714*V + 135
+	 *   B = Y + 1.772*U - 227
+	 */
+	{
+		0x04, 0x00, 0x00, 0x00, 0x05, 0x9b, 0x02, 0xf8,
+		0x04, 0x00, 0x11, 0x60, 0x12, 0xdb, 0x00, 0x87,
+		0x04, 0x00, 0x07, 0x16, 0x00, 0x00, 0x02, 0xe3
+	},
+	/*
+	 * YUV2RGB:709 HD mode(Y[16:235],UV[16:240],RGB[0:255]):
+	 *   R = 1.164*Y + 1.793*V - 248
+	 *   G = 1.164*Y - 0.213*U - 0.534*V + 77
+	 *   B = 1.164*Y + 2.115*U - 289
+	 */
+	{
+		0x04, 0xa7, 0x00, 0x00, 0x07, 0x2c, 0x02, 0xf8,
+		0x04, 0xa7, 0x10, 0xda, 0x12, 0x22, 0x00, 0x4d,
+		0x04, 0xa7, 0x08, 0x74, 0x00, 0x00, 0x03, 0x21
+	},
+
+	/*
+	 * RGB2YUV:601 SD mode:
+	 *   Cb = -0.291G - 0.148R + 0.439B + 128
+	 *   Y  = 0.504G  + 0.257R + 0.098B + 16
+	 *   Cr = -0.368G + 0.439R - 0.071B + 128
+	 */
+	{
+		0x11, 0x5f, 0x01, 0x82, 0x10, 0x23, 0x00, 0x80,
+		0x02, 0x1c, 0x00, 0xa1, 0x00, 0x36, 0x00, 0x1e,
+		0x11, 0x29, 0x10, 0x59, 0x01, 0x82, 0x00, 0x80
+	},
+	/*
+	 * RGB2YUV:709 HD mode:
+	 *   Cb = - 0.338G - 0.101R + 0.439B + 128
+	 *   Y  = 0.614G   + 0.183R + 0.062B + 16
+	 *   Cr = - 0.399G + 0.439R - 0.040B + 128
+	 */
+	{
+		0x11, 0x98, 0x01, 0xc1, 0x10, 0x28, 0x00, 0x80,
+		0x02, 0x74, 0x00, 0xbb, 0x00, 0x3f, 0x00, 0x10,
+		0x11, 0x5a, 0x10, 0x67, 0x01, 0xc1, 0x00, 0x80
+	},
+	/*
+	 * RGB[0:255]2RGB[16:235]:
+	 *   R' = R x (235-16)/255 + 16;
+	 *   G' = G x (235-16)/255 + 16;
+	 *   B' = B x (235-16)/255 + 16;
+	 */
+	{
+		0x00, 0x00, 0x03, 0x6F, 0x00, 0x00, 0x00, 0x10,
+		0x03, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+		0x00, 0x00, 0x00, 0x00, 0x03, 0x6F, 0x00, 0x10
+	},
+};
+
+static inline u8 hdmi_readb(struct inno_hdmi *hdmi, u16 offset)
+{
+	return readl_relaxed(hdmi->regs + (offset) * 0x04);
+}
+
+static inline void hdmi_writeb(struct inno_hdmi *hdmi, u16 offset, u32 val)
+{
+	writel_relaxed(val, hdmi->regs + (offset) * 0x04);
+}
+
+static inline void hdmi_modb(struct inno_hdmi *hdmi, u16 offset,
+			     u32 msk, u32 val)
+{
+	u8 temp = hdmi_readb(hdmi, offset) & ~msk;
+
+	temp |= val & msk;
+	hdmi_writeb(hdmi, offset, temp);
+}
+
+static void inno_hdmi_i2c_init(struct inno_hdmi *hdmi)
+{
+	int ddc_bus_freq;
+
+	ddc_bus_freq = (hdmi->tmds_rate >> 2) / HDMI_SCL_RATE;
+
+	hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
+	hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
+
+	/* Clear the EDID interrupt flag and mute the interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+}
+
+static void inno_hdmi_sys_power(struct inno_hdmi *hdmi, bool enable)
+{
+	if (enable)
+		hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON);
+	else
+		hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
+}
+
+static void inno_hdmi_set_pwr_mode(struct inno_hdmi *hdmi, int mode)
+{
+	switch (mode) {
+	case NORMAL:
+		inno_hdmi_sys_power(hdmi, false);
+
+		hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x6f);
+		hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0xbb);
+
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x14);
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x10);
+		hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x0f);
+		hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x01);
+
+		inno_hdmi_sys_power(hdmi, true);
+		break;
+
+	case LOWER_PWR:
+		inno_hdmi_sys_power(hdmi, false);
+		hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
+
+		break;
+
+	default:
+		dev_err(hdmi->dev, "Unknown power mode %d\n", mode);
+	}
+}
+
+static void inno_hdmi_reset(struct inno_hdmi *hdmi)
+{
+	u32 val;
+	u32 msk;
+
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_DIGITAL, v_NOT_RST_DIGITAL);
+	udelay(100);
+
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_ANALOG, v_NOT_RST_ANALOG);
+	udelay(100);
+
+	msk = m_REG_CLK_INV | m_REG_CLK_SOURCE | m_POWER | m_INT_POL;
+	val = v_REG_CLK_INV | v_REG_CLK_SOURCE_SYS | v_PWR_ON | v_INT_POL_HIGH;
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, msk, val);
+
+	inno_hdmi_set_pwr_mode(hdmi, NORMAL);
+}
+
+static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi, int setup_rc,
+				  union hdmi_infoframe *frame, u32 frame_index,
+				  u32 mask, u32 disable, u32 enable)
+{
+	if (mask)
+		hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, disable);
+
+	hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, frame_index);
+
+	if (setup_rc >= 0) {
+		u8 packed_frame[HDMI_MAXIMUM_INFO_FRAME_SIZE];
+		ssize_t rc, i;
+
+		rc = hdmi_infoframe_pack(frame, packed_frame,
+					 sizeof(packed_frame));
+		if (rc < 0)
+			return rc;
+
+		for (i = 0; i < rc; i++)
+			hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i,
+				    packed_frame[i]);
+
+		if (mask)
+			hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, enable);
+	}
+
+	return setup_rc;
+}
+
+static int inno_hdmi_config_video_vsi(struct inno_hdmi *hdmi,
+				      struct drm_display_mode *mode)
+{
+	union hdmi_infoframe frame;
+	int rc;
+
+	rc = drm_hdmi_vendor_infoframe_from_display_mode(&frame.vendor.hdmi,
+							 mode);
+
+	return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_VSI,
+		m_PACKET_VSI_EN, v_PACKET_VSI_EN(0), v_PACKET_VSI_EN(1));
+}
+
+static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
+				      struct drm_display_mode *mode)
+{
+	union hdmi_infoframe frame;
+	int rc;
+
+	rc = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, mode);
+
+	if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_RGB)
+		frame.avi.colorspace = HDMI_COLORSPACE_RGB;
+	else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV444)
+		frame.avi.colorspace = HDMI_COLORSPACE_YUV444;
+	else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV422)
+		frame.avi.colorspace = HDMI_COLORSPACE_YUV422;
+	else
+		frame.avi.colorspace = HDMI_COLORSPACE_RGB;
+
+	return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_AVI, 0, 0, 0);
+}
+
+static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
+{
+	struct hdmi_data_info *data = &hdmi->hdmi_data;
+	int c0_c2_change = 0;
+	int csc_enable = 0;
+	int csc_mode = 0;
+	int auto_csc = 0;
+	int value;
+	int i;
+
+	/* Input video mode is SDR RGB24bit, data enable signal from external */
+	hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL1, v_DE_EXTERNAL |
+		    v_VIDEO_INPUT_FORMAT(VIDEO_INPUT_SDR_RGB444));
+
+	/* Input color hardcode to RGB, and output color hardcode to RGB888 */
+	value = v_VIDEO_INPUT_BITS(VIDEO_INPUT_8BITS) |
+		v_VIDEO_OUTPUT_COLOR(0) |
+		v_VIDEO_INPUT_CSP(0);
+	hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL2, value);
+
+	if (data->enc_out_format == data->enc_out_format) {
+		if ((data->enc_in_format == HDMI_COLORSPACE_RGB) ||
+		    (data->enc_in_format >= HDMI_COLORSPACE_YUV444)) {
+			value = v_SOF_DISABLE | v_COLOR_DEPTH_NOT_INDICATED(1);
+			hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
+
+			hdmi_modb(hdmi, HDMI_VIDEO_CONTRL,
+				  m_VIDEO_AUTO_CSC | m_VIDEO_C0_C2_SWAP,
+				  v_VIDEO_AUTO_CSC(AUTO_CSC_DISABLE) |
+				  v_VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE));
+			return 0;
+		}
+	}
+
+	if (data->colorimetry == HDMI_COLORIMETRY_ITU_601) {
+		if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
+		    (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
+			csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT;
+			auto_csc = AUTO_CSC_DISABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = v_CSC_ENABLE;
+		} else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
+			   (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
+			csc_mode = CSC_ITU601_16_235_TO_RGB_0_255_8BIT;
+			auto_csc = AUTO_CSC_ENABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = v_CSC_DISABLE;
+		}
+	} else {
+		if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
+		    (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
+			csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT;
+			auto_csc = AUTO_CSC_DISABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = v_CSC_ENABLE;
+		} else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
+			   (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
+			csc_mode = CSC_ITU709_16_235_TO_RGB_0_255_8BIT;
+			auto_csc = AUTO_CSC_ENABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = v_CSC_DISABLE;
+		}
+	}
+
+	for (i = 0; i < 24; i++)
+		hdmi_writeb(hdmi, HDMI_VIDEO_CSC_COEF + i,
+			    coeff_csc[csc_mode][i]);
+
+	value = v_SOF_DISABLE | csc_enable | v_COLOR_DEPTH_NOT_INDICATED(1);
+	hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
+	hdmi_modb(hdmi, HDMI_VIDEO_CONTRL, m_VIDEO_AUTO_CSC |
+		  m_VIDEO_C0_C2_SWAP, v_VIDEO_AUTO_CSC(auto_csc) |
+		  v_VIDEO_C0_C2_SWAP(c0_c2_change));
+
+	return 0;
+}
+
+static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi,
+					 struct drm_display_mode *mode)
+{
+	int value;
+
+	/* Set detail external video timing polarity and interlace mode */
+	value = v_EXTERANL_VIDEO(1);
+	value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
+		 v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
+	value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
+		 v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
+	value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
+		 v_INETLACE(1) : v_INETLACE(0);
+	hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
+
+	/* Set detail external video timing */
+	value = mode->htotal;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
+
+	value = mode->htotal - mode->hdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
+
+	value = mode->hsync_start - mode->hdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
+
+	value = mode->hsync_end - mode->hsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
+
+	value = mode->vtotal;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
+
+	value = mode->vtotal - mode->vdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
+
+	value = mode->vsync_start - mode->vdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
+
+	value = mode->vsync_end - mode->vsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
+
+	hdmi_writeb(hdmi, HDMI_PHY_PRE_DIV_RATIO, 0x1e);
+	hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_LOW, 0x2c);
+	hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH, 0x01);
+
+	return 0;
+}
+
+static int inno_hdmi_setup(struct inno_hdmi *hdmi,
+			   struct drm_display_mode *mode)
+{
+	hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
+
+	hdmi->hdmi_data.enc_in_format = HDMI_COLORSPACE_RGB;
+	hdmi->hdmi_data.enc_out_format = HDMI_COLORSPACE_RGB;
+
+	if ((hdmi->hdmi_data.vic == 6) || (hdmi->hdmi_data.vic == 7) ||
+	    (hdmi->hdmi_data.vic == 21) || (hdmi->hdmi_data.vic == 22) ||
+	    (hdmi->hdmi_data.vic == 2) || (hdmi->hdmi_data.vic == 3) ||
+	    (hdmi->hdmi_data.vic == 17) || (hdmi->hdmi_data.vic == 18))
+		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601;
+	else
+		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
+
+	/* Mute video and audio output */
+	hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
+		  v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1));
+
+	/* Set HDMI Mode */
+	hdmi_writeb(hdmi, HDMI_HDCP_CTRL,
+		    v_HDMI_DVI(hdmi->hdmi_data.sink_is_hdmi));
+
+	inno_hdmi_config_video_timing(hdmi, mode);
+
+	inno_hdmi_config_video_csc(hdmi);
+
+	if (hdmi->hdmi_data.sink_is_hdmi) {
+		inno_hdmi_config_video_avi(hdmi, mode);
+		inno_hdmi_config_video_vsi(hdmi, mode);
+	}
+
+	/*
+	 * When IP controller have configured to an accurate video
+	 * timing, then the TMDS clock source would be switched to
+	 * DCLK_LCDC, so we need to init the TMDS rate to mode pixel
+	 * clock rate, and reconfigure the DDC clock.
+	 */
+	hdmi->tmds_rate = mode->clock * 1000;
+	inno_hdmi_i2c_init(hdmi);
+
+	/* Unmute video and audio output */
+	hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
+		  v_AUDIO_MUTE(0) | v_VIDEO_MUTE(0));
+
+	return 0;
+}
+
+static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder,
+				       struct drm_display_mode *mode,
+				       struct drm_display_mode *adj_mode)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+
+	inno_hdmi_setup(hdmi, adj_mode);
+
+	/* Store the display mode for plugin/DPMS poweron events */
+	memcpy(&hdmi->previous_mode, adj_mode, sizeof(hdmi->previous_mode));
+}
+
+static void inno_hdmi_encoder_enable(struct drm_encoder *encoder)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+
+	rockchip_drm_crtc_mode_config(encoder->crtc, DRM_MODE_CONNECTOR_HDMIA,
+				      ROCKCHIP_OUT_MODE_P888);
+
+	inno_hdmi_set_pwr_mode(hdmi, NORMAL);
+}
+
+static void inno_hdmi_encoder_disable(struct drm_encoder *encoder)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+
+	inno_hdmi_set_pwr_mode(hdmi, LOWER_PWR);
+}
+
+static bool inno_hdmi_encoder_mode_fixup(struct drm_encoder *encoder,
+					 const struct drm_display_mode *mode,
+					 struct drm_display_mode *adj_mode)
+{
+	return true;
+}
+
+static struct drm_encoder_helper_funcs inno_hdmi_encoder_helper_funcs = {
+	.enable     = inno_hdmi_encoder_enable,
+	.disable    = inno_hdmi_encoder_disable,
+	.mode_fixup = inno_hdmi_encoder_mode_fixup,
+	.mode_set   = inno_hdmi_encoder_mode_set,
+};
+
+static struct drm_encoder_funcs inno_hdmi_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static enum drm_connector_status
+inno_hdmi_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+
+	return (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
+		connector_status_connected : connector_status_disconnected;
+}
+
+static int inno_hdmi_connector_get_modes(struct drm_connector *connector)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+	struct edid *edid;
+	int ret = 0;
+
+	if (!hdmi->ddc)
+		return 0;
+
+	edid = drm_get_edid(connector, hdmi->ddc);
+	if (edid) {
+		hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
+		hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
+		drm_mode_connector_update_edid_property(connector, edid);
+		ret = drm_add_edid_modes(connector, edid);
+		kfree(edid);
+	}
+
+	return ret;
+}
+
+static enum drm_mode_status
+inno_hdmi_connector_mode_valid(struct drm_connector *connector,
+			       struct drm_display_mode *mode)
+{
+	return MODE_OK;
+}
+
+static struct drm_encoder *
+inno_hdmi_connector_best_encoder(struct drm_connector *connector)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+
+	return &hdmi->encoder;
+}
+
+static int
+inno_hdmi_probe_single_connector_modes(struct drm_connector *connector,
+				       uint32_t maxX, uint32_t maxY)
+{
+	return drm_helper_probe_single_connector_modes(connector, 1920, 1080);
+}
+
+static void inno_hdmi_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs inno_hdmi_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.fill_modes = inno_hdmi_probe_single_connector_modes,
+	.detect = inno_hdmi_connector_detect,
+	.destroy = inno_hdmi_connector_destroy,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static struct drm_connector_helper_funcs inno_hdmi_connector_helper_funcs = {
+	.get_modes = inno_hdmi_connector_get_modes,
+	.mode_valid = inno_hdmi_connector_mode_valid,
+	.best_encoder = inno_hdmi_connector_best_encoder,
+};
+
+static int inno_hdmi_register(struct drm_device *drm, struct inno_hdmi *hdmi)
+{
+	struct drm_encoder *encoder = &hdmi->encoder;
+	struct device *dev = hdmi->dev;
+
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
+
+	/*
+	 * If we failed to find the CRTC(s) which this encoder is
+	 * supposed to be connected to, it's because the CRTC has
+	 * not been registered yet.  Defer probing, and hope that
+	 * the required CRTC is added later.
+	 */
+	if (encoder->possible_crtcs == 0)
+		return -EPROBE_DEFER;
+
+	drm_encoder_helper_add(encoder, &inno_hdmi_encoder_helper_funcs);
+	drm_encoder_init(drm, encoder, &inno_hdmi_encoder_funcs,
+			 DRM_MODE_ENCODER_TMDS, NULL);
+
+	hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
+
+	drm_connector_helper_add(&hdmi->connector,
+				 &inno_hdmi_connector_helper_funcs);
+	drm_connector_init(drm, &hdmi->connector, &inno_hdmi_connector_funcs,
+			   DRM_MODE_CONNECTOR_HDMIA);
+
+	drm_mode_connector_attach_encoder(&hdmi->connector, encoder);
+
+	return 0;
+}
+
+static irqreturn_t inno_hdmi_i2c_irq(struct inno_hdmi *hdmi)
+{
+	struct inno_hdmi_i2c *i2c = hdmi->i2c;
+	u8 stat;
+
+	stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
+	if (!(stat & m_INT_EDID_READY))
+		return IRQ_NONE;
+
+	/* Clear HDMI EDID interrupt flag */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+
+	complete(&i2c->cmp);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t inno_hdmi_hardirq(int irq, void *dev_id)
+{
+	struct inno_hdmi *hdmi = dev_id;
+	irqreturn_t ret = IRQ_NONE;
+	u8 interrupt;
+
+	if (hdmi->i2c)
+		ret = inno_hdmi_i2c_irq(hdmi);
+
+	interrupt = hdmi_readb(hdmi, HDMI_STATUS);
+	if (interrupt & m_INT_HOTPLUG) {
+		hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG);
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	return ret;
+}
+
+static irqreturn_t inno_hdmi_irq(int irq, void *dev_id)
+{
+	struct inno_hdmi *hdmi = dev_id;
+
+	drm_helper_hpd_irq_event(hdmi->connector.dev);
+
+	return IRQ_HANDLED;
+}
+
+static int inno_hdmi_i2c_read(struct inno_hdmi *hdmi, struct i2c_msg *msgs)
+{
+	int length = msgs->len;
+	u8 *buf = msgs->buf;
+	int ret;
+
+	ret = wait_for_completion_timeout(&hdmi->i2c->cmp, HZ / 10);
+	if (!ret)
+		return -EAGAIN;
+
+	while (length--)
+		*buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
+
+	return 0;
+}
+
+static int inno_hdmi_i2c_write(struct inno_hdmi *hdmi, struct i2c_msg *msgs)
+{
+	/*
+	 * The DDC module only support read EDID message, so
+	 * we assume that each word write to this i2c adapter
+	 * should be the offset of EDID word address.
+	 */
+	if ((msgs->len != 1) ||
+	    ((msgs->addr != DDC_ADDR) && (msgs->addr != DDC_SEGMENT_ADDR)))
+		return -EINVAL;
+
+	reinit_completion(&hdmi->i2c->cmp);
+
+	if (msgs->addr == DDC_SEGMENT_ADDR)
+		hdmi->i2c->segment_addr = msgs->buf[0];
+	if (msgs->addr == DDC_ADDR)
+		hdmi->i2c->ddc_addr = msgs->buf[0];
+
+	/* Set edid fifo first addr */
+	hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
+
+	/* Set edid word address 0x00/0x80 */
+	hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
+
+	/* Set edid segment pointer */
+	hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr);
+
+	return 0;
+}
+
+static int inno_hdmi_i2c_xfer(struct i2c_adapter *adap,
+			      struct i2c_msg *msgs, int num)
+{
+	struct inno_hdmi *hdmi = i2c_get_adapdata(adap);
+	struct inno_hdmi_i2c *i2c = hdmi->i2c;
+	int i, ret = 0;
+
+	mutex_lock(&i2c->lock);
+
+	/* Clear the EDID interrupt flag and unmute the interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY);
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+
+	for (i = 0; i < num; i++) {
+		dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n",
+			i + 1, num, msgs[i].len, msgs[i].flags);
+
+		if (msgs[i].flags & I2C_M_RD)
+			ret = inno_hdmi_i2c_read(hdmi, &msgs[i]);
+		else
+			ret = inno_hdmi_i2c_write(hdmi, &msgs[i]);
+
+		if (ret < 0)
+			break;
+	}
+
+	if (!ret)
+		ret = num;
+
+	/* Mute HDMI EDID interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
+
+	mutex_unlock(&i2c->lock);
+
+	return ret;
+}
+
+static u32 inno_hdmi_i2c_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm inno_hdmi_algorithm = {
+	.master_xfer	= inno_hdmi_i2c_xfer,
+	.functionality	= inno_hdmi_i2c_func,
+};
+
+static struct i2c_adapter *inno_hdmi_i2c_adapter(struct inno_hdmi *hdmi)
+{
+	struct i2c_adapter *adap;
+	struct inno_hdmi_i2c *i2c;
+	int ret;
+
+	i2c = devm_kzalloc(hdmi->dev, sizeof(*i2c), GFP_KERNEL);
+	if (!i2c)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_init(&i2c->lock);
+	init_completion(&i2c->cmp);
+
+	adap = &i2c->adap;
+	adap->class = I2C_CLASS_DDC;
+	adap->owner = THIS_MODULE;
+	adap->dev.parent = hdmi->dev;
+	adap->dev.of_node = hdmi->dev->of_node;
+	adap->algo = &inno_hdmi_algorithm;
+	strlcpy(adap->name, "Inno HDMI", sizeof(adap->name));
+	i2c_set_adapdata(adap, hdmi);
+
+	ret = i2c_add_adapter(adap);
+	if (ret) {
+		dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name);
+		devm_kfree(hdmi->dev, i2c);
+		return ERR_PTR(ret);
+	}
+
+	hdmi->i2c = i2c;
+
+	dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name);
+
+	return adap;
+}
+
+static int inno_hdmi_bind(struct device *dev, struct device *master,
+				 void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = data;
+	struct inno_hdmi *hdmi;
+	struct resource *iores;
+	int irq;
+	int ret;
+
+	hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
+	if (!hdmi)
+		return -ENOMEM;
+
+	hdmi->dev = dev;
+	hdmi->drm_dev = drm;
+
+	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!iores)
+		return -ENXIO;
+
+	hdmi->regs = devm_ioremap_resource(dev, iores);
+	if (IS_ERR(hdmi->regs))
+		return PTR_ERR(hdmi->regs);
+
+	hdmi->pclk = devm_clk_get(hdmi->dev, "pclk");
+	if (IS_ERR(hdmi->pclk)) {
+		dev_err(hdmi->dev, "Unable to get HDMI pclk clk\n");
+		return PTR_ERR(hdmi->pclk);
+	}
+
+	ret = clk_prepare_enable(hdmi->pclk);
+	if (ret) {
+		dev_err(hdmi->dev, "Cannot enable HDMI pclk clock: %d\n", ret);
+		return ret;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	inno_hdmi_reset(hdmi);
+
+	hdmi->ddc = inno_hdmi_i2c_adapter(hdmi);
+	if (IS_ERR(hdmi->ddc)) {
+		hdmi->ddc = NULL;
+		return PTR_ERR(hdmi->ddc);
+	}
+
+	/*
+	 * When IP controller haven't configured to an accurate video
+	 * timing, then the TMDS clock source would be switched to
+	 * PCLK_HDMI, so we need to init the TMDS rate to PCLK rate,
+	 * and reconfigure the DDC clock.
+	 */
+	hdmi->tmds_rate = clk_get_rate(hdmi->pclk);
+	inno_hdmi_i2c_init(hdmi);
+
+	ret = inno_hdmi_register(drm, hdmi);
+	if (ret)
+		return ret;
+
+	dev_set_drvdata(dev, hdmi);
+
+	/* Unmute hotplug interrupt */
+	hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
+
+	ret = devm_request_threaded_irq(dev, irq, inno_hdmi_hardirq,
+					inno_hdmi_irq, IRQF_SHARED,
+					dev_name(dev), hdmi);
+
+	return ret;
+}
+
+static void inno_hdmi_unbind(struct device *dev, struct device *master,
+			     void *data)
+{
+	struct inno_hdmi *hdmi = dev_get_drvdata(dev);
+
+	hdmi->connector.funcs->destroy(&hdmi->connector);
+	hdmi->encoder.funcs->destroy(&hdmi->encoder);
+
+	clk_disable_unprepare(hdmi->pclk);
+	i2c_put_adapter(hdmi->ddc);
+}
+
+static const struct component_ops inno_hdmi_ops = {
+	.bind	= inno_hdmi_bind,
+	.unbind	= inno_hdmi_unbind,
+};
+
+static int inno_hdmi_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &inno_hdmi_ops);
+}
+
+static int inno_hdmi_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &inno_hdmi_ops);
+
+	return 0;
+}
+
+static const struct of_device_id inno_hdmi_dt_ids[] = {
+	{ .compatible = "rockchip,rk3036-inno-hdmi",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, inno_hdmi_dt_ids);
+
+static struct platform_driver inno_hdmi_driver = {
+	.probe  = inno_hdmi_probe,
+	.remove = inno_hdmi_remove,
+	.driver = {
+		.name = "innohdmi-rockchip",
+		.of_match_table = inno_hdmi_dt_ids,
+	},
+};
+
+module_platform_driver(inno_hdmi_driver);
+
+MODULE_AUTHOR("Zheng Yang <zhengyang at rock-chips.com>");
+MODULE_AUTHOR("Yakir Yang <ykk at rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip Specific INNO-HDMI Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:innohdmi-rockchip");
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.h b/drivers/gpu/drm/rockchip/inno_hdmi.h
new file mode 100644
index 0000000..aa7c415
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.h
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ *    Zheng Yang <zhengyang at rock-chips.com>
+ *    Yakir Yang <ykk at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __INNO_HDMI_H__
+#define __INNO_HDMI_H__
+
+#define DDC_SEGMENT_ADDR		0x30
+
+enum PWR_MODE {
+	NORMAL,
+	LOWER_PWR,
+};
+
+#define HDMI_SCL_RATE			(100*1000)
+#define DDC_BUS_FREQ_L			0x4b
+#define DDC_BUS_FREQ_H			0x4c
+
+#define HDMI_SYS_CTRL			0x00
+#define m_RST_ANALOG			(1 << 6)
+#define v_RST_ANALOG			(0 << 6)
+#define v_NOT_RST_ANALOG		(1 << 6)
+#define m_RST_DIGITAL			(1 << 5)
+#define v_RST_DIGITAL			(0 << 5)
+#define v_NOT_RST_DIGITAL		(1 << 5)
+#define m_REG_CLK_INV			(1 << 4)
+#define v_REG_CLK_NOT_INV		(0 << 4)
+#define v_REG_CLK_INV			(1 << 4)
+#define m_VCLK_INV			(1 << 3)
+#define v_VCLK_NOT_INV			(0 << 3)
+#define v_VCLK_INV			(1 << 3)
+#define m_REG_CLK_SOURCE		(1 << 2)
+#define v_REG_CLK_SOURCE_TMDS		(0 << 2)
+#define v_REG_CLK_SOURCE_SYS		(1 << 2)
+#define m_POWER				(1 << 1)
+#define v_PWR_ON			(0 << 1)
+#define v_PWR_OFF			(1 << 1)
+#define m_INT_POL			(1 << 0)
+#define v_INT_POL_HIGH			1
+#define v_INT_POL_LOW			0
+
+#define HDMI_VIDEO_CONTRL1		0x01
+#define m_VIDEO_INPUT_FORMAT		(7 << 1)
+#define m_DE_SOURCE			(1 << 0)
+#define v_VIDEO_INPUT_FORMAT(n)		(n << 1)
+#define v_DE_EXTERNAL			1
+#define v_DE_INTERNAL			0
+enum {
+	VIDEO_INPUT_SDR_RGB444 = 0,
+	VIDEO_INPUT_DDR_RGB444 = 5,
+	VIDEO_INPUT_DDR_YCBCR422 = 6
+};
+
+#define HDMI_VIDEO_CONTRL2		0x02
+#define m_VIDEO_OUTPUT_COLOR		(3 << 6)
+#define m_VIDEO_INPUT_BITS		(3 << 4)
+#define m_VIDEO_INPUT_CSP		(1 << 0)
+#define v_VIDEO_OUTPUT_COLOR(n)		(((n) & 0x3) << 6)
+#define v_VIDEO_INPUT_BITS(n)		(n << 4)
+#define v_VIDEO_INPUT_CSP(n)		(n << 0)
+enum {
+	VIDEO_INPUT_12BITS = 0,
+	VIDEO_INPUT_10BITS = 1,
+	VIDEO_INPUT_REVERT = 2,
+	VIDEO_INPUT_8BITS = 3,
+};
+
+#define HDMI_VIDEO_CONTRL		0x03
+#define m_VIDEO_AUTO_CSC		(1 << 7)
+#define v_VIDEO_AUTO_CSC(n)		(n << 7)
+#define m_VIDEO_C0_C2_SWAP		(1 << 0)
+#define v_VIDEO_C0_C2_SWAP(n)		(n << 0)
+enum {
+	C0_C2_CHANGE_ENABLE = 0,
+	C0_C2_CHANGE_DISABLE = 1,
+	AUTO_CSC_DISABLE = 0,
+	AUTO_CSC_ENABLE = 1,
+};
+
+#define HDMI_VIDEO_CONTRL3		0x04
+#define m_COLOR_DEPTH_NOT_INDICATED	(1 << 4)
+#define m_SOF				(1 << 3)
+#define m_COLOR_RANGE			(1 << 2)
+#define m_CSC				(1 << 0)
+#define v_COLOR_DEPTH_NOT_INDICATED(n)	((n) << 4)
+#define v_SOF_ENABLE			(0 << 3)
+#define v_SOF_DISABLE			(1 << 3)
+#define v_COLOR_RANGE_FULL		(1 << 2)
+#define v_COLOR_RANGE_LIMITED		(0 << 2)
+#define v_CSC_ENABLE			1
+#define v_CSC_DISABLE			0
+
+#define HDMI_AV_MUTE			0x05
+#define m_AVMUTE_CLEAR			(1 << 7)
+#define m_AVMUTE_ENABLE			(1 << 6)
+#define m_AUDIO_MUTE			(1 << 1)
+#define m_VIDEO_BLACK			(1 << 0)
+#define v_AVMUTE_CLEAR(n)		(n << 7)
+#define v_AVMUTE_ENABLE(n)		(n << 6)
+#define v_AUDIO_MUTE(n)			(n << 1)
+#define v_VIDEO_MUTE(n)			(n << 0)
+
+#define HDMI_VIDEO_TIMING_CTL		0x08
+#define v_HSYNC_POLARITY(n)		(n << 3)
+#define v_VSYNC_POLARITY(n)		(n << 2)
+#define v_INETLACE(n)			(n << 1)
+#define v_EXTERANL_VIDEO(n)		(n << 0)
+
+#define HDMI_VIDEO_EXT_HTOTAL_L		0x09
+#define HDMI_VIDEO_EXT_HTOTAL_H		0x0a
+#define HDMI_VIDEO_EXT_HBLANK_L		0x0b
+#define HDMI_VIDEO_EXT_HBLANK_H		0x0c
+#define HDMI_VIDEO_EXT_HDELAY_L		0x0d
+#define HDMI_VIDEO_EXT_HDELAY_H		0x0e
+#define HDMI_VIDEO_EXT_HDURATION_L	0x0f
+#define HDMI_VIDEO_EXT_HDURATION_H	0x10
+#define HDMI_VIDEO_EXT_VTOTAL_L		0x11
+#define HDMI_VIDEO_EXT_VTOTAL_H		0x12
+#define HDMI_VIDEO_EXT_VBLANK		0x13
+#define HDMI_VIDEO_EXT_VDELAY		0x14
+#define HDMI_VIDEO_EXT_VDURATION	0x15
+
+#define HDMI_VIDEO_CSC_COEF		0x18
+
+#define HDMI_AUDIO_CTRL1		0x35
+enum {
+	CTS_SOURCE_INTERNAL = 0,
+	CTS_SOURCE_EXTERNAL = 1,
+};
+#define v_CTS_SOURCE(n)			(n << 7)
+
+enum {
+	DOWNSAMPLE_DISABLE = 0,
+	DOWNSAMPLE_1_2 = 1,
+	DOWNSAMPLE_1_4 = 2,
+};
+#define v_DOWN_SAMPLE(n)		(n << 5)
+
+enum {
+	AUDIO_SOURCE_IIS = 0,
+	AUDIO_SOURCE_SPDIF = 1,
+};
+#define v_AUDIO_SOURCE(n)		(n << 3)
+
+#define v_MCLK_ENABLE(n)		(n << 2)
+enum {
+	MCLK_128FS = 0,
+	MCLK_256FS = 1,
+	MCLK_384FS = 2,
+	MCLK_512FS = 3,
+};
+#define v_MCLK_RATIO(n)			(n)
+
+#define AUDIO_SAMPLE_RATE		0x37
+enum {
+	AUDIO_32K = 0x3,
+	AUDIO_441K = 0x0,
+	AUDIO_48K = 0x2,
+	AUDIO_882K = 0x8,
+	AUDIO_96K = 0xa,
+	AUDIO_1764K = 0xc,
+	AUDIO_192K = 0xe,
+};
+
+#define AUDIO_I2S_MODE			0x38
+enum {
+	I2S_CHANNEL_1_2 = 1,
+	I2S_CHANNEL_3_4 = 3,
+	I2S_CHANNEL_5_6 = 7,
+	I2S_CHANNEL_7_8 = 0xf
+};
+#define v_I2S_CHANNEL(n)		((n) << 2)
+enum {
+	I2S_STANDARD = 0,
+	I2S_LEFT_JUSTIFIED = 1,
+	I2S_RIGHT_JUSTIFIED = 2,
+};
+#define v_I2S_MODE(n)			(n)
+
+#define AUDIO_I2S_MAP			0x39
+#define AUDIO_I2S_SWAPS_SPDIF		0x3a
+#define v_SPIDF_FREQ(n)			(n)
+
+#define N_32K				0x1000
+#define N_441K				0x1880
+#define N_882K				0x3100
+#define N_1764K				0x6200
+#define N_48K				0x1800
+#define N_96K				0x3000
+#define N_192K				0x6000
+
+#define HDMI_AUDIO_CHANNEL_STATUS	0x3e
+#define m_AUDIO_STATUS_NLPCM		(1 << 7)
+#define m_AUDIO_STATUS_USE		(1 << 6)
+#define m_AUDIO_STATUS_COPYRIGHT	(1 << 5)
+#define m_AUDIO_STATUS_ADDITION		(3 << 2)
+#define m_AUDIO_STATUS_CLK_ACCURACY	(2 << 0)
+#define v_AUDIO_STATUS_NLPCM(n)		((n & 1) << 7)
+#define AUDIO_N_H			0x3f
+#define AUDIO_N_M			0x40
+#define AUDIO_N_L			0x41
+
+#define HDMI_AUDIO_CTS_H		0x45
+#define HDMI_AUDIO_CTS_M		0x46
+#define HDMI_AUDIO_CTS_L		0x47
+
+#define HDMI_DDC_CLK_L			0x4b
+#define HDMI_DDC_CLK_H			0x4c
+
+#define HDMI_EDID_SEGMENT_POINTER	0x4d
+#define HDMI_EDID_WORD_ADDR		0x4e
+#define HDMI_EDID_FIFO_OFFSET		0x4f
+#define HDMI_EDID_FIFO_ADDR		0x50
+
+#define HDMI_PACKET_SEND_MANUAL		0x9c
+#define HDMI_PACKET_SEND_AUTO		0x9d
+#define m_PACKET_GCP_EN			(1 << 7)
+#define m_PACKET_MSI_EN			(1 << 6)
+#define m_PACKET_SDI_EN			(1 << 5)
+#define m_PACKET_VSI_EN			(1 << 4)
+#define v_PACKET_GCP_EN(n)		((n & 1) << 7)
+#define v_PACKET_MSI_EN(n)		((n & 1) << 6)
+#define v_PACKET_SDI_EN(n)		((n & 1) << 5)
+#define v_PACKET_VSI_EN(n)		((n & 1) << 4)
+
+#define HDMI_CONTROL_PACKET_BUF_INDEX	0x9f
+enum {
+	INFOFRAME_VSI = 0x05,
+	INFOFRAME_AVI = 0x06,
+	INFOFRAME_AAI = 0x08,
+};
+
+#define HDMI_CONTROL_PACKET_ADDR	0xa0
+#define HDMI_MAXIMUM_INFO_FRAME_SIZE	0x11
+enum {
+	AVI_COLOR_MODE_RGB = 0,
+	AVI_COLOR_MODE_YCBCR422 = 1,
+	AVI_COLOR_MODE_YCBCR444 = 2,
+	AVI_COLORIMETRY_NO_DATA = 0,
+
+	AVI_COLORIMETRY_SMPTE_170M = 1,
+	AVI_COLORIMETRY_ITU709 = 2,
+	AVI_COLORIMETRY_EXTENDED = 3,
+
+	AVI_CODED_FRAME_ASPECT_NO_DATA = 0,
+	AVI_CODED_FRAME_ASPECT_4_3 = 1,
+	AVI_CODED_FRAME_ASPECT_16_9 = 2,
+
+	ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME = 0x08,
+	ACTIVE_ASPECT_RATE_4_3 = 0x09,
+	ACTIVE_ASPECT_RATE_16_9 = 0x0A,
+	ACTIVE_ASPECT_RATE_14_9 = 0x0B,
+};
+
+#define HDMI_HDCP_CTRL			0x52
+#define m_HDMI_DVI			(1 << 1)
+#define v_HDMI_DVI(n)			(n << 1)
+
+#define HDMI_INTERRUPT_MASK1		0xc0
+#define HDMI_INTERRUPT_STATUS1		0xc1
+#define	m_INT_ACTIVE_VSYNC		(1 << 5)
+#define m_INT_EDID_READY		(1 << 2)
+
+#define HDMI_INTERRUPT_MASK2		0xc2
+#define HDMI_INTERRUPT_STATUS2		0xc3
+#define m_INT_HDCP_ERR			(1 << 7)
+#define m_INT_BKSV_FLAG			(1 << 6)
+#define m_INT_HDCP_OK			(1 << 4)
+
+#define HDMI_STATUS			0xc8
+#define m_HOTPLUG			(1 << 7)
+#define m_MASK_INT_HOTPLUG		(1 << 5)
+#define m_INT_HOTPLUG			(1 << 1)
+#define v_MASK_INT_HOTPLUG(n)		((n & 0x1) << 5)
+
+#define HDMI_COLORBAR                   0xc9
+
+#define HDMI_PHY_SYNC			0xce
+#define HDMI_PHY_SYS_CTL		0xe0
+#define m_TMDS_CLK_SOURCE		(1 << 5)
+#define v_TMDS_FROM_PLL			(0 << 5)
+#define v_TMDS_FROM_GEN			(1 << 5)
+#define m_PHASE_CLK			(1 << 4)
+#define v_DEFAULT_PHASE			(0 << 4)
+#define v_SYNC_PHASE			(1 << 4)
+#define m_TMDS_CURRENT_PWR		(1 << 3)
+#define v_TURN_ON_CURRENT		(0 << 3)
+#define v_CAT_OFF_CURRENT		(1 << 3)
+#define m_BANDGAP_PWR			(1 << 2)
+#define v_BANDGAP_PWR_UP		(0 << 2)
+#define v_BANDGAP_PWR_DOWN		(1 << 2)
+#define m_PLL_PWR			(1 << 1)
+#define v_PLL_PWR_UP			(0 << 1)
+#define v_PLL_PWR_DOWN			(1 << 1)
+#define m_TMDS_CHG_PWR			(1 << 0)
+#define v_TMDS_CHG_PWR_UP		(0 << 0)
+#define v_TMDS_CHG_PWR_DOWN		(1 << 0)
+
+#define HDMI_PHY_CHG_PWR		0xe1
+#define v_CLK_CHG_PWR(n)		((n & 1) << 3)
+#define v_DATA_CHG_PWR(n)		((n & 7) << 0)
+
+#define HDMI_PHY_DRIVER			0xe2
+#define v_CLK_MAIN_DRIVER(n)		(n << 4)
+#define v_DATA_MAIN_DRIVER(n)		(n << 0)
+
+#define HDMI_PHY_PRE_EMPHASIS		0xe3
+#define v_PRE_EMPHASIS(n)		((n & 7) << 4)
+#define v_CLK_PRE_DRIVER(n)		((n & 3) << 2)
+#define v_DATA_PRE_DRIVER(n)		((n & 3) << 0)
+
+#define HDMI_PHY_FEEDBACK_DIV_RATIO_LOW		0xe7
+#define v_FEEDBACK_DIV_LOW(n)			(n & 0xff)
+#define HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH	0xe8
+#define v_FEEDBACK_DIV_HIGH(n)			(n & 1)
+
+#define HDMI_PHY_PRE_DIV_RATIO		0xed
+#define v_PRE_DIV_RATIO(n)		(n & 0x1f)
+
+#define HDMI_CEC_CTRL			0xd0
+#define m_ADJUST_FOR_HISENSE		(1 << 6)
+#define m_REJECT_RX_BROADCAST		(1 << 5)
+#define m_BUSFREETIME_ENABLE		(1 << 2)
+#define m_REJECT_RX			(1 << 1)
+#define m_START_TX			(1 << 0)
+
+#define HDMI_CEC_DATA			0xd1
+#define HDMI_CEC_TX_OFFSET		0xd2
+#define HDMI_CEC_RX_OFFSET		0xd3
+#define HDMI_CEC_CLK_H			0xd4
+#define HDMI_CEC_CLK_L			0xd5
+#define HDMI_CEC_TX_LENGTH		0xd6
+#define HDMI_CEC_RX_LENGTH		0xd7
+#define HDMI_CEC_TX_INT_MASK		0xd8
+#define m_TX_DONE			(1 << 3)
+#define m_TX_NOACK			(1 << 2)
+#define m_TX_BROADCAST_REJ		(1 << 1)
+#define m_TX_BUSNOTFREE			(1 << 0)
+
+#define HDMI_CEC_RX_INT_MASK		0xd9
+#define m_RX_LA_ERR			(1 << 4)
+#define m_RX_GLITCH			(1 << 3)
+#define m_RX_DONE			(1 << 0)
+
+#define HDMI_CEC_TX_INT			0xda
+#define HDMI_CEC_RX_INT			0xdb
+#define HDMI_CEC_BUSFREETIME_L		0xdc
+#define HDMI_CEC_BUSFREETIME_H		0xdd
+#define HDMI_CEC_LOGICADDR		0xde
+
+#endif /* __INNO_HDMI_H__ */
-- 
1.9.1




From shawn.lin at rock-chips.com  Sun Jan 24 23:33:43 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Mon, 25 Jan 2016 15:33:43 +0800
Subject: [PATCH] ARM64: rk3368: add tuning clk for emmc and sdmmc
Message-ID: <1453707223-28855-1-git-send-email-shawn.lin@rock-chips.com>

Add tuning clk for emmc and sdmmc, otherwise I get
the following failure while enabling mmc-hs200-1_8v.

dwmmc_rockchip ff0f0000.dwmmc: Tuning clock (sample_clk) not defined.
mmc0: tuning execution failed
mmc0: error -5 whilst initialising MMC card

With it
dwmmc_rockchip ff0f0000.dwmmc: Successfully tuned phase to 170
mmc0: new HS200 MMC card at address 0001
mmcblk0: mmc0:0001 M8G1GC 7.28 GiB

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
---

 arch/arm64/boot/dts/rockchip/rk3368.dtsi | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/boot/dts/rockchip/rk3368.dtsi b/arch/arm64/boot/dts/rockchip/rk3368.dtsi
index 122777b..1cf3cf7 100644
--- a/arch/arm64/boot/dts/rockchip/rk3368.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3368.dtsi
@@ -231,8 +231,9 @@
 		compatible = "rockchip,rk3368-dw-mshc", "rockchip,rk3288-dw-mshc";
 		reg = <0x0 0xff0c0000 0x0 0x4000>;
 		clock-freq-min-max = <400000 150000000>;
-		clocks = <&cru HCLK_SDMMC>, <&cru SCLK_SDMMC>;
-		clock-names = "biu", "ciu";
+		clocks = <&cru HCLK_SDMMC>, <&cru SCLK_SDMMC>,
+			 <&cru SCLK_SDMMC_DRV>, <&cru SCLK_SDMMC_SAMPLE>;
+		clock-names = "biu", "ciu", "ciu-drive", "ciu-sample";
 		fifo-depth = <0x100>;
 		interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
 		status = "disabled";
@@ -256,6 +257,9 @@
 		clock-freq-min-max = <400000 150000000>;
 		clocks = <&cru HCLK_EMMC>, <&cru SCLK_EMMC>;
 		clock-names = "biu", "ciu";
+		clocks = <&cru HCLK_EMMC>, <&cru SCLK_EMMC>,
+			 <&cru SCLK_EMMC_DRV>, <&cru SCLK_EMMC_SAMPLE>;
+		clock-names = "biu", "ciu", "ciu-drive", "ciu-sample";
 		fifo-depth = <0x100>;
 		interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>;
 		status = "disabled";
-- 
2.3.7




From shawn.lin at rock-chips.com  Sun Jan 24 23:49:47 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Mon, 25 Jan 2016 15:49:47 +0800
Subject: [PATCH v2 2/2] phy: add a driver for the Rockchip SoC internal
 eMMC PHY
In-Reply-To: <1451956081-24501-1-git-send-email-shawn.lin@rock-chips.com>
References: <1451956034-24421-1-git-send-email-shawn.lin@rock-chips.com>
 <1451956081-24501-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <56A5D39B.8090008@rock-chips.com>

Hello Kishon,

Any changce to apply this patch?  :)

On 2016/1/5 9:08, Shawn Lin wrote:
> This patch to add a generic PHY driver for ROCKCHIP eMMC PHY.
> Access the PHY via registers provided by GRF (general register
> files) module.
>
> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
>
> ---
>
> Changes in v2:
> - remove phy from rockchip_emmc_phy
> - remove state from rockchip_emmc_phy
> - remove on/off from init/exit
>
>   drivers/phy/Kconfig             |   7 ++
>   drivers/phy/Makefile            |   1 +
>   drivers/phy/phy-rockchip-emmc.c | 241 ++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 249 insertions(+)
>   create mode 100644 drivers/phy/phy-rockchip-emmc.c
>
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index 03cb3ea..47d16e3 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -320,6 +320,13 @@ config PHY_ROCKCHIP_USB
>   	help
>   	  Enable this to support the Rockchip USB 2.0 PHY.
>
> +config PHY_ROCKCHIP_EMMC
> +	tristate "Rockchip EMMC PHY Driver"
> +	depends on ARCH_ROCKCHIP && OF
> +	select GENERIC_PHY
> +	help
> +	  Enable this to support the Rockchip EMMC PHY.
> +
>   config PHY_ST_SPEAR1310_MIPHY
>   	tristate "ST SPEAR1310-MIPHY driver"
>   	select GENERIC_PHY
> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> index 075db1a..ed8f175 100644
> --- a/drivers/phy/Makefile
> +++ b/drivers/phy/Makefile
> @@ -35,6 +35,7 @@ phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2)	+= phy-s5pv210-usb2.o
>   obj-$(CONFIG_PHY_EXYNOS5_USBDRD)	+= phy-exynos5-usbdrd.o
>   obj-$(CONFIG_PHY_QCOM_APQ8064_SATA)	+= phy-qcom-apq8064-sata.o
>   obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
> +obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o
>   obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA)	+= phy-qcom-ipq806x-sata.o
>   obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY)	+= phy-spear1310-miphy.o
>   obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)	+= phy-spear1340-miphy.o
> diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c
> new file mode 100644
> index 0000000..6a157c2
> --- /dev/null
> +++ b/drivers/phy/phy-rockchip-emmc.c
> @@ -0,0 +1,241 @@
> +/*
> + * Rockchip emmc PHY driver
> + *
> + * Copyright (C) 2015 Shawn Lin <shawn.lin at rock-chips.com>
> + * Copyright (C) 2015 ROCKCHIP, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +
> +/*
> + * The higher 16-bit of this register is used for write protection
> + * only if BIT(x + 16) set to 1 the BIT(x) can be written.
> + */
> +#define HIWORD_UPDATE(val, mask, shift) \
> +		((val) << (shift) | (mask) << ((shift) + 16))
> +
> +/* Register definition */
> +#define GRF_EMMCPHY_CON0	0x0
> +#define GRF_EMMCPHY_CON1	0x4
> +#define GRF_EMMCPHY_CON2	0x8
> +#define GRF_EMMCPHY_CON3	0xc
> +#define GRF_EMMCPHY_CON4	0x10
> +#define GRF_EMMCPHY_CON5	0x14
> +#define GRF_EMMCPHY_CON6	0x18
> +#define GRF_EMMCPHY_STATUS	0x20
> +
> +#define PHYCTRL_PDB_MASK	0x1
> +#define PHYCTRL_PDB_SHIFT	0x0
> +#define PHYCTRL_PDB_PWR_ON	0x1
> +#define PHYCTRL_PDB_PWR_OFF	0x0
> +#define PHYCTRL_ENDLL_MASK	0x1
> +#define PHYCTRL_ENDLL_SHIFT     0x1
> +#define PHYCTRL_ENDLL_ENABLE	0x1
> +#define PHYCTRL_ENDLL_DISABLE	0x0
> +#define PHYCTRL_CALDONE_MASK	0x1
> +#define PHYCTRL_CALDONE_SHIFT   0x6
> +#define PHYCTRL_CALDONE_DONE	0x1
> +#define PHYCTRL_CALDONE_GOING	0x0
> +#define PHYCTRL_DLLRDY_MASK	0x1
> +#define PHYCTRL_DLLRDY_SHIFT	0x5
> +#define PHYCTRL_DLLRDY_DONE	0x1
> +#define PHYCTRL_DLLRDY_GOING	0x0
> +
> +struct rockchip_emmc_phy {
> +	unsigned int	reg_offset;
> +	struct regmap	*reg_base;
> +};
> +
> +static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy,
> +				   bool on_off)
> +{
> +	unsigned int caldone;
> +	unsigned int dllrdy;
> +
> +	/*
> +	 * Keep phyctrl_pdb and phyctrl_endll low to allow
> +	 * initialization of CALIO state M/C DFFs
> +	 */
> +	regmap_write(rk_phy->reg_base,
> +		     rk_phy->reg_offset + GRF_EMMCPHY_CON6,
> +		     HIWORD_UPDATE(PHYCTRL_PDB_PWR_OFF,
> +				   PHYCTRL_PDB_MASK,
> +				   PHYCTRL_PDB_SHIFT));
> +	regmap_write(rk_phy->reg_base,
> +		     rk_phy->reg_offset + GRF_EMMCPHY_CON6,
> +		     HIWORD_UPDATE(PHYCTRL_ENDLL_DISABLE,
> +				   PHYCTRL_ENDLL_MASK,
> +				   PHYCTRL_ENDLL_SHIFT));
> +
> +	/* Already finish power_off above */
> +	if (on_off == PHYCTRL_PDB_PWR_OFF)
> +		return 0;
> +
> +	/*
> +	 * According to the user manual, calpad calibration
> +	 * cycle takes more than 2us without the minimal recommended
> +	 * value, so we may need a little margin here
> +	 */
> +	udelay(3);
> +	regmap_write(rk_phy->reg_base,
> +		     rk_phy->reg_offset + GRF_EMMCPHY_CON6,
> +		     HIWORD_UPDATE(PHYCTRL_PDB_PWR_ON,
> +				   PHYCTRL_PDB_MASK,
> +				   PHYCTRL_PDB_SHIFT));
> +
> +	/*
> +	 * According to the user manual, it asks driver to
> +	 * wait 5us for calpad busy trimming
> +	 */
> +	udelay(5);
> +	regmap_read(rk_phy->reg_base,
> +		    rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
> +		    &caldone);
> +	caldone = (caldone >> PHYCTRL_CALDONE_SHIFT) & PHYCTRL_CALDONE_MASK;
> +	if (caldone != PHYCTRL_CALDONE_DONE) {
> +		pr_err("rockchip_emmc_phy_power: caldone timeout.\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	/*
> +	 * Enable analog DLL circuits, and we need extra 10.2us
> +	 * for dll to be ready for work.
> +	 */
> +	regmap_write(rk_phy->reg_base,
> +		     rk_phy->reg_offset + GRF_EMMCPHY_CON6,
> +		     HIWORD_UPDATE(PHYCTRL_ENDLL_ENABLE,
> +				   PHYCTRL_ENDLL_MASK,
> +				   PHYCTRL_ENDLL_SHIFT));
> +	udelay(11);
> +	regmap_read(rk_phy->reg_base,
> +		    rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
> +		    &dllrdy);
> +	dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK;
> +	if (dllrdy != PHYCTRL_DLLRDY_DONE) {
> +		pr_err("rockchip_emmc_phy_power: dllrdy timeout.\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	return 0;
> +}
> +
> +static int rockchip_emmc_phy_power_off(struct phy *phy)
> +{
> +	struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
> +	int ret = 0;
> +
> +	/* Power down emmc phy analog blocks */
> +	ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_OFF);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int rockchip_emmc_phy_power_on(struct phy *phy)
> +{
> +	struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
> +	int ret = 0;
> +
> +	/* Power up emmc phy analog blocks */
> +	ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_ON);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int rockchip_emmc_phy_init(struct phy *phy)
> +{
> +	return 0;
> +}
> +
> +static int rockchip_emmc_phy_exit(struct phy *phy)
> +{
> +	return 0;
> +}
> +
> +static const struct phy_ops ops = {
> +	.init		= rockchip_emmc_phy_init,
> +	.exit		= rockchip_emmc_phy_exit,
> +	.power_on	= rockchip_emmc_phy_power_on,
> +	.power_off	= rockchip_emmc_phy_power_off,
> +	.owner		= THIS_MODULE,
> +};
> +
> +static int rockchip_emmc_phy_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct rockchip_emmc_phy *rk_phy;
> +	struct phy *generic_phy;
> +	struct phy_provider *phy_provider;
> +	struct regmap *grf;
> +	unsigned int reg_offset;
> +
> +	grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
> +	if (IS_ERR(grf)) {
> +		dev_err(dev, "Missing rockchip,grf property\n");
> +		return PTR_ERR(grf);
> +	}
> +
> +	rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL);
> +	if (!rk_phy)
> +		return -ENOMEM;
> +
> +	if (of_property_read_u32(dev->of_node, "reg", &reg_offset)) {
> +		dev_err(dev, "missing reg property in node %s\n",
> +			dev->of_node->name);
> +		return -EINVAL;
> +	}
> +
> +	rk_phy->reg_offset = reg_offset;
> +	rk_phy->reg_base = grf;
> +
> +	generic_phy = devm_phy_create(dev, dev->of_node, &ops);
> +	if (IS_ERR(generic_phy)) {
> +		dev_err(dev, "failed to create PHY\n");
> +		return PTR_ERR(generic_phy);
> +	}
> +
> +	phy_set_drvdata(generic_phy, rk_phy);
> +	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
> +
> +	return PTR_ERR_OR_ZERO(phy_provider);
> +}
> +
> +static const struct of_device_id rockchip_emmc_phy_dt_ids[] = {
> +	{ .compatible = "rockchip,rk3399-emmc-phy" },
> +	{}
> +};
> +
> +MODULE_DEVICE_TABLE(of, rockchip_emmc_phy_dt_ids);
> +
> +static struct platform_driver rockchip_emmc_driver = {
> +	.probe		= rockchip_emmc_phy_probe,
> +	.driver		= {
> +		.name	= "rockchip-emmc-phy",
> +		.of_match_table = rockchip_emmc_phy_dt_ids,
> +	},
> +};
> +
> +module_platform_driver(rockchip_emmc_driver);
> +
> +MODULE_AUTHOR("Shawn Lin <shawn.lin at rock-chips.com>");
> +MODULE_DESCRIPTION("Rockchip EMMC PHY driver");
> +MODULE_LICENSE("GPL v2");
>


-- 
Best Regards
Shawn Lin



From julia.lawall at lip6.fr  Thu Jan 14 06:50:43 2016
From: julia.lawall at lip6.fr (Julia Lawall)
Date: Thu, 14 Jan 2016 15:50:43 +0100 (CET)
Subject: [PATCH v3 3/4] i2c: rk3x: new method to caculate i2c clocks
Message-ID: <alpine.DEB.2.10.1601141548100.2509@hadrien>

It looks like it would be an infinite loop?

julia

---------- Forwarded message ----------
Date: Thu, 14 Jan 2016 22:42:19 +0800
From: kbuild test robot <fengguang.wu at intel.com>
To: kbuild at 01.org
Cc: Julia Lawall <julia.lawall at lip6.fr>
Subject: Re: [PATCH v3 3/4] i2c: rk3x: new method to caculate i2c clocks

CC: kbuild-all at 01.org
In-Reply-To: <1452774699-57455-4-git-send-email-david.wu at rock-chips.com>
TO: David Wu <david.wu at rock-chips.com>
CC: heiko at sntech.de
CC: wsa at the-dreams.de, andy.shevchenko at gmail.com, dianders at chromium.org, huangtao at rock-chips.com, zyw at rock-chips.com, cf at rock-chips.com, xjq at rock-chips.com, hl at rock-chips.com, linux-arm-kernel at lists.infradead.org, linux-rockchip at lists.infradead.org, linux-gpio at vger.kernel.org, linux-kernel at vger.kernel.org, David Wu <david.wu at rock-chips.com>

Hi David,

[auto build test WARNING on wsa/i2c/for-next]
[also build test WARNING on v4.4 next-20160114]
[cannot apply to rockchip/for-next]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]

url:    https://github.com/0day-ci/linux/commits/David-Wu/i2c-rk3x-switch-to-i2c-generic-dt-parsing/20160114-203309
base:   https://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux i2c/for-next
:::::: branch date: 2 hours ago
:::::: commit date: 2 hours ago

>> drivers/i2c/busses/i2c-rk3x.c:793:23-34: WARNING: Unsigned expression compared with zero: data_hd_cnt >= 0

git remote add linux-review https://github.com/0day-ci/linux
git remote update linux-review
git checkout 5fed4c320427dd34a909f9b408fadf297121924c
vim +793 drivers/i2c/busses/i2c-rk3x.c

5fed4c32 David Wu 2016-01-14  777  		 * for low (spend more time low).
5fed4c32 David Wu 2016-01-14  778  		 */
5fed4c32 David Wu 2016-01-14  779  		extra_div = min_total_div - min_div_for_hold;
5fed4c32 David Wu 2016-01-14  780  		extra_low_div = DIV_ROUND_UP(min_low_div * extra_div,
5fed4c32 David Wu 2016-01-14  781  					     min_div_for_hold);
5fed4c32 David Wu 2016-01-14  782
5fed4c32 David Wu 2016-01-14  783  		t_output->div_low = min_low_div + extra_low_div;
5fed4c32 David Wu 2016-01-14  784  		t_output->div_high = min_high_div + (extra_div - extra_low_div);
5fed4c32 David Wu 2016-01-14  785  	}
5fed4c32 David Wu 2016-01-14  786
5fed4c32 David Wu 2016-01-14  787  	/*
5fed4c32 David Wu 2016-01-14  788  	 * calculate sda data hold count by the rules, thd_sda_count:3
5fed4c32 David Wu 2016-01-14  789  	 * is a appropriate value to reduce calculated times.
5fed4c32 David Wu 2016-01-14  790  	 * tHD;sda  = (l * s + 1) * T
5fed4c32 David Wu 2016-01-14  791  	 * tSU;sda = ((8 - s) * l + 1) * T
5fed4c32 David Wu 2016-01-14  792  	 */
5fed4c32 David Wu 2016-01-14 @793  	for (data_hd_cnt = 3; data_hd_cnt >= 0; data_hd_cnt--) {
5fed4c32 David Wu 2016-01-14  794  		max_hold_data_ns =  DIV_ROUND_UP((data_hd_cnt
5fed4c32 David Wu 2016-01-14  795  						 * (t_output->div_low) + 1)
5fed4c32 David Wu 2016-01-14  796  						 * 1000000, clk_rate_khz);
5fed4c32 David Wu 2016-01-14  797  		min_setup_data_ns =  DIV_ROUND_UP(((8 - data_hd_cnt)
5fed4c32 David Wu 2016-01-14  798  						 * (t_output->div_low) + 1)
5fed4c32 David Wu 2016-01-14  799  						 * 1000000, clk_rate_khz);
5fed4c32 David Wu 2016-01-14  800  		if ((max_hold_data_ns < spec_max_data_hold_ns) &&
5fed4c32 David Wu 2016-01-14  801  		    (min_setup_data_ns > spec_min_data_setup_ns)) {

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation


From zhangqing at rock-chips.com  Mon Jan 25 08:55:59 2016
From: zhangqing at rock-chips.com (Zhang Qing)
Date: Mon, 25 Jan 2016 08:55:59 -0800
Subject: [PATCH v1 0/3] clk: rockchip: rk3368: fix some error for rk3368 clk
Message-ID: <1453740962-8556-1-git-send-email-zhangqing@rock-chips.com>

From: zhangqing <zhangqing at rock-chips.com>

modify edp_24m parent select bit.
enable CLK_SET_RATE_PARENT flag for spdif_8ch and i2s_2ch.

zhangqing (3):
  clk: rockchip: rk3368: fix edp_24m parent
  clk: rockchip: rk3368: enable the CLK_SET_RATE_PARENT flag for
    spdif_8ch
  clk: rockchip: rk3368: enable the CLK_SET_RATE_PARENT flag for i2s_2ch

 drivers/clk/rockchip/clk-rk3368.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

-- 
1.9.1




From zhangqing at rock-chips.com  Mon Jan 25 08:56:00 2016
From: zhangqing at rock-chips.com (Zhang Qing)
Date: Mon, 25 Jan 2016 08:56:00 -0800
Subject: [PATCH v1 1/3] clk: rockchip: rk3368: fix edp_24m parent
In-Reply-To: <1453740962-8556-1-git-send-email-zhangqing@rock-chips.com>
References: <1453740962-8556-1-git-send-email-zhangqing@rock-chips.com>
Message-ID: <1453740962-8556-2-git-send-email-zhangqing@rock-chips.com>

From: zhangqing <zhangqing at rock-chips.com>

The edp_24m parent select bit define is:
1'b0:xin24m
1'b1:1'b0(dummy)
so adapt the parent sel bit to the currect one.

Signed-off-by: zhangqing <zhangqing at rock-chips.com>
---
 drivers/clk/rockchip/clk-rk3368.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/clk/rockchip/clk-rk3368.c b/drivers/clk/rockchip/clk-rk3368.c
index be0ede5..4885e6b 100644
--- a/drivers/clk/rockchip/clk-rk3368.c
+++ b/drivers/clk/rockchip/clk-rk3368.c
@@ -121,7 +121,7 @@ PNAME(mux_i2s_2ch_p)		= { "i2s_2ch_src", "i2s_2ch_frac",
 				    "dummy", "xin12m" };
 PNAME(mux_spdif_8ch_p)		= { "spdif_8ch_pre", "spdif_8ch_frac",
 				    "ext_i2s", "xin12m" };
-PNAME(mux_edp_24m_p)		= { "dummy", "xin24m" };
+PNAME(mux_edp_24m_p)		= { "xin24m", "dummy" };
 PNAME(mux_vip_out_p)		= { "vip_src", "xin24m" };
 PNAME(mux_usbphy480m_p)		= { "usbotg_out", "xin24m" };
 PNAME(mux_hsic_usbphy480m_p)	= { "usbotg_out", "dummy" };
-- 
1.9.1




From zhangqing at rock-chips.com  Mon Jan 25 08:56:02 2016
From: zhangqing at rock-chips.com (Zhang Qing)
Date: Mon, 25 Jan 2016 08:56:02 -0800
Subject: [PATCH v1 3/3] clk: rockchip: rk3368: enable the CLK_SET_RATE_PARENT
 flag for i2s_2ch
In-Reply-To: <1453740962-8556-1-git-send-email-zhangqing@rock-chips.com>
References: <1453740962-8556-1-git-send-email-zhangqing@rock-chips.com>
Message-ID: <1453740962-8556-4-git-send-email-zhangqing@rock-chips.com>

From: zhangqing <zhangqing at rock-chips.com>

I2S_2CH set freq need to select parent and calculate parent freq.
so just mark it as the CLK_SET_RATE_PARENT flag.

Signed-off-by: zhangqing <zhangqing at rock-chips.com>
---
 drivers/clk/rockchip/clk-rk3368.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/rockchip/clk-rk3368.c b/drivers/clk/rockchip/clk-rk3368.c
index 58ef08b..5ceb536 100644
--- a/drivers/clk/rockchip/clk-rk3368.c
+++ b/drivers/clk/rockchip/clk-rk3368.c
@@ -363,7 +363,8 @@ static struct rockchip_clk_branch rk3368_clk_branches[] __initdata = {
 	COMPOSITE_FRAC(0, "i2s_2ch_frac", "i2s_2ch_src", CLK_SET_RATE_PARENT,
 			RK3368_CLKSEL_CON(54), 0,
 			RK3368_CLKGATE_CON(5), 14, GFLAGS),
-	COMPOSITE_NODIV(SCLK_I2S_2CH, "sclk_i2s_2ch", mux_i2s_2ch_p, 0,
+	COMPOSITE_NODIV(SCLK_I2S_2CH, "sclk_i2s_2ch", mux_i2s_2ch_p,
+			CLK_SET_RATE_PARENT,
 			RK3368_CLKSEL_CON(53), 8, 2, MFLAGS,
 			RK3368_CLKGATE_CON(5), 15, GFLAGS),
 
-- 
1.9.1




From zhangqing at rock-chips.com  Mon Jan 25 08:56:01 2016
From: zhangqing at rock-chips.com (Zhang Qing)
Date: Mon, 25 Jan 2016 08:56:01 -0800
Subject: [PATCH v1 2/3] clk: rockchip: rk3368: enable the CLK_SET_RATE_PARENT
 flag for spdif_8ch
In-Reply-To: <1453740962-8556-1-git-send-email-zhangqing@rock-chips.com>
References: <1453740962-8556-1-git-send-email-zhangqing@rock-chips.com>
Message-ID: <1453740962-8556-3-git-send-email-zhangqing@rock-chips.com>

From: zhangqing <zhangqing at rock-chips.com>

SPDIF_8CH set freq need to select parent and calculate parent freq.
so just mark it as the CLK_SET_RATE_PARENT flag.

Signed-off-by: zhangqing <zhangqing at rock-chips.com>
---
 drivers/clk/rockchip/clk-rk3368.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/rockchip/clk-rk3368.c b/drivers/clk/rockchip/clk-rk3368.c
index 4885e6b..58ef08b 100644
--- a/drivers/clk/rockchip/clk-rk3368.c
+++ b/drivers/clk/rockchip/clk-rk3368.c
@@ -353,7 +353,8 @@ static struct rockchip_clk_branch rk3368_clk_branches[] __initdata = {
 	COMPOSITE_FRAC(0, "spdif_8ch_frac", "spdif_8ch_src", CLK_SET_RATE_PARENT,
 			RK3368_CLKSEL_CON(32), 0,
 			RK3368_CLKGATE_CON(6), 5, GFLAGS),
-	COMPOSITE_NODIV(SCLK_SPDIF_8CH, "sclk_spdif_8ch", mux_spdif_8ch_p, 0,
+	COMPOSITE_NODIV(SCLK_SPDIF_8CH, "sclk_spdif_8ch", mux_spdif_8ch_p,
+			CLK_SET_RATE_PARENT,
 			RK3368_CLKSEL_CON(31), 8, 2, MFLAGS,
 			RK3368_CLKGATE_CON(6), 6, GFLAGS),
 	COMPOSITE(0, "i2s_2ch_src", mux_pll_src_cpll_gpll_p, 0,
-- 
1.9.1




From heiko at sntech.de  Mon Jan 25 02:14:28 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Mon, 25 Jan 2016 11:14:28 +0100
Subject: [PATCH v1 0/3] clk: rockchip: rk3368: fix some error for rk3368
 clk
In-Reply-To: <1453740962-8556-1-git-send-email-zhangqing@rock-chips.com>
References: <1453740962-8556-1-git-send-email-zhangqing@rock-chips.com>
Message-ID: <1616798.0SYvlcg7vJ@diego>

Hi,

Am Montag, 25. Januar 2016, 08:55:59 schrieb Zhang Qing:
> From: zhangqing <zhangqing at rock-chips.com>
> 
> modify edp_24m parent select bit.
> enable CLK_SET_RATE_PARENT flag for spdif_8ch and i2s_2ch.
> 
> zhangqing (3):
>   clk: rockchip: rk3368: fix edp_24m parent
>   clk: rockchip: rk3368: enable the CLK_SET_RATE_PARENT flag for
>     spdif_8ch
>   clk: rockchip: rk3368: enable the CLK_SET_RATE_PARENT flag for i2s_2ch

applied all 3 to my clk branch for 4.6.

I've modified patches 2+3 to keep the line-alingment (= move the CLK_SET_PARENT 
to the line above). In the clock trees I really like to keep the structure 
identical over all clocks (and thus ignore the 80 column limit in parts) to 
make reading the large lists easier, as now everything always is in the same 
place.


Heiko


From heiko at sntech.de  Mon Jan 25 02:32:51 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Mon, 25 Jan 2016 11:32:51 +0100
Subject: [PATCH] ARM64: rk3368: add tuning clk for emmc and sdmmc
In-Reply-To: <1453707223-28855-1-git-send-email-shawn.lin@rock-chips.com>
References: <1453707223-28855-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <8282572.0k5rjtDfQ3@diego>

Hi Shawn,

Am Montag, 25. Januar 2016, 15:33:43 schrieb Shawn Lin:
> Add tuning clk for emmc and sdmmc, otherwise I get
> the following failure while enabling mmc-hs200-1_8v.
> 
> dwmmc_rockchip ff0f0000.dwmmc: Tuning clock (sample_clk) not defined.
> mmc0: tuning execution failed
> mmc0: error -5 whilst initialising MMC card
> 
> With it
> dwmmc_rockchip ff0f0000.dwmmc: Successfully tuned phase to 170
> mmc0: new HS200 MMC card at address 0001
> mmcblk0: mmc0:0001 M8G1GC 7.28 GiB
> 
> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>

applied to my dts64 branch for 4.6

I've adapted the subject to
	arm64: dts: rockchip: add rk3368 tuning clk for emmc and sdmmc
to follow the format used there (it doesn't seem 100% consistent between socs)


>  		clock-freq-min-max = <400000 150000000>;
>  		clocks = <&cru HCLK_EMMC>, <&cru SCLK_EMMC>;
>  		clock-names = "biu", "ciu";
> +		clocks = <&cru HCLK_EMMC>, <&cru SCLK_EMMC>,
> +			 <&cru SCLK_EMMC_DRV>, <&cru SCLK_EMMC_SAMPLE>;
> +		clock-names = "biu", "ciu", "ciu-drive", "ciu-sample";
>  		fifo-depth = <0x100>;
>  		interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>;
>  		status = "disabled";

I've also removed the duplicate clock entry here ;-)


Heiko


From sjoerd.simons at collabora.co.uk  Mon Jan 25 02:47:29 2016
From: sjoerd.simons at collabora.co.uk (Sjoerd Simons)
Date: Mon, 25 Jan 2016 11:47:29 +0100
Subject: [PATCH] brcmfmac: sdio: Increase the default timeouts a bit
Message-ID: <1453718849-3508-1-git-send-email-sjoerd.simons@collabora.co.uk>

On a Radxa Rock2 board with a Ampak AP6335 (Broadcom 4339 core) it seems
the card responds very quickly most of the time, unfortunately during
initialisation it sometimes seems to take just a bit over 2 seconds to
respond.

This results intialization failing with message like:
  brcmf_c_preinit_dcmds: Retreiving cur_etheraddr failed, -52
  brcmf_bus_start: failed: -52
  brcmf_sdio_firmware_callback: dongle is not responding

Increasing the timeout to allow for a bit more headroom allows the
card to initialize reliably.

A quick search online after diagnosing/fixing this showed that Google
has a similar patch in their ChromeOS tree, so this doesn't seem
specific to the board I'm using.

Signed-off-by: Sjoerd Simons <sjoerd.simons at collabora.co.uk>

---

 drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
index dd66143..75ac4bd 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
@@ -45,8 +45,8 @@
 #include "chip.h"
 #include "firmware.h"
 
-#define DCMD_RESP_TIMEOUT	msecs_to_jiffies(2000)
-#define CTL_DONE_TIMEOUT	msecs_to_jiffies(2000)
+#define DCMD_RESP_TIMEOUT	msecs_to_jiffies(2500)
+#define CTL_DONE_TIMEOUT	msecs_to_jiffies(2500)
 
 #ifdef DEBUG
 
-- 
2.7.0



From sjoerd.simons at collabora.co.uk  Mon Jan 25 02:54:07 2016
From: sjoerd.simons at collabora.co.uk (Sjoerd Simons)
Date: Mon, 25 Jan 2016 11:54:07 +0100
Subject: [PATCH 1/2] drm/rockchip: Don't build rockchip_drm_vop as modules
In-Reply-To: <1452673328-29188-2-git-send-email-mark.yao@rock-chips.com>
References: <1452673328-29188-1-git-send-email-mark.yao@rock-chips.com>
 <1452673328-29188-2-git-send-email-mark.yao@rock-chips.com>
Message-ID: <1453719247.30130.22.camel@collabora.co.uk>

On Wed, 2016-01-13 at 16:22 +0800, Mark Yao wrote:
> rockchip_drm_vop's module init had moved to rockchip_vop_reg.c
> so no need to build rockchip_drm_vop.ko

Looks like this didn't make it for the Linux 4.5 merge window, which
unfortunately means in 4.5-rc1 DRM on rockchip is broken :/

> Signed-off-by: Mark Yao <mark.yao at rock-chips.com>
> ---
> ?drivers/gpu/drm/rockchip/Makefile |????5 ++---
> ?1 file changed, 2 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/gpu/drm/rockchip/Makefile
> b/drivers/gpu/drm/rockchip/Makefile
> index a9d380f..9e6e992 100644
> --- a/drivers/gpu/drm/rockchip/Makefile
> +++ b/drivers/gpu/drm/rockchip/Makefile
> @@ -3,9 +3,8 @@
> ?# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
> ?
> ?rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o
> rockchip_drm_fbdev.o \
> -		rockchip_drm_gem.o
> +		rockchip_drm_gem.o rockchip_drm_vop.o
> ?
> ?obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
> ?
> -obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_drm_vop.o \
> -				rockchip_vop_reg.o
> +obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_vop_reg.o

-- 
Sjoerd Simons
Collabora Ltd.


From julian.calaby at gmail.com  Mon Jan 25 03:06:35 2016
From: julian.calaby at gmail.com (Julian Calaby)
Date: Mon, 25 Jan 2016 22:06:35 +1100
Subject: [PATCH] brcmfmac: sdio: Increase the default timeouts a bit
In-Reply-To: <1453718849-3508-1-git-send-email-sjoerd.simons@collabora.co.uk>
References: <1453718849-3508-1-git-send-email-sjoerd.simons@collabora.co.uk>
Message-ID: <CAGRGNgXWE=b52Mcy8NsKBZS_4gz-TUiuFipjsuTZh1xjja=uUg@mail.gmail.com>

Hi Sjoerd,

On Mon, Jan 25, 2016 at 9:47 PM, Sjoerd Simons
<sjoerd.simons at collabora.co.uk> wrote:
> On a Radxa Rock2 board with a Ampak AP6335 (Broadcom 4339 core) it seems
> the card responds very quickly most of the time, unfortunately during
> initialisation it sometimes seems to take just a bit over 2 seconds to
> respond.
>
> This results intialization failing with message like:
>   brcmf_c_preinit_dcmds: Retreiving cur_etheraddr failed, -52
>   brcmf_bus_start: failed: -52
>   brcmf_sdio_firmware_callback: dongle is not responding
>
> Increasing the timeout to allow for a bit more headroom allows the
> card to initialize reliably.
>
> A quick search online after diagnosing/fixing this showed that Google
> has a similar patch in their ChromeOS tree, so this doesn't seem
> specific to the board I'm using.
>
> Signed-off-by: Sjoerd Simons <sjoerd.simons at collabora.co.uk>

Looks sane to me.

Reviewed-by: Julian Calaby <julian.calaby at gmail.com>

>  drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
> index dd66143..75ac4bd 100644
> --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
> +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
> @@ -45,8 +45,8 @@
>  #include "chip.h"
>  #include "firmware.h"
>
> -#define DCMD_RESP_TIMEOUT      msecs_to_jiffies(2000)
> -#define CTL_DONE_TIMEOUT       msecs_to_jiffies(2000)
> +#define DCMD_RESP_TIMEOUT      msecs_to_jiffies(2500)
> +#define CTL_DONE_TIMEOUT       msecs_to_jiffies(2500)
>
>  #ifdef DEBUG
>
> --
> 2.7.0
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html



-- 
Julian Calaby

Email: julian.calaby at gmail.com
Profile: http://www.google.com/profiles/julian.calaby/


From heiko at sntech.de  Mon Jan 25 03:09:31 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Mon, 25 Jan 2016 12:09:31 +0100
Subject: [PATCH 1/2] drm/rockchip: Don't build rockchip_drm_vop as modules
In-Reply-To: <1453719247.30130.22.camel@collabora.co.uk>
References: <1452673328-29188-1-git-send-email-mark.yao@rock-chips.com>
 <1452673328-29188-2-git-send-email-mark.yao@rock-chips.com>
 <1453719247.30130.22.camel@collabora.co.uk>
Message-ID: <4712230.210ZLu46G4@diego>

Am Montag, 25. Januar 2016, 11:54:07 schrieb Sjoerd Simons:
> On Wed, 2016-01-13 at 16:22 +0800, Mark Yao wrote:
> > rockchip_drm_vop's module init had moved to rockchip_vop_reg.c
> > so no need to build rockchip_drm_vop.ko
> 
> Looks like this didn't make it for the Linux 4.5 merge window, which
> unfortunately means in 4.5-rc1 DRM on rockchip is broken :/

but it is in the fixes pull request Mark sent to Dave on friday


From sjoerd.simons at collabora.co.uk  Mon Jan 25 03:19:26 2016
From: sjoerd.simons at collabora.co.uk (Sjoerd Simons)
Date: Mon, 25 Jan 2016 12:19:26 +0100
Subject: [PATCH] ARM: dts: rockchip: Assign RK3288 EDP_24M input centrally
Message-ID: <1453720766-13086-1-git-send-email-sjoerd.simons@collabora.co.uk>

The EDP 24M clock can be fed either by an SoC internal fixed clock or
from an external IC. Change the default parent to the internal clock in
the main rk3288 dtsi, to ensure (by default) it gets setup with a
non-orphaned clock (hardware defaults to the externa clock).

This prevents potential issues when the clock framework get support for
deferring on orphaned clocks, while specific boards can always change
the parent clock if an external input is preferred.

Signed-off-by: Sjoerd Simons <sjoerd.simons at collabora.co.uk>

---

 arch/arm/boot/dts/rk3288-veyron.dtsi | 5 -----
 arch/arm/boot/dts/rk3288.dtsi        | 3 +++
 2 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/arch/arm/boot/dts/rk3288-veyron.dtsi b/arch/arm/boot/dts/rk3288-veyron.dtsi
index 9fce91f..5e61f07 100644
--- a/arch/arm/boot/dts/rk3288-veyron.dtsi
+++ b/arch/arm/boot/dts/rk3288-veyron.dtsi
@@ -340,11 +340,6 @@
 	i2c-scl-rising-time-ns = <1000>;
 };
 
-&power {
-	assigned-clocks = <&cru SCLK_EDP_24M>;
-	assigned-clock-parents = <&xin24m>;
-};
-
 &pwm1 {
 	status = "okay";
 };
diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
index 8ac49f3..03bb747 100644
--- a/arch/arm/boot/dts/rk3288.dtsi
+++ b/arch/arm/boot/dts/rk3288.dtsi
@@ -630,6 +630,9 @@
 			#address-cells = <1>;
 			#size-cells = <0>;
 
+			assigned-clocks = <&cru SCLK_EDP_24M>;
+			assigned-clock-parents = <&xin24m>;
+
 			/*
 			 * Note: Although SCLK_* are the working clocks
 			 * of device without including on the NOC, needed for
-- 
2.7.0



From wxt at rock-chips.com  Mon Jan 25 04:38:08 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Mon, 25 Jan 2016 20:38:08 +0800
Subject: [PATCH v2] ARM: dts: rockchip: add support emac for RK3036
Message-ID: <1453725488-5279-1-git-send-email-wxt@rock-chips.com>

From: zhengxing <zhengxing at rock-chips.com>

This patch describe the emac, and we need to let mac clock under
the APLL which is able to provide the accurate 50MHz what mac_ref
need.

This patch makes the emac parent clock is DPLL instead of APLL.
since that will cause some unstable things if the cpufreq is working.

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v2:
- add the assigned clock parent for emac.
  https://github.com/rockchip-linux/u-boot/commit/d64ef6b272d84c92bd02a7925f500880633c8599
- change the emac to a better place.

 arch/arm/boot/dts/rk3036-evb.dts   | 23 ++++++++++++++++++++++
 arch/arm/boot/dts/rk3036-kylin.dts | 21 ++++++++++++++++++++
 arch/arm/boot/dts/rk3036.dtsi      | 39 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 83 insertions(+)

diff --git a/arch/arm/boot/dts/rk3036-evb.dts b/arch/arm/boot/dts/rk3036-evb.dts
index 28a0336..d7d3719 100644
--- a/arch/arm/boot/dts/rk3036-evb.dts
+++ b/arch/arm/boot/dts/rk3036-evb.dts
@@ -47,6 +47,17 @@
 	compatible = "rockchip,rk3036-evb", "rockchip,rk3036";
 };
 
+&emac {
+	pinctrl-names = "default";
+	pinctrl-0 = <&emac_xfer>, <&emac_mdio>, <&rmii_rst>;
+	phy = <&phy0>;
+	status = "okay";
+
+	phy0: ethernet-phy at 0 {
+		reg = <0>;
+	};
+};
+
 &i2c1 {
 	status = "okay";
 
@@ -62,3 +73,15 @@
 &uart2 {
 	status = "okay";
 };
+
+&pinctrl {
+	pcfg_output_high: pcfg-output-high {
+		output-high;
+	};
+
+	emac {
+		rmii_rst: rmii-rst {
+			rockchip,pins = <2 22 RK_FUNC_GPIO &pcfg_output_high>;
+		};
+	};
+};
diff --git a/arch/arm/boot/dts/rk3036-kylin.dts b/arch/arm/boot/dts/rk3036-kylin.dts
index 190f22c..0bc127b 100644
--- a/arch/arm/boot/dts/rk3036-kylin.dts
+++ b/arch/arm/boot/dts/rk3036-kylin.dts
@@ -101,6 +101,17 @@
 	status = "okay";
 };
 
+&emac {
+	pinctrl-names = "default";
+	pinctrl-0 = <&emac_xfer>, <&emac_mdio>, <&rmii_rst>;
+	phy = <&phy0>;
+	status = "okay";
+
+	phy0: ethernet-phy at 0 {
+		reg = <0>;
+	};
+};
+
 &emmc {
 	status = "okay";
 };
@@ -359,6 +370,16 @@
 };
 
 &pinctrl {
+	pcfg_output_high: pcfg-output-high {
+		output-high;
+	};
+
+	emac {
+		rmii_rst: rmii-rst {
+			rockchip,pins = <2 22 RK_FUNC_GPIO &pcfg_output_high>;
+		};
+	};
+
 	pmic {
 		pmic_int: pmic-int {
 			rockchip,pins = <2 2 RK_FUNC_GPIO &pcfg_pull_default>;
diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
index 7897449..436c77a 100644
--- a/arch/arm/boot/dts/rk3036.dtsi
+++ b/arch/arm/boot/dts/rk3036.dtsi
@@ -186,6 +186,27 @@
 		status = "disabled";
 	};
 
+	emac: ethernet at 10200000 {
+		compatible = "rockchip,rk3036-emac", "snps,arc-emac";
+		reg = <0x10200000 0x4000>;
+		interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		rockchip,grf = <&grf>;
+		clocks = <&cru HCLK_MAC>, <&cru SCLK_MACREF>, <&cru SCLK_MAC>;
+		clock-names = "hclk", "macref", "macclk";
+		/*
+		 * Fix the emac parent clock is DPLL instead of APLL.
+		 * since that will cause some unstable things if the cpufreq
+		 * is working. (e.g: the accurate 50MHz what mac_ref need)
+		 */
+		assigned-clocks = <&cru SCLK_MACPLL>;
+		assigned-clock-parents = <&cru PLL_DPLL>;
+		max-speed = <100>;
+		phy-mode = "rmii";
+		status = "disabled";
+	};
+
 	sdmmc: dwmmc at 10214000 {
 		compatible = "rockchip,rk3036-dw-mshc", "rockchip,rk3288-dw-mshc";
 		reg = <0x10214000 0x4000>;
@@ -556,6 +577,24 @@
 			};
 		};
 
+		emac {
+			emac_xfer: emac-xfer {
+				rockchip,pins = <2 10 RK_FUNC_1 &pcfg_pull_default>, /* crs_dvalid */
+						<2 13 RK_FUNC_1 &pcfg_pull_default>, /* tx_en */
+						<2 14 RK_FUNC_1 &pcfg_pull_default>, /* mac_clk */
+						<2 15 RK_FUNC_1 &pcfg_pull_default>, /* rx_err */
+						<2 16 RK_FUNC_1 &pcfg_pull_default>, /* rxd1 */
+						<2 17 RK_FUNC_1 &pcfg_pull_default>, /* rxd0 */
+						<2 18 RK_FUNC_1 &pcfg_pull_default>, /* txd1 */
+						<2 19 RK_FUNC_1 &pcfg_pull_default>; /* txd0 */
+			};
+
+			emac_mdio: emac-mdio {
+				rockchip,pins = <2 12 RK_FUNC_1 &pcfg_pull_default>, /* mac_md */
+						<2 25 RK_FUNC_1 &pcfg_pull_default>; /* mac_mdclk */
+			};
+		};
+
 		i2c0 {
 			i2c0_xfer: i2c0-xfer {
 				rockchip,pins = <0 0 RK_FUNC_1 &pcfg_pull_none>,
-- 
1.9.1



From heiko at sntech.de  Mon Jan 25 04:47:06 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Mon, 25 Jan 2016 13:47:06 +0100
Subject: [PATCH v6 0/1] Introduce Innosilicon HDMI driver on Rockchip
 platforms
In-Reply-To: <1453686197-20975-1-git-send-email-ykk@rock-chips.com>
References: <1453686197-20975-1-git-send-email-ykk@rock-chips.com>
Message-ID: <30308769.0Ysu5UYYQv@diego>

Am Montag, 25. Januar 2016, 09:43:17 schrieb Yakir Yang:
> Here are a brief introduction to Innosilicon HDMI IP:
>   - Support HDMI 1.4a, HDCP 1.2 and DVI 1.0 standard compliant transmitter
>   - Support HDMI1.4 a/b 3D function defined in HDMI 1.4 a/b spec
>   - Digital video interface supports a pixel size of 24, 30, 36, 48bits
> color depth in RGB - S/PDIF output supports PCM, Dolby Digital, DTS digital
> audio transmission (32-192kHz Fs) using IEC60958 and IEC 61937
>   - The EDID and CEC function are also supported by Innosilicon HDMI
> Transmitter Controlle

have you lost separate the devicetree-binding patch somewhere?

I don't remember it getting applied and Mark may miss it if it's only 
contained in some old series.


Heiko


From caesar.upstream at gmail.com  Mon Jan 25 04:49:36 2016
From: caesar.upstream at gmail.com (Caesar Wang)
Date: Mon, 25 Jan 2016 20:49:36 +0800
Subject: rk3188 emac stuck at high load
In-Reply-To: <SNT151-W399C6D5938D0EC43FAD0F0E7E70@phx.gbl>
References: <SNT151-W399C6D5938D0EC43FAD0F0E7E70@phx.gbl>
Message-ID: <56A619E0.7060901@gmail.com>

Hi Shuyu,

Thanks your report!  Do you have fixed this issue?
I guess that caused by the cpufreq.

if you run 'cat sys/kernel/debug/clk/clk_summary' to check the emac 
parent clock, Is it the APLL?
Maybe you can change the parent clock to fix this issue if it's.

Fox example:
uboot?
https://github.com/rockchip-linux/u-boot/commit/d64ef6b272d84c92bd02a7925f500880633c8599

kernel:
https://patchwork.kernel.org/patch/8108231/


I try to install many somethings with ubuntu os on rk3036. (that's also 
working with the emac..)

.....
Get:35 http://ports.ubuntu.com/ubuntu-ports/ wily/main 
libpython2.7-minimal armhf 2.7.10-4ubuntu1 [338 kB]
Get:36 http://ports.ubuntu.com/ubuntu-ports/ wily/main 
libpython2.7-stdlib armhf 2.7.10-4ubuntu1 [1750 kB]
Get:37 http://ports.ubuntu.com/ubuntu-ports/ wily/main libpython2.7 
armhf 2.7.10-4ubuntu1 [935 kB]
Get:38 http://ports.ubuntu.com/ubuntu-ports/ wily/main vim-runtime all 
2:7.4.712-2ubuntu4 [4984 kB]
85% [38 vim-runtime 4405 kB/4984 kB 88%] 195 kB/s 11s[  314.685641] BUG: 
Bad page state in process swapper/0 pfn:7e0
[  314.691607] page:dffcee00 count:-1 mapcount:0 mapping:  (null) index:0x0
[  314.698311] flags: 0x0()
[  314.700866] page dumped because: nonzero _count
88% [38 vim-runtime 4738 kB/4984 kB 95%] 195 kB/s 9s[  316.096013] 
skbuff: skb_over_panic: text:c04ae19c len:2878 p>
[  316.108471] ------------[ cut here ]------------
[  316.113109] kernel BUG at 
/home/wxt/work/android/brillo/kernel/net/core/skbuff.c:102!
[  316.120938] Internal error: Oops - BUG: 0 [#1] SMP ARM
[  316.126079] Modules linked in:
[  316.129166] CPU: 0 PID: 0 Comm: swapper/0 Tainted: G B           
4.4.0-rc8+ #35
[  316.136732] Hardware name: Rockchip (Device Tree)
[  316.141444] task: c0f0c0b0 ti: c0f00000 task.ti: c0f00000
[  316.146862] PC is at skb_panic+0x60/0x6c
[  316.150799] LR is at _raw_spin_unlock_irqrestore+0x1c/0x24
[  316.156291] pc : [<c093f694>]    lr : [<c0947fb0>] psr: 60000113
[  316.156291] sp : c0f01d88  ip : c0f01c08  fp : c0f01dbc
[  316.167761] r10: df718540  r9 : deabbcc0  r8 : 000003a0
[  316.172989] r7 : c0c3ba0a  r6 : debb0c00  r5 : debb0c64  r4 : debb17a2
[  316.179517] r3 : c0e933b8  r2 : 00000000  r1 : 60000113  r0 : 0000007d
...
[  316.372994] [<c093f694>] (skb_panic) from [<c07353b8>] 
(skb_put+0x54/0x60)
[  316.379888] [<c07353b8>] (skb_put) from [<c04ae19c>] 
(arc_emac_poll+0x26c/0x4bc)
[  316.387299] [<c04ae19c>] (arc_emac_poll) from [<c07456b4>] 
(net_rx_action+0xf4/0x308)
[  316.395146] [<c07456b4>] (net_rx_action) from [<c012365c>] 
(__do_softirq+0x10c/0x2d8)
[  316.402991] [<c012365c>] (__do_softirq) from [<c0123adc>] 
(irq_exit+0x94/0x104)
[  316.410313] [<c0123adc>] (irq_exit) from [<c016a778>] 
(__handle_domain_irq+0x98/0xbc)
[  316.418156] [<c016a778>] (__handle_domain_irq) from [<c010143c>] 
(gic_handle_irq+0x58/0xa0)
[  316.426514] [<c010143c>] (gic_handle_irq) from [<c010be94>] 
(__irq_svc+0x54/

? 2015?12?24? 23:52, Shuyu Wei ??:
> Hi,
> I'm testing Radxa Rock Pro board with mainline kernel 4.4.0-rc6.
> The problem can be reproduced by simultaneously wget two large files at high speed to /dev/null.
>
> $wget https://192.168.1.1/file.bin -O /dev/null -q &
> $wget https://192.168.1.1/file.bin -O /dev/null
>
> or open two sftp session to download two large files to /dev/null.
>
> Some addtional information that might help:
>      Downloading from encrypted https links or sftp server triggers the problem much easier.
>      Disabling SMP in kernel seems to fix the problem.
>      Sometimes the emac just stops sending and receiving frames, sometimes it produces kernel panics like below.
>
> [ 2191.975729] BUG: Bad page state in process swapper/0  pfn:8d960
> [ 2191.982354] page:ef463180 count:-1 mapcount:0 mapping:  (null) index:0x0
> [ 2191.989047] flags: 0x0()
> [ 2191.991594] page dumped because: nonzero _count
> [ 2191.996127] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.4.0-rc6 #114
> [ 2192.002475] Hardware name: Rockchip (Device Tree)
> [ 2192.007174] Backtrace:
> [ 2192.009658] [<c00134d4>] (dump_backtrace) from [<c0013680>] (show_stack+0x18/0x1c)
> [ 2192.017220]  r7:c051c4f8 r6:ef463180 r5:c05b7000 r4:00000000
> [ 2192.022948] [<c0013668>] (show_stack) from [<c0219d90>] (dump_stack+0x90/0xa0)
> [ 2192.030176] [<c0219d00>] (dump_stack) from [<c00b2cd4>] (bad_page+0xdc/0x12c)
> [ 2192.037302]  r5:c059a100 r4:c05f430c
> [ 2192.040913] [<c00b2bf8>] (bad_page) from [<c00b606c>] (get_page_from_freelist+0x388/0x95c)
> [ 2192.049166]  r9:00000008 r8:ef463180 r7:c051c4d0 r6:00000000 r5:00000000 r4:c051c4e4
> [ 2192.056982] [<c00b5ce4>] (get_page_from_freelist) from [<c00b6880>] (__alloc_pages_nodemask+0xd8/0x8e8)
> [ 2192.066362]  r10:c001b068 r9:00000000 r8:ee0b02b0 r7:60000113 r6:00000003 r5:02095220
> [ 2192.074254]  r4:c05ca1c0
> [ 2192.076809] [<c00b67a8>] (__alloc_pages_nodemask) from [<c00b7140>] (__alloc_page_frag+0xb0/0x160)
> [ 2192.085757]  r10:c001b068 r9:00000000 r8:ee0b02b0 r7:60000113 r6:02080020 r5:00000740
> [ 2192.093650]  r4:eedbc884
> [ 2192.096207] [<c00b7090>] (__alloc_page_frag) from [<c03273b4>] (__netdev_alloc_skb+0xa0/0x104)
> [ 2192.104806]  r7:60000113 r6:eedbc884 r5:ee0b0000 r4:00000740
> [ 2192.110525] [<c0327314>] (__netdev_alloc_skb) from [<c02aac00>] (arc_emac_poll+0x318/0x57c)
> [ 2192.118865]  r9:00000000 r8:ee0b02b0 r7:0000019c r6:ee163780 r5:00000670 r4:ee0b0000
> [ 2192.126683] [<c02aa8e8>] (arc_emac_poll) from [<c0339ed8>] (net_rx_action+0x1f0/0x2ec)
> [ 2192.134590]  r10:c0599df8 r9:c059a100 r8:00073760 r7:0000012c r6:00000028 r5:c02aa8e8
> [ 2192.142483]  r4:ee0b04e0
> [ 2192.145040] [<c0339ce8>] (net_rx_action) from [<c0026f5c>] (__do_softirq+0x134/0x258)
> [ 2192.152860]  r10:c059a080 r9:40000003 r8:00000003 r7:00000100 r6:c0598000 r5:c059a08c
> [ 2192.160751]  r4:00000000
> [ 2192.163303] [<c0026e28>] (__do_softirq) from [<c0027344>] (irq_exit+0xb8/0x120)
> [ 2192.170604]  r10:c04818a4 r9:c059a4a0 r8:ee808000 r7:00000000 r6:00000000 r5:0000001a
> [ 2192.178496]  r4:c05943bc
> [ 2192.181051] [<c002728c>] (irq_exit) from [<c00656a4>] (__handle_domain_irq+0x68/0xbc)
> [ 2192.188871]  r5:0000001a r4:c05943bc
> [ 2192.192479] [<c006563c>] (__handle_domain_irq) from [<c0009438>] (gic_handle_irq+0x40/0x78)
> [ 2192.200818]  r9:c059a4a0 r8:f0803100 r7:f0802100 r6:c0599f00 r5:f080210c r4:c059a87c
> [ 2192.208630] [<c00093f8>] (gic_handle_irq) from [<c0014214>] (__irq_svc+0x54/0x70)
> [ 2192.216105] Exception stack(0xc0599f00 to 0xc0599f48)
> [ 2192.221161] 9f00: 00000001 00000000 00000000 c001e940 c0598000 c059a454 00000000 c05ca291
> [ 2192.229336] 9f20: 00000000 c059a4a0 c04818a4 c0599f5c c0599f60 c0599f50 c0010208 c001020c
> [ 2192.237505] 9f40: 60000013 ffffffff
> [ 2192.240990]  r9:c059a4a0 r8:00000000 r7:c0599f34 r6:ffffffff r5:60000013 r4:c001020c
> [ 2192.248810] [<c00101cc>] (arch_cpu_idle) from [<c005bbec>] (default_idle_call+0x28/0x34)
> [ 2192.256899] [<c005bbc4>] (default_idle_call) from [<c005be60>] (cpu_startup_entry+0x214/0x270)
> [ 2192.265515] [<c005bc4c>] (cpu_startup_entry) from [<c047a6f4>] (rest_init+0x80/0x84)
> [ 2192.273248]  r7:ffffffff
> [ 2192.275808] [<c047a674>] (rest_init) from [<c055fca0>] (start_kernel+0x348/0x354)
> [ 2192.283288] [<c055f958>] (start_kernel) from [<60008078>] (0x60008078)
> [ 2192.289798] Disabling lock debugging due to kernel taint
> [ 2192.300779] rockchip_emac 10204000.ethernet eth0: BUG! Tx Ring full when queue awake!
>
> I'm glad to provide more information if needed.
>
> Shuyu Wei
>   		 	   		
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip


-- 
Thanks,
Caesar



From shuyu.w at outlook.com  Mon Jan 25 05:33:02 2016
From: shuyu.w at outlook.com (Shuyu Wei)
Date: Mon, 25 Jan 2016 13:33:02 +0000
Subject: rk3188 emac stuck at high load
In-Reply-To: <56A619E0.7060901@gmail.com>
References: <SNT151-W399C6D5938D0EC43FAD0F0E7E70@phx.gbl>,
 <56A619E0.7060901@gmail.com>
Message-ID: <SNT151-W396F79221B612690406E88E7C70@phx.gbl>

Hi Caesar,

Thanks for your reply, I thought I have fixed the issue by this patch:
http://lists.infradead.org/pipermail/linux-rockchip/2016-January/006412.html
Maybe I sent it to the wrong maintainer so it didn't get noticed.
I'll try your solution and report as soon as possible. 
----------------------------------------
> Subject: Re: rk3188 emac stuck at high load
> To: shuyu.w at outlook.com
> CC: linux-rockchip at lists.infradead.org; heiko at sntech.de
> From: caesar.upstream at gmail.com
> Date: Mon, 25 Jan 2016 20:49:36 +0800
>
> Hi Shuyu,
>
> Thanks your report! Do you have fixed this issue?
> I guess that caused by the cpufreq.
>
> if you run 'cat sys/kernel/debug/clk/clk_summary' to check the emac
> parent clock, Is it the APLL?
> Maybe you can change the parent clock to fix this issue if it's.
>
> Fox example:
> uboot?
> https://github.com/rockchip-linux/u-boot/commit/d64ef6b272d84c92bd02a7925f500880633c8599
>
> kernel:
> https://patchwork.kernel.org/patch/8108231/
>
>
> I try to install many somethings with ubuntu os on rk3036. (that's also
> working with the emac..)
>
> .....
> Get:35 http://ports.ubuntu.com/ubuntu-ports/ wily/main
> libpython2.7-minimal armhf 2.7.10-4ubuntu1 [338 kB]
> Get:36 http://ports.ubuntu.com/ubuntu-ports/ wily/main
> libpython2.7-stdlib armhf 2.7.10-4ubuntu1 [1750 kB]
> Get:37 http://ports.ubuntu.com/ubuntu-ports/ wily/main libpython2.7
> armhf 2.7.10-4ubuntu1 [935 kB]
> Get:38 http://ports.ubuntu.com/ubuntu-ports/ wily/main vim-runtime all
> 2:7.4.712-2ubuntu4 [4984 kB]
> 85% [38 vim-runtime 4405 kB/4984 kB 88%] 195 kB/s 11s[ 314.685641] BUG:
> Bad page state in process swapper/0 pfn:7e0
> [ 314.691607] page:dffcee00 count:-1 mapcount:0 mapping: (null) index:0x0
> [ 314.698311] flags: 0x0()
> [ 314.700866] page dumped because: nonzero _count
> 88% [38 vim-runtime 4738 kB/4984 kB 95%] 195 kB/s 9s[ 316.096013]
> skbuff: skb_over_panic: text:c04ae19c len:2878 p>
> [ 316.108471] ------------[ cut here ]------------
> [ 316.113109] kernel BUG at
> /home/wxt/work/android/brillo/kernel/net/core/skbuff.c:102!
> [ 316.120938] Internal error: Oops - BUG: 0 [#1] SMP ARM
> [ 316.126079] Modules linked in:
> [ 316.129166] CPU: 0 PID: 0 Comm: swapper/0 Tainted: G B
> 4.4.0-rc8+ #35
> [ 316.136732] Hardware name: Rockchip (Device Tree)
> [ 316.141444] task: c0f0c0b0 ti: c0f00000 task.ti: c0f00000
> [ 316.146862] PC is at skb_panic+0x60/0x6c
> [ 316.150799] LR is at _raw_spin_unlock_irqrestore+0x1c/0x24
> [ 316.156291] pc : [<c093f694>] lr : [<c0947fb0>] psr: 60000113
> [ 316.156291] sp : c0f01d88 ip : c0f01c08 fp : c0f01dbc
> [ 316.167761] r10: df718540 r9 : deabbcc0 r8 : 000003a0
> [ 316.172989] r7 : c0c3ba0a r6 : debb0c00 r5 : debb0c64 r4 : debb17a2
> [ 316.179517] r3 : c0e933b8 r2 : 00000000 r1 : 60000113 r0 : 0000007d
> ...
> [ 316.372994] [<c093f694>] (skb_panic) from [<c07353b8>]
> (skb_put+0x54/0x60)
> [ 316.379888] [<c07353b8>] (skb_put) from [<c04ae19c>]
> (arc_emac_poll+0x26c/0x4bc)
> [ 316.387299] [<c04ae19c>] (arc_emac_poll) from [<c07456b4>]
> (net_rx_action+0xf4/0x308)
> [ 316.395146] [<c07456b4>] (net_rx_action) from [<c012365c>]
> (__do_softirq+0x10c/0x2d8)
> [ 316.402991] [<c012365c>] (__do_softirq) from [<c0123adc>]
> (irq_exit+0x94/0x104)
> [ 316.410313] [<c0123adc>] (irq_exit) from [<c016a778>]
> (__handle_domain_irq+0x98/0xbc)
> [ 316.418156] [<c016a778>] (__handle_domain_irq) from [<c010143c>]
> (gic_handle_irq+0x58/0xa0)
> [ 316.426514] [<c010143c>] (gic_handle_irq) from [<c010be94>]
> (__irq_svc+0x54/
>
> ? 2015?12?24? 23:52, Shuyu Wei ??:
>> Hi,
>> I'm testing Radxa Rock Pro board with mainline kernel 4.4.0-rc6.
>> The problem can be reproduced by simultaneously wget two large files at high speed to /dev/null.
>>
>> $wget https://192.168.1.1/file.bin -O /dev/null -q &
>> $wget https://192.168.1.1/file.bin -O /dev/null
>>
>> or open two sftp session to download two large files to /dev/null.
>>
>> Some addtional information that might help:
>> Downloading from encrypted https links or sftp server triggers the problem much easier.
>> Disabling SMP in kernel seems to fix the problem.
>> Sometimes the emac just stops sending and receiving frames, sometimes it produces kernel panics like below.
>>
>> [ 2191.975729] BUG: Bad page state in process swapper/0 pfn:8d960
>> [ 2191.982354] page:ef463180 count:-1 mapcount:0 mapping: (null) index:0x0
>> [ 2191.989047] flags: 0x0()
>> [ 2191.991594] page dumped because: nonzero _count
>> [ 2191.996127] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.4.0-rc6 #114
>> [ 2192.002475] Hardware name: Rockchip (Device Tree)
>> [ 2192.007174] Backtrace:
>> [ 2192.009658] [<c00134d4>] (dump_backtrace) from [<c0013680>] (show_stack+0x18/0x1c)
>> [ 2192.017220] r7:c051c4f8 r6:ef463180 r5:c05b7000 r4:00000000
>> [ 2192.022948] [<c0013668>] (show_stack) from [<c0219d90>] (dump_stack+0x90/0xa0)
>> [ 2192.030176] [<c0219d00>] (dump_stack) from [<c00b2cd4>] (bad_page+0xdc/0x12c)
>> [ 2192.037302] r5:c059a100 r4:c05f430c
>> [ 2192.040913] [<c00b2bf8>] (bad_page) from [<c00b606c>] (get_page_from_freelist+0x388/0x95c)
>> [ 2192.049166] r9:00000008 r8:ef463180 r7:c051c4d0 r6:00000000 r5:00000000 r4:c051c4e4
>> [ 2192.056982] [<c00b5ce4>] (get_page_from_freelist) from [<c00b6880>] (__alloc_pages_nodemask+0xd8/0x8e8)
>> [ 2192.066362] r10:c001b068 r9:00000000 r8:ee0b02b0 r7:60000113 r6:00000003 r5:02095220
>> [ 2192.074254] r4:c05ca1c0
>> [ 2192.076809] [<c00b67a8>] (__alloc_pages_nodemask) from [<c00b7140>] (__alloc_page_frag+0xb0/0x160)
>> [ 2192.085757] r10:c001b068 r9:00000000 r8:ee0b02b0 r7:60000113 r6:02080020 r5:00000740
>> [ 2192.093650] r4:eedbc884
>> [ 2192.096207] [<c00b7090>] (__alloc_page_frag) from [<c03273b4>] (__netdev_alloc_skb+0xa0/0x104)
>> [ 2192.104806] r7:60000113 r6:eedbc884 r5:ee0b0000 r4:00000740
>> [ 2192.110525] [<c0327314>] (__netdev_alloc_skb) from [<c02aac00>] (arc_emac_poll+0x318/0x57c)
>> [ 2192.118865] r9:00000000 r8:ee0b02b0 r7:0000019c r6:ee163780 r5:00000670 r4:ee0b0000
>> [ 2192.126683] [<c02aa8e8>] (arc_emac_poll) from [<c0339ed8>] (net_rx_action+0x1f0/0x2ec)
>> [ 2192.134590] r10:c0599df8 r9:c059a100 r8:00073760 r7:0000012c r6:00000028 r5:c02aa8e8
>> [ 2192.142483] r4:ee0b04e0
>> [ 2192.145040] [<c0339ce8>] (net_rx_action) from [<c0026f5c>] (__do_softirq+0x134/0x258)
>> [ 2192.152860] r10:c059a080 r9:40000003 r8:00000003 r7:00000100 r6:c0598000 r5:c059a08c
>> [ 2192.160751] r4:00000000
>> [ 2192.163303] [<c0026e28>] (__do_softirq) from [<c0027344>] (irq_exit+0xb8/0x120)
>> [ 2192.170604] r10:c04818a4 r9:c059a4a0 r8:ee808000 r7:00000000 r6:00000000 r5:0000001a
>> [ 2192.178496] r4:c05943bc
>> [ 2192.181051] [<c002728c>] (irq_exit) from [<c00656a4>] (__handle_domain_irq+0x68/0xbc)
>> [ 2192.188871] r5:0000001a r4:c05943bc
>> [ 2192.192479] [<c006563c>] (__handle_domain_irq) from [<c0009438>] (gic_handle_irq+0x40/0x78)
>> [ 2192.200818] r9:c059a4a0 r8:f0803100 r7:f0802100 r6:c0599f00 r5:f080210c r4:c059a87c
>> [ 2192.208630] [<c00093f8>] (gic_handle_irq) from [<c0014214>] (__irq_svc+0x54/0x70)
>> [ 2192.216105] Exception stack(0xc0599f00 to 0xc0599f48)
>> [ 2192.221161] 9f00: 00000001 00000000 00000000 c001e940 c0598000 c059a454 00000000 c05ca291
>> [ 2192.229336] 9f20: 00000000 c059a4a0 c04818a4 c0599f5c c0599f60 c0599f50 c0010208 c001020c
>> [ 2192.237505] 9f40: 60000013 ffffffff
>> [ 2192.240990] r9:c059a4a0 r8:00000000 r7:c0599f34 r6:ffffffff r5:60000013 r4:c001020c
>> [ 2192.248810] [<c00101cc>] (arch_cpu_idle) from [<c005bbec>] (default_idle_call+0x28/0x34)
>> [ 2192.256899] [<c005bbc4>] (default_idle_call) from [<c005be60>] (cpu_startup_entry+0x214/0x270)
>> [ 2192.265515] [<c005bc4c>] (cpu_startup_entry) from [<c047a6f4>] (rest_init+0x80/0x84)
>> [ 2192.273248] r7:ffffffff
>> [ 2192.275808] [<c047a674>] (rest_init) from [<c055fca0>] (start_kernel+0x348/0x354)
>> [ 2192.283288] [<c055f958>] (start_kernel) from [<60008078>] (0x60008078)
>> [ 2192.289798] Disabling lock debugging due to kernel taint
>> [ 2192.300779] rockchip_emac 10204000.ethernet eth0: BUG! Tx Ring full when queue awake!
>>
>> I'm glad to provide more information if needed.
>>
>> Shuyu Wei
>>
>> _______________________________________________
>> Linux-rockchip mailing list
>> Linux-rockchip at lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-rockchip
>
>
> --
> Thanks,
> Caesar
>
 		 	   		  

From heiko at sntech.de  Mon Jan 25 05:50:41 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Mon, 25 Jan 2016 14:50:41 +0100
Subject: [PATCH] ARM: dts: rockchip: Assign RK3288 EDP_24M input centrally
In-Reply-To: <1453720766-13086-1-git-send-email-sjoerd.simons@collabora.co.uk>
References: <1453720766-13086-1-git-send-email-sjoerd.simons@collabora.co.uk>
Message-ID: <9462615.JqcFZ6IZ4a@diego>

Am Montag, 25. Januar 2016, 12:19:26 schrieb Sjoerd Simons:
> The EDP 24M clock can be fed either by an SoC internal fixed clock or
> from an external IC. Change the default parent to the internal clock in
> the main rk3288 dtsi, to ensure (by default) it gets setup with a
> non-orphaned clock (hardware defaults to the externa clock).
> 
> This prevents potential issues when the clock framework get support for
> deferring on orphaned clocks, while specific boards can always change
> the parent clock if an external input is preferred.
> 
> Signed-off-by: Sjoerd Simons <sjoerd.simons at collabora.co.uk>

applied to dts32 for 4.6


From heiko at sntech.de  Mon Jan 25 05:51:59 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Mon, 25 Jan 2016 14:51:59 +0100
Subject: [PATCH v2 3/4] arm64: Enable the timer on Rockchip architecture
In-Reply-To: <1443147298-12603-4-git-send-email-wxt@rock-chips.com>
References: <1443147298-12603-1-git-send-email-wxt@rock-chips.com>
 <1443147298-12603-4-git-send-email-wxt@rock-chips.com>
Message-ID: <27979155.GUPQX20Gsc@diego>

Am Freitag, 25. September 2015, 10:14:57 schrieb Caesar Wang:
> On the RK3368 SoC, support the APB timers for rockchip platform.
> 
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>

applied to my soc64 branch for 4.6


Heiko


From heiko at sntech.de  Mon Jan 25 06:04:31 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Mon, 25 Jan 2016 15:04:31 +0100
Subject: [PATCH v3 7/8] clk: rockchip: fix usbphy-related clocks
In-Reply-To: <1447968149-10979-8-git-send-email-heiko@sntech.de>
References: <1447968149-10979-1-git-send-email-heiko@sntech.de>
 <1447968149-10979-8-git-send-email-heiko@sntech.de>
Message-ID: <4016576.NEsgSPjyJ9@diego>

Kishon,

Am Donnerstag, 19. November 2015, 22:22:28 schrieb Heiko Stuebner:
> The otgphy clocks really only drive the phy blocks. These in turn
> contain plls that then generate the 480m clocks the clock controller
> uses to supply some other clocks like uart0, gpu or the video-codec.
> 
> So fix this structure to actually respect that hirarchy and removed
> that usb480m fixed-rate clock working as a placeholder till now, as
> this wouldn't even work if the supplying phy gets turned off while
> its pll-output gets used elsewhere.
> 
> Signed-off-by: Heiko Stuebner <heiko at sntech.de>
> Reviewed-by: Douglas Anderson <dianders at chromium.org>

it looks like this patch didn't make your cutoff time for sending your stuff 
to Greg. As the core phy series up to patch 5 is in mainline now, I've just 
applied this patch to my clk-branch for 4.6.


Heiko


From heiko at sntech.de  Mon Jan 25 06:06:57 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Mon, 25 Jan 2016 15:06:57 +0100
Subject: [PATCH v3 6/8] ARM: dts: rockchip: add clock-cells for usb phy
 nodes
In-Reply-To: <1447968149-10979-7-git-send-email-heiko@sntech.de>
References: <1447968149-10979-1-git-send-email-heiko@sntech.de>
 <1447968149-10979-7-git-send-email-heiko@sntech.de>
Message-ID: <2533572.i0vRkUzAgm@diego>

Am Donnerstag, 19. November 2015, 22:22:27 schrieb Heiko Stuebner:
> Add the #clock-cells properties for the usbphy nodes as they
> provide the pll-clocks now.
> 
> Signed-off-by: Heiko Stuebner <heiko at sntech.de>
> Reviewed-by: Douglas Anderson <dianders at chromium.org>

applied to my dts32 branch for 4.6 with Mike's Review-tag from v1


From aspriel at gmail.com  Mon Jan 25 07:36:52 2016
From: aspriel at gmail.com (Arend van Spriel)
Date: Mon, 25 Jan 2016 16:36:52 +0100
Subject: [PATCH] brcmfmac: sdio: Increase the default timeouts a bit
In-Reply-To: <1453718849-3508-1-git-send-email-sjoerd.simons@collabora.co.uk>
References: <1453718849-3508-1-git-send-email-sjoerd.simons@collabora.co.uk>
Message-ID: <56A64114.3010400@gmail.com>

On 25-01-16 11:47, Sjoerd Simons wrote:
> On a Radxa Rock2 board with a Ampak AP6335 (Broadcom 4339 core) it seems
> the card responds very quickly most of the time, unfortunately during
> initialisation it sometimes seems to take just a bit over 2 seconds to
> respond.
> 
> This results intialization failing with message like:
>   brcmf_c_preinit_dcmds: Retreiving cur_etheraddr failed, -52
>   brcmf_bus_start: failed: -52
>   brcmf_sdio_firmware_callback: dongle is not responding
> 
> Increasing the timeout to allow for a bit more headroom allows the
> card to initialize reliably.

I would prefer to know where the 2 second response time comes from.
Could be sdio retuning. Maybe the chromeos people can comment whether
this has been root caused.

There is a mmc patch pending in which retuning procedure can be deferred
by the driver. Using that API may resolve the issue as well and I would
prefer that solution.

> A quick search online after diagnosing/fixing this showed that Google
> has a similar patch in their ChromeOS tree, so this doesn't seem
> specific to the board I'm using.

As the retuning stuff is not in main line I guess we need this fix for
now so...

Acked-by: Arend van Spriel <arend at broadcom.com>
> Signed-off-by: Sjoerd Simons <sjoerd.simons at collabora.co.uk>
> 
> ---
Still would like to know whether it is firmware initialization or some
mmc stack procedure. Any suggestions to debug this are welcome.

Regards,
Arend
---
> 
>  drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
> index dd66143..75ac4bd 100644
> --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
> +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
> @@ -45,8 +45,8 @@
>  #include "chip.h"
>  #include "firmware.h"
>  
> -#define DCMD_RESP_TIMEOUT	msecs_to_jiffies(2000)
> -#define CTL_DONE_TIMEOUT	msecs_to_jiffies(2000)
> +#define DCMD_RESP_TIMEOUT	msecs_to_jiffies(2500)
> +#define CTL_DONE_TIMEOUT	msecs_to_jiffies(2500)
>  
>  #ifdef DEBUG
>  
> 


From aspriel at gmail.com  Mon Jan 25 07:39:56 2016
From: aspriel at gmail.com (Arend van Spriel)
Date: Mon, 25 Jan 2016 16:39:56 +0100
Subject: [PATCH] brcmfmac: sdio: Increase the default timeouts a bit
In-Reply-To: <CAGRGNgXWE=b52Mcy8NsKBZS_4gz-TUiuFipjsuTZh1xjja=uUg@mail.gmail.com>
References: <1453718849-3508-1-git-send-email-sjoerd.simons@collabora.co.uk>
 <CAGRGNgXWE=b52Mcy8NsKBZS_4gz-TUiuFipjsuTZh1xjja=uUg@mail.gmail.com>
Message-ID: <56A641CC.6020500@gmail.com>

On 25-01-16 12:06, Julian Calaby wrote:
> Hi Sjoerd,
> 
> On Mon, Jan 25, 2016 at 9:47 PM, Sjoerd Simons
> <sjoerd.simons at collabora.co.uk> wrote:
>> On a Radxa Rock2 board with a Ampak AP6335 (Broadcom 4339 core) it seems
>> the card responds very quickly most of the time, unfortunately during
>> initialisation it sometimes seems to take just a bit over 2 seconds to
>> respond.
>>
>> This results intialization failing with message like:
>>   brcmf_c_preinit_dcmds: Retreiving cur_etheraddr failed, -52
>>   brcmf_bus_start: failed: -52
>>   brcmf_sdio_firmware_callback: dongle is not responding
>>
>> Increasing the timeout to allow for a bit more headroom allows the
>> card to initialize reliably.
>>
>> A quick search online after diagnosing/fixing this showed that Google
>> has a similar patch in their ChromeOS tree, so this doesn't seem
>> specific to the board I'm using.
>>
>> Signed-off-by: Sjoerd Simons <sjoerd.simons at collabora.co.uk>
> 
> Looks sane to me.
> 
> Reviewed-by: Julian Calaby <julian.calaby at gmail.com>

Not really a cleanup patch :-p , but thanks for the review.

Regards,
Arend

> 
>>  drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 4 ++--
>>  1 file changed, 2 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
>> index dd66143..75ac4bd 100644
>> --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
>> +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
>> @@ -45,8 +45,8 @@
>>  #include "chip.h"
>>  #include "firmware.h"
>>
>> -#define DCMD_RESP_TIMEOUT      msecs_to_jiffies(2000)
>> -#define CTL_DONE_TIMEOUT       msecs_to_jiffies(2000)
>> +#define DCMD_RESP_TIMEOUT      msecs_to_jiffies(2500)
>> +#define CTL_DONE_TIMEOUT       msecs_to_jiffies(2500)
>>
>>  #ifdef DEBUG
>>
>> --
>> 2.7.0
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
>> the body of a message to majordomo at vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
> 
> 


From sjoerd.simons at collabora.co.uk  Mon Jan 25 07:55:13 2016
From: sjoerd.simons at collabora.co.uk (Sjoerd Simons)
Date: Mon, 25 Jan 2016 16:55:13 +0100
Subject: [PATCH] brcmfmac: sdio: Increase the default timeouts a bit
In-Reply-To: <56A64114.3010400@gmail.com>
References: <1453718849-3508-1-git-send-email-sjoerd.simons@collabora.co.uk>
 <56A64114.3010400@gmail.com>
Message-ID: <1453737313.30130.39.camel@collabora.co.uk>

On Mon, 2016-01-25 at 16:36 +0100, Arend van Spriel wrote:
> On 25-01-16 11:47, Sjoerd Simons wrote:
> > On a Radxa Rock2 board with a Ampak AP6335 (Broadcom 4339 core) it
> > seems
> > the card responds very quickly most of the time, unfortunately
> > during
> > initialisation it sometimes seems to take just a bit over 2 seconds
> > to
> > respond.
> > 
> > This results intialization failing with message like:
> > ? brcmf_c_preinit_dcmds: Retreiving cur_etheraddr failed, -52
> > ? brcmf_bus_start: failed: -52
> > ? brcmf_sdio_firmware_callback: dongle is not responding
> > 
> > Increasing the timeout to allow for a bit more headroom allows the
> > card to initialize reliably.
> 
> I would prefer to know where the 2 second response time comes from.
> Could be sdio retuning. Maybe the chromeos people can comment whether
> this has been root caused.

The only reference i could find for where the timeout came from was in
the bcmdhd which has:
??#define IOCTL_RESP_TIMEOUT??2000??/* In milli second default value
for Production FW */

But not sure if that's helpful :).?


> There is a mmc patch pending in which retuning procedure can be
> deferred
> by the driver. Using that API may resolve the issue as well and I
> would
> prefer that solution.
> 
> > A quick search online after diagnosing/fixing this showed that
> > Google
> > has a similar patch in their ChromeOS tree, so this doesn't seem
> > specific to the board I'm using.
> 
> As the retuning stuff is not in main line I guess we need this fix
> for
> now so...
> 
> Acked-by: Arend van Spriel <arend at broadcom.com>
> > Signed-off-by: Sjoerd Simons <sjoerd.simons at collabora.co.uk>
> > 
> > ---
> Still would like to know whether it is firmware initialization or
> some
> mmc stack procedure. Any suggestions to debug this are welcome.
> 
> Regards,
> Arend
> ---
> > 
> > ?drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 4 ++--
> > ?1 file changed, 2 insertions(+), 2 deletions(-)
> > 
> > diff --git
> > a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
> > b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
> > index dd66143..75ac4bd 100644
> > --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
> > +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
> > @@ -45,8 +45,8 @@
> > ?#include "chip.h"
> > ?#include "firmware.h"
> > ?
> > -#define DCMD_RESP_TIMEOUT	msecs_to_jiffies(2000)
> > -#define CTL_DONE_TIMEOUT	msecs_to_jiffies(2000)
> > +#define DCMD_RESP_TIMEOUT	msecs_to_jiffies(2500)
> > +#define CTL_DONE_TIMEOUT	msecs_to_jiffies(2500)
> > ?
> > ?#ifdef DEBUG
> > ?
> > 

-- 
Sjoerd Simons
Collabora Ltd.


From caesar.upstream at gmail.com  Mon Jan 25 08:06:27 2016
From: caesar.upstream at gmail.com (Caesar Wang)
Date: Tue, 26 Jan 2016 00:06:27 +0800
Subject: [PATCH v2] ARM: dts: rockchip: add support emac for RK3036
In-Reply-To: <1453725488-5279-1-git-send-email-wxt@rock-chips.com>
References: <1453725488-5279-1-git-send-email-wxt@rock-chips.com>
Message-ID: <56A64803.8070006@gmail.com>



? 2016?01?25? 20:38, Caesar Wang ??:
> From: zhengxing <zhengxing at rock-chips.com>
>
> This patch describe the emac, and we need to let mac clock under
> the APLL which is able to provide the accurate 50MHz what mac_ref
> need.
>
> This patch makes the emac parent clock is DPLL instead of APLL.
> since that will cause some unstable things if the cpufreq is working.
>
> Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
>
> ---
[...]
> diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
> index 7897449..436c77a 100644
> --- a/arch/arm/boot/dts/rk3036.dtsi
> +++ b/arch/arm/boot/dts/rk3036.dtsi
> @@ -186,6 +186,27 @@
>   		status = "disabled";
>   	};
>   
> +	emac: ethernet at 10200000 {
> +		compatible = "rockchip,rk3036-emac", "snps,arc-emac";
> +		reg = <0x10200000 0x4000>;
> +		interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		rockchip,grf = <&grf>;
> +		clocks = <&cru HCLK_MAC>, <&cru SCLK_MACREF>, <&cru SCLK_MAC>;
> +		clock-names = "hclk", "macref", "macclk";
> +		/*
> +		 * Fix the emac parent clock is DPLL instead of APLL.
> +		 * since that will cause some unstable things if the cpufreq
> +		 * is working. (e.g: the accurate 50MHz what mac_ref need)
> +		 */
> +		assigned-clocks = <&cru SCLK_MACPLL>;
> +		assigned-clock-parents = <&cru PLL_DPLL>;

This patch depends on the clock patch. :-( ?still in my work branch)

> +		max-speed = <100>;
> +		phy-mode = "rmii";
> +		status = "disabled";
> +	};
> +
>   	sdmmc: dwmmc at 10214000 {
>   		compatible = "rockchip,rk3036-dw-mshc", "rockchip,rk3288-dw-mshc";
>   		reg = <0x10214000 0x4000>;
> @@ -556,6 +577,24 @@
>   			};
>   		};
>   
> +		emac {
> +			emac_xfer: emac-xfer {
> +				rockchip,pins = <2 10 RK_FUNC_1 &pcfg_pull_default>, /* crs_dvalid */
> +						<2 13 RK_FUNC_1 &pcfg_pull_default>, /* tx_en */
> +						<2 14 RK_FUNC_1 &pcfg_pull_default>, /* mac_clk */
> +						<2 15 RK_FUNC_1 &pcfg_pull_default>, /* rx_err */
> +						<2 16 RK_FUNC_1 &pcfg_pull_default>, /* rxd1 */
> +						<2 17 RK_FUNC_1 &pcfg_pull_default>, /* rxd0 */
> +						<2 18 RK_FUNC_1 &pcfg_pull_default>, /* txd1 */
> +						<2 19 RK_FUNC_1 &pcfg_pull_default>; /* txd0 */
> +			};
> +
> +			emac_mdio: emac-mdio {
> +				rockchip,pins = <2 12 RK_FUNC_1 &pcfg_pull_default>, /* mac_md */
> +						<2 25 RK_FUNC_1 &pcfg_pull_default>; /* mac_mdclk */
> +			};
> +		};
> +
>   		i2c0 {
>   			i2c0_xfer: i2c0-xfer {
>   				rockchip,pins = <0 0 RK_FUNC_1 &pcfg_pull_none>,

-- 
Thanks,
Caesar



From dianders at google.com  Mon Jan 25 08:28:31 2016
From: dianders at google.com (Doug Anderson)
Date: Mon, 25 Jan 2016 08:28:31 -0800
Subject: [PATCH v3 00/12] pwm: add support for atomic update
In-Reply-To: <20151110173416.GB21727@ulmo>
References: <1442828009-6241-1-git-send-email-boris.brezillon@free-electrons.com>
 <2341981.a79ioYM9Es@diego> <20151110173416.GB21727@ulmo>
Message-ID: <CAD=FV=WL3jYEgVe-m6QcAyQwfJgdX4J5OtKisKwyoKOrTSpEQA@mail.gmail.com>

Thierry and Boris,

On Tue, Nov 10, 2015 at 9:34 AM, Thierry Reding
<thierry.reding at gmail.com> wrote:
> On Mon, Oct 19, 2015 at 12:12:12PM +0200, Heiko St?bner wrote:
>> Hi Thierry,
>>
>> Am Montag, 21. September 2015, 11:33:17 schrieb Boris Brezillon:
>> > Hello,
>> >
>> > This series adds support for atomic PWM update, or IOW, the capability
>> > to update all the parameters of a PWM device (enabled/disabled, period,
>> > duty and polarity) in one go.
>>
>> is anything more blocking this series? It's now sitting on the lists for
>> nearly a month and everybody seems happy with it, so it would be really nice
>> to have in mainline :-) .
>>
>> Especially as this also makes it possible for Rockchip Chromebooks to actually
>> control the logic-regulator that is implemented as pwm-regulator there.
>
> Last time I tried to put this into linux-next I got immediately
> bombarded by a number of build failures, so I backed things out. The
> current plan is to give this another try after v4.4-rc1.

We're now into the 4.5 timeframe.  Does anyone have a concrete set of
things that need to happen before this patch series makes it into
mainline?

>From searching I see that the latest version of this series is v4 and
there are a smattering of comments on the 24-patch series.  Presumably
a v5 needs to be posted to address those things.

...but it looks like the big sticking point is that Boris is waiting
for a response to his questions in
<https://patchwork.kernel.org/patch/7622881/>.  Thierry: can you give
Boris some direction for what else he needs to do?  We need to come up
with _some_ solution since this series gets us much better support for
PWM regulators.  Without this series or some other solution, PWM
regulators aren't usable in mainline on any system that uses them for
system-critical rails.  Nearly all Rockchip reference boards and
shipping devices uses a PWM regulator for the system-critidal "logic"
rail.  That means any patches which need to change this rail in Linux
are blocked.

If there's already been off-list discussion and Boris already knows
what the next steps are then my apologies and I'll wait patiently for
the next series.  ;)

Thanks!

-Doug


From trini at konsulko.com  Mon Jan 25 08:57:32 2016
From: trini at konsulko.com (Tom Rini)
Date: Mon, 25 Jan 2016 11:57:32 -0500
Subject: [U-Boot] [PATCH v4 5/6] rockchip: kylin: Enable boot with
 android boot image
In-Reply-To: <1452872541.3905.21.camel@gmail.com>
References: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
 <1452675200-15941-6-git-send-email-jeffy.chen@rock-chips.com>
 <20160113152811.GC3359@bill-the-cat>
 <56970886.2040009@rock-chips.com>
 <20160114162227.GY3359@bill-the-cat>
 <569842F2.4080808@rock-chips.com>
 <20160115005950.GC3359@bill-the-cat>
 <5698577B.8040209@rock-chips.com>
 <20160115144224.GR3359@bill-the-cat>
 <1452872541.3905.21.camel@gmail.com>
Message-ID: <20160125165732.GL3359@bill-the-cat>

On Fri, Jan 15, 2016 at 04:42:21PM +0100, Daniel Schwierzeck wrote:
> Am Freitag, den 15.01.2016, 09:42 -0500 schrieb Tom Rini:
> > On Fri, Jan 15, 2016 at 10:20:43AM +0800, Jeffy Chen wrote:
> > > Hi Tom,
> > > 
> > > On 2016-1-15 8:59, Tom Rini wrote:
> > > > On Fri, Jan 15, 2016 at 08:53:06AM +0800, Jeffy Chen wrote:
> > > > > Hi Tom,
> > > > > 
> > > > > On 2016-1-15 0:22, Tom Rini wrote:
> > > > > > On Thu, Jan 14, 2016 at 10:31:34AM +0800, Jeffy Chen wrote:
> > > > > > > Hi Tom,
> > > > > > > 
> > > > > > > On 2016-1-13 23:28, Tom Rini wrote:
> > > > > > > > On Wed, Jan 13, 2016 at 04:53:19PM +0800, Jeffy Chen
> > > > > > > > wrote:
> > > > > > > > 
> > > > > > > > > The android kernel is using appended dtb by default,
> > > > > > > > > and store
> > > > > > > > > ramdisk right after kernel & dtb.
> > > > > > > > > So we needs to relocate ramdisk, and use atags to pass
> > > > > > > > > params.
> > > > > > > > > 
> > > > > > > > > Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
> > > > > > > > > Acked-by: Simon Glass <sjg at chromium.org>
> > > > > > > > > ---
> > > > > > > > > 
> > > > > > > > > Changes in v4: None
> > > > > > > > > Changes in v3: None
> > > > > > > > > Changes in v2: None
> > > > > > > > > 
> > > > > > > > >  include/configs/kylin_rk3036.h | 23
> > > > > > > > > +++++++++++++++++++++++
> > > > > > > > >  1 file changed, 23 insertions(+)
> > > > > > > > > 
> > > > > > > > > diff --git a/include/configs/kylin_rk3036.h
> > > > > > > > > b/include/configs/kylin_rk3036.h
> > > > > > > > > index b750b26..49997ec 100644
> > > > > > > > > --- a/include/configs/kylin_rk3036.h
> > > > > > > > > +++ b/include/configs/kylin_rk3036.h
> > > > > > > > > @@ -35,6 +35,29 @@
> > > > > > > > >  #undef CONFIG_EXTRA_ENV_SETTINGS
> > > > > > > > >  #define CONFIG_EXTRA_ENV_SETTINGS \
> > > > > > > > >  	"partitions=" PARTS_DEFAULT \
> > > > > > > > > +	"mmcdev=0\0" \
> > > > > > > > > +	"mmcpart=5\0" \
> > > > > > > > > +	"loadaddr=" __stringify(CONFIG_SYS_LOAD_ADDR)
> > > > > > > > > "\0" \
> > > > > > > > > +
> > > > > > > > > +#define CONFIG_ANDROID_BOOT_IMAGE
> > > > > > > > > +#define CONFIG_SYS_BOOT_RAMDISK_HIGH
> > > > > > > > This should already be set.
> > > > > > > Right, i'll remove it...
> > > > > > > > > +#define CONFIG_SYS_HUSH_PARSER
> > > > > > > > > +
> > > > > > > > > +#undef CONFIG_BOOTCOMMAND
> > > > > > > > > +#define CONFIG_BOOTCOMMAND \
> > > > > > > > > +	"mmc dev ${mmcdev}; if mmc rescan; then " \
> > > > > > > > > +		"part start mmc ${mmcdev} ${mmcpart}
> > > > > > > > > boot_start;" \
> > > > > > > > > +		"part size mmc ${mmcdev} ${mmcpart}
> > > > > > > > > boot_size;" \
> > > > > > > > > +		"mmc read ${loadaddr} ${boot_start}
> > > > > > > > > ${boot_size};" \
> > > > > > > > > +		"bootm start ${loadaddr}; bootm
> > > > > > > > > ramdisk;" \
> > > > > > > > > +		"bootm prep; bootm go;" \
> > > > > > > > > +	"fi;" \
> > > > > > > > > +
> > > > > > > > > +/* Enable atags */
> > > > > > > > > +#define CONFIG_SYS_BOOTPARAMS_LEN	(64*1024)
> > > > > > > > > +#define CONFIG_INITRD_TAG
> > > > > > > > > +#define CONFIG_SETUP_MEMORY_TAGS
> > > > > > > > > +#define CONFIG_CMDLINE_TAG
> > > > > > > > But I'm confused as to what exactly is going on here. 
> > > > > > > >  Appended dtb is
> > > > > > > > not the same as ATAGS.  And you shouldn't need to split
> > > > > > > > up bootm like
> > > > > > > > that.  Can you please explain a bit more?  Thanks!
> > > > > > > The u-boot will pass atags to kernel, and kernel will merge
> > > > > > > those
> > > > > > > atags into the appended dtb(fdt).
> > > > > > > 
> > > > > > > The default bootm flow would not pass ramdisk state, but we
> > > > > > > need it,
> > > > > > > so we should add this state into default flow, or just use
> > > > > > > split
> > > > > > > bootm cmds :)
> > > > > > That seems very strange.  Is the ramdisk concatenated with
> > > > > > the kernel
> > > > > > and dtb as well (and that's why bootm ramdisk somehow finds
> > > > > > it but
> > > > > > normal bootm doesn't as you aren't passing in a ramdisk
> > > > > > address) ?
> > > > > Yes, the ramdisk concatenated with the kernel and dtb as
> > > > > well(u-boot/include/android_image.h: struct andr_img_hdr).
> > > > > 
> > > > > And the normal bootm cmd would find it by parsing andr_img_hdr
> > > > > struct.
> > > > > But we still need bootm ramdisk state, because it will call
> > > > > boot_ramdisk_high to relocate ramdisk area :)
> > > > > 
> > > > > I found if not relocate it to somewhere else, it would be
> > > > > corrupted
> > > > > after kernel's decompressing(during update fdt area).
> > > > So 'bootm $loadaddr' of an Android image sees, but does not
> > > > relocate the
> > > > ramdisk that is included in the image, but bootm ramdisk does? 
> > > >  That
> > > > sounds like a bug in the regular bootm handling.
> > > Yep, the default bootm flow would not contain ramdisk relocate
> > > state:
> > > 
> > > vi common/cmd_bootm.c
> > >         return do_bootm_states(cmdtp, flag, argc, argv,
> > > BOOTM_STATE_START |
> > >                 BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER |
> > >                 BOOTM_STATE_LOADOS |
> > > #if defined(CONFIG_PPC) || defined(CONFIG_MIPS)
> > >                 BOOTM_STATE_OS_CMDLINE |
> > > #endif
> > >                 BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |
> > >                 BOOTM_STATE_OS_GO, &images, 1);
> > > 
> > > But i'm not sure if it's ok to add it here...
> > 
> > Actually, maybe so.  This made me do some digging again back into
> > Simon's series that refactored the bootm code.  In the long long ago,
> > nearly every architecture would call bootm_ramdisk_high to relocate
> > the
> > ramdisk and set the environment (and poke the DT).  But it wasn't
> > always
> > clear when / why it would do this.  And it got consolidated.  And
> > here's
> > where it's tricky.  It looks correct today, still, to make sure that
> > we
> > relocate the ramdisk.  image_setup_linux() checks
> > IMAGE_ENABLE_RAMDISK_HIGH which is toggled by
> > CONFIG_SYS_BOOT_RAMDISK_HIGH.  But for example, MIPS whacked _back_
> > in a
> > local call to make sure the ramdisk was being relocated because it
> > wasn't back in 2014 into boot_prep_linux.  It may even be related to
> > some of the initrd rated things Masahiro has found recently.  So
> > something is wrong down in this core code.  
> 
> one problem is the different behaviour of bootm between legacy uImages
> and FIT uImages. In case of legacy uImages, the core code automatically
> relocates initramfs and DTB. In case of FIT uImages, the arch-specific
> do_bootm_linux() still has to do that. This was only somehow addressed
> by adding the helper function image_setup_linux() and calling it from
> do_bootm_linux(). But you can't use that function with legacy uImages,
> because initramfs and DTB would be relocated twice because the states
> BOOTM_STATE_RAMDISK and BOOTM_STATE_FDT are not checked. Because of
> that image_setup_linux() also leads to a different behaviour when
> calling bootm as a single command and calling bootm with sub-steps.
> 
> So the current bootm implementation on MIPS is the only way for me to
> make bootm working with all possible combinations of legacy/FIT uImage,
> with/without initramfs, with/without DTB and single/multiple bootm
> calls. I suspect that the current implementation on ARM does not work
> with all possible combinations.

Yes, I bet ARM and PowerPC and SPARC and m68k are broken now.

> > Jeffy, can you try and debug
> > this a bit since you have a failing case in front of you?  Otherwise
> > I'll put it on my TODO list.  Thanks!
> > 
> 
> Some ideas for a possible refactoring of the bootm core code:
> - remove arch-specific do_bootm_linux() and image_setup_linux()
> - rename functions like boot_prep_linux() and boot_jump_linux() to
> arch_boot_prep_linux() and arch_boot_jump_linux() and declare them as
> __weak and add a default implementation, the arch code can overwrite
> them
> - refactor the core code to call those functions at the appropriate
> locations

There's certainly room to make the whole bootm sequence shared better.
But I also see now that in the cleanup we moved from always calling
bootm_ramdisk_high to, well, not always calling it in all cases as you
found (but it looks like it should have been, but isn't).  I'm trying to
wrap my head around how to fix this.  Thanks!

-- 
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160125/1dce85db/attachment-0001.sig>

From thierry.reding at gmail.com  Mon Jan 25 09:08:55 2016
From: thierry.reding at gmail.com (Thierry Reding)
Date: Mon, 25 Jan 2016 18:08:55 +0100
Subject: [PATCH v3 00/12] pwm: add support for atomic update
In-Reply-To: <CAD=FV=WL3jYEgVe-m6QcAyQwfJgdX4J5OtKisKwyoKOrTSpEQA@mail.gmail.com>
References: <1442828009-6241-1-git-send-email-boris.brezillon@free-electrons.com>
 <2341981.a79ioYM9Es@diego> <20151110173416.GB21727@ulmo>
 <CAD=FV=WL3jYEgVe-m6QcAyQwfJgdX4J5OtKisKwyoKOrTSpEQA@mail.gmail.com>
Message-ID: <20160125170855.GA10182@ulmo>

On Mon, Jan 25, 2016 at 08:28:31AM -0800, Doug Anderson wrote:
> Thierry and Boris,
> 
> On Tue, Nov 10, 2015 at 9:34 AM, Thierry Reding
> <thierry.reding at gmail.com> wrote:
> > On Mon, Oct 19, 2015 at 12:12:12PM +0200, Heiko St?bner wrote:
> >> Hi Thierry,
> >>
> >> Am Montag, 21. September 2015, 11:33:17 schrieb Boris Brezillon:
> >> > Hello,
> >> >
> >> > This series adds support for atomic PWM update, or IOW, the capability
> >> > to update all the parameters of a PWM device (enabled/disabled, period,
> >> > duty and polarity) in one go.
> >>
> >> is anything more blocking this series? It's now sitting on the lists for
> >> nearly a month and everybody seems happy with it, so it would be really nice
> >> to have in mainline :-) .
> >>
> >> Especially as this also makes it possible for Rockchip Chromebooks to actually
> >> control the logic-regulator that is implemented as pwm-regulator there.
> >
> > Last time I tried to put this into linux-next I got immediately
> > bombarded by a number of build failures, so I backed things out. The
> > current plan is to give this another try after v4.4-rc1.
> 
> We're now into the 4.5 timeframe.  Does anyone have a concrete set of
> things that need to happen before this patch series makes it into
> mainline?

I think the current status is that we're more or less blocked on the
decision on what the reset state of the PWM should be. The question is
what to do if the PWM hardware readout differs from the settings found
in DT.

> From searching I see that the latest version of this series is v4 and
> there are a smattering of comments on the 24-patch series.  Presumably
> a v5 needs to be posted to address those things.
> 
> ...but it looks like the big sticking point is that Boris is waiting
> for a response to his questions in
> <https://patchwork.kernel.org/patch/7622881/>.  Thierry: can you give
> Boris some direction for what else he needs to do?  We need to come up
> with _some_ solution since this series gets us much better support for
> PWM regulators.  Without this series or some other solution, PWM
> regulators aren't usable in mainline on any system that uses them for
> system-critical rails.  Nearly all Rockchip reference boards and
> shipping devices uses a PWM regulator for the system-critidal "logic"
> rail.  That means any patches which need to change this rail in Linux
> are blocked.

I really don't understand this design decision. I presume that the PWM
controlling this system-critical logic is driven by the SoC? So if the
regulator is system-critical, doesn't that make it a chicken and egg
problem? How can the SoC turn the PWM on if it doesn't have power? But
perhaps I'm completely misunderstanding what you're saying. Perhaps if
somebody could summarize how exactly this works, it would help better
understand the requirements or what's the correct thing to do.

> If there's already been off-list discussion and Boris already knows
> what the next steps are then my apologies and I'll wait patiently for
> the next series.  ;)

I don't think we reached a conclusion on this. And to be honest, I'm not
sure what the right way forward is in this situation. So in order to
make some forward progress I suggest we start a discussion, hopefully
that will clarify the situation and help lead to the conclusion. Let me
recap where we are:

Boris' series has two goals: 1) allow seamless hand-off from firmware to
kernel of a PWM channel and 2) apply changes to a regulator in a single
atomic operation. To achieve this the concept of PWM state is introduced
which encapsulates the settings of a PWM channel. On driver probe the
current state will be read from hardware and when one or more parameters
are to be changed, the current state is duplicated, the new values set
in the state and the new state applied.

The problem that we've encountered is that since the PWM parameters are
specified in DT (or board files), there is the possibility of the PWM
hardware state and the board parameters disagreeing. To resolve such
situations there must be a point in time where both hardware state and
software state must be synchronized. Now the most straightforward way to
do that would be to simply apply the software state and be done with it.
However the software state initially lacks the duty cycle because it is
a parameter that usually depends on the use-case (for backlight for
instance it controls the brightness, for regulators it controls the
output voltage, ...).

Applying the software state as-is also means that there's no reason at
all to read out the hardware state in the first place, because it will
simply be discarded.

An alternative would be to discard the software state and trust the
hardware to be configured correctly. That's somewhat risky because we
don't know if the hardware is properly configured. Or Linux might have
different requirements from the firmware and hence needs to configure
the PWM differently.

Neither of the above are very attractive options. The best I've been
able to come up with so far is to completely remove this decision from
the PWM subsystem and let users handle this. That is, a PWM regulator
driver would have to have all the knowledge about how to configure the
PWM for its needs. So upon probe, the PWM regulator driver would inspect
the current state of the PWM and adjust if necessary, then apply again.
Ideally of course it wouldn't have to do anything because the hardware
PWM state would match the software configuration. The idea here is that
the PWM regulator driver knows exactly what duty cycle to configure to
obtain the desired output voltage.

That doesn't really get us closer, though. There is still the issue of
the user having to deal with two states: the current hardware state and
the software state as configured in DT or board files.

Like I said, I'm on the fence about this, so I'd appreciate any comments
and perhaps insight from user subsystem maintainers on how they'd like
this to look, or how this has been done with other resources (GPIOs,
...?)

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160125/b396ebf0/attachment.sig>

From robh at kernel.org  Mon Jan 25 09:11:34 2016
From: robh at kernel.org (Rob Herring)
Date: Mon, 25 Jan 2016 11:11:34 -0600
Subject: [PATCH v2 1/4] dt-bindings: power: reset: add document for
 reboot-mode driver
In-Reply-To: <56A07A6D.3010406@rock-chips.com>
References: <1452598029-8222-1-git-send-email-andy.yan@rock-chips.com>
 <1452598189-8272-1-git-send-email-andy.yan@rock-chips.com>
 <20160120182840.GA15741@rob-hp-laptop>
 <56A07A6D.3010406@rock-chips.com>
Message-ID: <20160125171134.GA21713@rob-hp-laptop>

On Thu, Jan 21, 2016 at 02:27:57PM +0800, Andy Yan wrote:
> Hi Rob:
>    thanks for your review.
> On 2016?01?21? 02:28, Rob Herring wrote:
> >On Tue, Jan 12, 2016 at 07:29:49PM +0800, Andy Yan wrote:
> >>add device tree binding document for reboot-mode driver
> >>
> >>Signed-off-by: Andy Yan <andy.yan at rock-chips.com>
> >>
> >>---
> >>
> >>Changes in v2: None
> >>Changes in v1: None
> >>
> >>  .../bindings/power/reset/reboot-mode.txt           | 41 +++++++++++++++++
> >>  .../bindings/power/reset/syscon-reboot-mode.txt    | 52 ++++++++++++++++++++++
> >>  2 files changed, 93 insertions(+)
> >>  create mode 100644 Documentation/devicetree/bindings/power/reset/reboot-mode.txt
> >>  create mode 100644 Documentation/devicetree/bindings/power/reset/syscon-reboot-mode.txt
> >>
> >>diff --git a/Documentation/devicetree/bindings/power/reset/reboot-mode.txt b/Documentation/devicetree/bindings/power/reset/reboot-mode.txt
> >>new file mode 100644
> >>index 0000000..81d9f66
> >>--- /dev/null
> >>+++ b/Documentation/devicetree/bindings/power/reset/reboot-mode.txt
> >>@@ -0,0 +1,41 @@
> >>+Generic reboot mode core map driver

[...]

> >>+		compatible = "syscon-reboot-mode";
> >>+		offset = <0x40>;
> >This doc by itself is a little confusing. For example, is a child of the
> >syscon node? I would remove offset (and perhaps compatible) from this
> >example.
> 
>    Yes, is a child of a syscon mapped node. For example, Rockchip platform
> use a register of PMU(rk3066/rk3288) or GRF(rk3036), PMU and GRF are aleady
> mapped by syscon.
>    offset and compatible are used by write interface driver like
> syscon-reboot-mode.c. If you don't like it appear in the core map doc, I
> will move it to the syscon-reboot-mode.txt?

Yes, try to make this doc stand on its own. It will obviously be 
incomplete lacking information on where in the DT it goes. So perhaps a 
note stating reboot-mode node location is defined in platform specific 
binding docs.

> >>+
> >>+		loader {
> >>+			linux,mode = "loader";
> >>+			loader,magic = <BOOT_LOADER>;
> >>+		};
> >Sorry, my previous suggestion was not clear. I'm suggesting get rid of
> >the subnodes and just do properties like this:
> >
> >loader = <BOOT_LOADER>;
> >maskrom = <BOOT_MASKROM>;
> >
> >That's the same amount of information unless node names and linux,mode
> >values are going to diverge. Do they need to? I can't see a reason.
> 
>     Because the command"linux,mode" and value"loader,magic" is vendor
> specific. I don't know what commands and how many mode other platform will
> use. So as John says in his reply, this sort of flexibility help us adapt
> the driver to different hardware/system environments.

The only part of "reboot to fastboot" that is vendor specific would be 
the magic value. While we can have custom modes, we should standardize 
the common ones as much as possible. As I pointed out in my reply to 
John, we can still support vendor specific modes with just a property.

> >
> >We need to be clear what loader means. More specifically, it is boot
> >into bootloader shell.
>     Actually, Rockchip platform will reboot into a bootloader download mode
> with this command. This mode can download faster than maskrom download mode.

My point is proven. I assumed one thing and you meant something else. 
Doesn't matter what the mode is, just needs to be clear.

Rob


From boris.brezillon at free-electrons.com  Mon Jan 25 09:55:19 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Mon, 25 Jan 2016 18:55:19 +0100
Subject: [PATCH v3 00/12] pwm: add support for atomic update
In-Reply-To: <20160125170855.GA10182@ulmo>
References: <1442828009-6241-1-git-send-email-boris.brezillon@free-electrons.com>
 <2341981.a79ioYM9Es@diego> <20151110173416.GB21727@ulmo>
 <CAD=FV=WL3jYEgVe-m6QcAyQwfJgdX4J5OtKisKwyoKOrTSpEQA@mail.gmail.com>
 <20160125170855.GA10182@ulmo>
Message-ID: <20160125185519.436339fc@bbrezillon>

Hi Thierry,

On Mon, 25 Jan 2016 18:08:55 +0100
Thierry Reding <thierry.reding at gmail.com> wrote:

> On Mon, Jan 25, 2016 at 08:28:31AM -0800, Doug Anderson wrote:
> > Thierry and Boris,
> > 
> > On Tue, Nov 10, 2015 at 9:34 AM, Thierry Reding
> > <thierry.reding at gmail.com> wrote:
> > > On Mon, Oct 19, 2015 at 12:12:12PM +0200, Heiko St?bner wrote:
> > >> Hi Thierry,
> > >>
> > >> Am Montag, 21. September 2015, 11:33:17 schrieb Boris Brezillon:
> > >> > Hello,
> > >> >
> > >> > This series adds support for atomic PWM update, or IOW, the capability
> > >> > to update all the parameters of a PWM device (enabled/disabled, period,
> > >> > duty and polarity) in one go.
> > >>
> > >> is anything more blocking this series? It's now sitting on the lists for
> > >> nearly a month and everybody seems happy with it, so it would be really nice
> > >> to have in mainline :-) .
> > >>
> > >> Especially as this also makes it possible for Rockchip Chromebooks to actually
> > >> control the logic-regulator that is implemented as pwm-regulator there.
> > >
> > > Last time I tried to put this into linux-next I got immediately
> > > bombarded by a number of build failures, so I backed things out. The
> > > current plan is to give this another try after v4.4-rc1.
> > 
> > We're now into the 4.5 timeframe.  Does anyone have a concrete set of
> > things that need to happen before this patch series makes it into
> > mainline?
> 
> I think the current status is that we're more or less blocked on the
> decision on what the reset state of the PWM should be. The question is
> what to do if the PWM hardware readout differs from the settings found
> in DT.

I think I already explained my PoV regarding the default/reference
setting (or what you later call the software state).
To me, this has never matched the state of the PWM device, it's just a
reference duty-cycle and polarity configuration that PWM users can
decide to apply or not. Currently, there's no mechanism to apply the
reference PWM config when a user request a PWM (which is the only
moment we could be able to apply this config, since reference configs
are per users and not attached to the PWM device itself).

All I'm trying to do in this series is teach some PWM users to make use
of hardware read-out state instead of blindly applying a default config
that can generate glitches in the PWM signal.

> 
> > From searching I see that the latest version of this series is v4 and
> > there are a smattering of comments on the 24-patch series.  Presumably
> > a v5 needs to be posted to address those things.
> > 
> > ...but it looks like the big sticking point is that Boris is waiting
> > for a response to his questions in
> > <https://patchwork.kernel.org/patch/7622881/>.  Thierry: can you give
> > Boris some direction for what else he needs to do?  We need to come up
> > with _some_ solution since this series gets us much better support for
> > PWM regulators.  Without this series or some other solution, PWM
> > regulators aren't usable in mainline on any system that uses them for
> > system-critical rails.  Nearly all Rockchip reference boards and
> > shipping devices uses a PWM regulator for the system-critidal "logic"
> > rail.  That means any patches which need to change this rail in Linux
> > are blocked.
> 
> I really don't understand this design decision. I presume that the PWM
> controlling this system-critical logic is driven by the SoC? So if the
> regulator is system-critical, doesn't that make it a chicken and egg
> problem? How can the SoC turn the PWM on if it doesn't have power? But
> perhaps I'm completely misunderstanding what you're saying. Perhaps if
> somebody could summarize how exactly this works, it would help better
> understand the requirements or what's the correct thing to do.
> 
> > If there's already been off-list discussion and Boris already knows
> > what the next steps are then my apologies and I'll wait patiently for
> > the next series.  ;)
> 
> I don't think we reached a conclusion on this. And to be honest, I'm not
> sure what the right way forward is in this situation. So in order to
> make some forward progress I suggest we start a discussion, hopefully
> that will clarify the situation and help lead to the conclusion. Let me
> recap where we are:
> 
> Boris' series has two goals: 1) allow seamless hand-off from firmware to
> kernel of a PWM channel and 2) apply changes to a regulator in a single
> atomic operation. To achieve this the concept of PWM state is introduced
> which encapsulates the settings of a PWM channel. On driver probe the
> current state will be read from hardware and when one or more parameters
> are to be changed, the current state is duplicated, the new values set
> in the state and the new state applied.
> 
> The problem that we've encountered is that since the PWM parameters are
> specified in DT (or board files), there is the possibility of the PWM
> hardware state and the board parameters disagreeing. To resolve such
> situations there must be a point in time where both hardware state and
> software state must be synchronized.

Maybe we're just hunting ghosts here. There's only one valid state, and
this is the 'hardware state'. I reused the pwm_state struct to store the
reference config (what you call software state), but actually that's
not a state, that's a configuration template, it will only become the
PWM state after being applied.
Maybe I should use another struct to clarify that.

> Now the most straightforward way to
> do that would be to simply apply the software state and be done with it.

Why that? I mean what's the problem of having a reference/template
config that PWM users can rely on if they want to, and still keep
the real state retrieved from hardware read-out? This way PWM users can
decide which one they want to use.

> However the software state initially lacks the duty cycle because it is
> a parameter that usually depends on the use-case (for backlight for
> instance it controls the brightness, for regulators it controls the
> output voltage, ...).

Yes, and I perfectly understand that, we don't want to encode the
default duty cycle in the DT.

> 
> Applying the software state as-is also means that there's no reason at
> all to read out the hardware state in the first place, because it will
> simply be discarded.

Yes, but as stated above, this can be a source of glitches, which is
exactly what we're trying to avoid.

> 
> An alternative would be to discard the software state and trust the
> hardware to be configured correctly. That's somewhat risky because we
> don't know if the hardware is properly configured. Or Linux might have
> different requirements from the firmware and hence needs to configure
> the PWM differently.

Yep, I agree on that too.

> 
> Neither of the above are very attractive options. The best I've been
> able to come up with so far is to completely remove this decision from
> the PWM subsystem and let users handle this. That is, a PWM regulator
> driver would have to have all the knowledge about how to configure the
> PWM for its needs. So upon probe, the PWM regulator driver would inspect
> the current state of the PWM and adjust if necessary, then apply again.
> Ideally of course it wouldn't have to do anything because the hardware
> PWM state would match the software configuration. The idea here is that
> the PWM regulator driver knows exactly what duty cycle to configure to
> obtain the desired output voltage.

That complicates a lot of things. I also suggested another approach:
provide an API to express the duty-cycle value relatively to the period
value (I actually sent a proposal for that, but you didn't comment on
it).
This would keep the PWM user implementations simple, and let the PWM
core switch from the bootloader/firmware period/polarity config to the
DT/PWM-lookup-table one on the first pwm_set_rel_duty_cycle() call.

> 
> That doesn't really get us closer, though. There is still the issue of
> the user having to deal with two states: the current hardware state and
> the software state as configured in DT or board files.

At the risk of repeating myself, I don't think we ever had a software
state, and existing PWM users are just assuming that the PWM devices
they are requesting were not used before. IOW, yes, patching PWM users
to support smoother transitions on the PWM devices require some changes,
but in the other hand, that's not like they were doing any better
before that series. And they still have the choice to completely ignore
the current PWM state and blindly apply their own config (extracted
from the reference PWM config)

Best Regards,

Boris

> 
> Like I said, I'm on the fence about this, so I'd appreciate any comments
> and perhaps insight from user subsystem maintainers on how they'd like
> this to look, or how this has been done with other resources (GPIOs,
> ...?)
> 
> Thierry



-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com


From dianders at google.com  Mon Jan 25 10:51:20 2016
From: dianders at google.com (Doug Anderson)
Date: Mon, 25 Jan 2016 10:51:20 -0800
Subject: [PATCH v3 00/12] pwm: add support for atomic update
In-Reply-To: <20160125170855.GA10182@ulmo>
References: <1442828009-6241-1-git-send-email-boris.brezillon@free-electrons.com>
 <2341981.a79ioYM9Es@diego> <20151110173416.GB21727@ulmo>
 <CAD=FV=WL3jYEgVe-m6QcAyQwfJgdX4J5OtKisKwyoKOrTSpEQA@mail.gmail.com>
 <20160125170855.GA10182@ulmo>
Message-ID: <CAD=FV=Xvxeqo3imUmjukC5iYk3EZkrJ04G7C1B-w4xyr-r6-2Q@mail.gmail.com>

Hi,

On Mon, Jan 25, 2016 at 9:08 AM, Thierry Reding
<thierry.reding at gmail.com> wrote:
> I really don't understand this design decision. I presume that the PWM
> controlling this system-critical logic is driven by the SoC? So if the
> regulator is system-critical, doesn't that make it a chicken and egg
> problem? How can the SoC turn the PWM on if it doesn't have power? But
> perhaps I'm completely misunderstanding what you're saying. Perhaps if
> somebody could summarize how exactly this works, it would help better
> understand the requirements or what's the correct thing to do.

Sure, here's how the dang thing works, as I understand it.

First, an overview of PWM regulator in general (maybe you know this,
but to get us on the same page).  There's an external regulator on the
system.  Looking on at least one board I see a TLV62565 specifically.

>From the docs of TLV62565, I see it describe the situation as the chip
being able to provide an adjustable output voltage configurable via an
external resistor divider.  In simplified terms words you can adjust
the output voltage of the regulator by tweaking the inputs to one of
its pins.  I'm just a software guy so I can't explain all the details
of it, but the net-net of the situation is is that you can hook this
configuration pin up to the output of a PWM (with a bunch of well
balanced resistors and capacitors) and then you can set the voltage
based on the output of the PWM.


OK, so what happens at bootup?  At bootup most of the pins of the
rk3288 (including the PWM) are configured as inputs with a pull.  The
particular pin hooked up to this PWM has a pulldown.  Remember that
we've got this nicely balanced set of resistors and capacitors hooked
up to the output of our PWM pin?  So what happens when we have this
pin configured as an input?  As I understand it / remember it:

* input w/ no pull: equivalent to 50% duty cycle on the PWM
* input w/ pull down: equivalent to slightly higher voltage than 50%
duty cycle on the PWM
* input w/ pull up: equivalent to slightly lower voltage than 50% duty
cycle on the PWM

On our particular board that means that the rail comes up with roughly
1.1V.  If you drive the PWM at 100% (or set the pin to output high)
you get .86V and if you drive the PWM at 0% (or set the pin to output
low) you get 1.36V.

Now, 1.1V is plenty of voltage to boot the system.  In fact most of
the logic within the SoC can run as low as 0.95V I think.  ...but 0.86
V is not enough to run the logic parts of the system (even at their
default bootup frequencies) 1.1V is _definitely_ not enough to run the
SDRAM memory controller at full speed.


So the bootloader wants to run the system fast so it can boot fast.
It increases the CPU rails (as is typical for a bootloader) and moves
the ARM CPU to 1.8GHz (from the relatively slow boot frequency) and
also raises the logic rail to 1.2V (or I think 1.15 V on systems w/
different memory configs) and inits the SDRAM controller to run at
full speed.  Then it boots Linux.

Note: apparently in U-Boot they actually boot system slower (this was
at least true 1.5 years ago with some reference U-Boot Rockchip
provided).  If I understand correctly they _didn't_ init the SDRAM
controller as full speed in the bootloader and just left the logic
rail at its bootup default.  If everyone had done that then our job
would be "easier" because we wouldn't need to read in the voltage
provided by the bootloader (by reading the PWM and cros-referencing
with our table), though even in that case we'd have to be very careful
not to glitch the line (since .86 V is too low).  Of course all of
those systems are stuck running at a very slow memory speed until
Linux gets DDR Frequency support for Rockchip whereas systems with our
bootloader not only boot faster but also get to use the full memory
speed even without any Linux DDRFreq drivers.


In any case: I think I've demonstrated how a critical system rail can
be using a PWM regulator and how glitching that PWM regulator at boot
time can be catastrophic.  Possibly it's not critical to be able to
"read" the voltage that that bootloader left things configured at
(it's mostly nice for debugging purposes), but it's definitely
important to make sure we don't set it to some default and important
to never glitch it.  Said another way, presumably a DDR Freq driver
would be able to switch the memory controller frequency sanely by
reading the memory controller frequency and using that to figure out
whether it needed to up the logic rail before or after the DDR Freq
change.


>> If there's already been off-list discussion and Boris already knows
>> what the next steps are then my apologies and I'll wait patiently for
>> the next series.  ;)
>
> I don't think we reached a conclusion on this. And to be honest, I'm not
> sure what the right way forward is in this situation. So in order to
> make some forward progress I suggest we start a discussion, hopefully
> that will clarify the situation and help lead to the conclusion. Let me
> recap where we are:
>
> Boris' series has two goals: 1) allow seamless hand-off from firmware to
> kernel of a PWM channel and 2) apply changes to a regulator in a single
> atomic operation. To achieve this the concept of PWM state is introduced
> which encapsulates the settings of a PWM channel. On driver probe the
> current state will be read from hardware and when one or more parameters
> are to be changed, the current state is duplicated, the new values set
> in the state and the new state applied.

At at even higher level the goal is to support PWM regulator for a
system-critical rail without ever glitching.  If we could solve that
problem in some other way that would also be fine too, I think.


> The problem that we've encountered is that since the PWM parameters are
> specified in DT (or board files), there is the possibility of the PWM
> hardware state and the board parameters disagreeing. To resolve such
> situations there must be a point in time where both hardware state and
> software state must be synchronized. Now the most straightforward way to
> do that would be to simply apply the software state and be done with it.
> However the software state initially lacks the duty cycle because it is
> a parameter that usually depends on the use-case (for backlight for
> instance it controls the brightness, for regulators it controls the
> output voltage, ...).

Excuse me for not knowing all details that have been talked about before, but...

A) The software state here is the period and flags (AKA "inverted),
right?  It does seem possible that you could apply the period and
flags while keeping the calculated bootup duty cycle percentage
(presuming that the PWM was actually enabled at probe time and there
was a bootup duty cycle at all).  That would basically say that
whenever you set the period of a PWM then the duty cycle of the PWM
should remain the same percentage.  That actually seems quite sane
IMHO.  It seems much saner than trying to keep the duty cycle "ns"
when the period changes or resetting the PWM to some default when the
period changes.


B) Alternatively, I'd also say that setting a period without a duty
cycle doesn't make a lot of sense.  ...so you could just apply the
period at the same time that you apply the duty cycle the first time.
Presumably you'd want to "lie" to the callers of the PWM subsystem and
tell them that you already changed the period even though the change
won't really take effect until they actually set the duty cycle.  If
anyone cared to find out the true hardware period we could add a new
pwm_get_hw_period().  ...or since the only reason you'd want to know
the hardware period would be if you're trying to read the current duty
cycle percentage, you could instead add "pwm_get_hw_state()" and have
that return both the hardware period ns and duty cycle ns (which is
the most accurate way to return the "percentage" without using fix or
floating point math).


Both of the above options seems like it could be sensible.  The 2nd
seems cleaner because it doesn't require you to recalculate /
approximate the old duty cycle using a new period, but it's slightly
uglier because it no longer returns the true hardware state from
pwm_get_period().


> Applying the software state as-is also means that there's no reason at
> all to read out the hardware state in the first place, because it will
> simply be discarded.

Pretty sure we can't discard the hardware duty cycle at bootup, as per above.


> An alternative would be to discard the software state and trust the
> hardware to be configured correctly. That's somewhat risky because we
> don't know if the hardware is properly configured. Or Linux might have
> different requirements from the firmware and hence needs to configure
> the PWM differently.

Doesn't seem like a good idea either.


> Neither of the above are very attractive options. The best I've been
> able to come up with so far is to completely remove this decision from
> the PWM subsystem and let users handle this. That is, a PWM regulator
> driver would have to have all the knowledge about how to configure the
> PWM for its needs. So upon probe, the PWM regulator driver would inspect
> the current state of the PWM and adjust if necessary, then apply again.
> Ideally of course it wouldn't have to do anything because the hardware
> PWM state would match the software configuration. The idea here is that
> the PWM regulator driver knows exactly what duty cycle to configure to
> obtain the desired output voltage.

I think this is like my suggestion B), right?  AKA the PWM regulator
would be the sole caller of pwm_get_hw_state() and it would use this
to figure out the existing duty cycle percentage.  Then it would
translate that into "ns" and would set the duty cycle.  Upon the first
set of the duty cycle both the period and duty cycle would be applied
at the same time.



> That doesn't really get us closer, though. There is still the issue of
> the user having to deal with two states: the current hardware state and
> the software state as configured in DT or board files.

I think the only users that need to deal with this are one that need a
seamless transition from bootup settings.  Adding a new API call to
support a new feature like this doesn't seem insane, and anyone who
doesn't want this new feature can just never call the new API.

The only thing that would "change" from the point of view of old
drivers is that the PWM period wouldn't change at bootup until the
duty cycle was set.  IMHO this is probably a bug fix.  AKA, for a PWM
backlight, imagine:

1. Firmware sets period to 20000 ns, duty cycle to 8000 ns (40%)
2. Linux boots up and sets period to 10000 ns.  Brightness of
backlight instantly goes to 80%.
3. Eventually something decides to set the backlight duty cycle and it
goes to the proper rate.

Skipping #2 seems like the right move.  ...or did I misunderstand how
something works?


> Like I said, I'm on the fence about this, so I'd appreciate any comments
> and perhaps insight from user subsystem maintainers on how they'd like
> this to look, or how this has been done with other resources (GPIOs,
> ...?)

-Doug


From trini at konsulko.com  Mon Jan 25 11:07:09 2016
From: trini at konsulko.com (Tom Rini)
Date: Mon, 25 Jan 2016 14:07:09 -0500
Subject: [U-Boot] [PATCH v4 5/6] rockchip: kylin: Enable boot with
 android boot image
In-Reply-To: <5698577B.8040209@rock-chips.com>
References: <1452675200-15941-1-git-send-email-jeffy.chen@rock-chips.com>
 <1452675200-15941-6-git-send-email-jeffy.chen@rock-chips.com>
 <20160113152811.GC3359@bill-the-cat>
 <56970886.2040009@rock-chips.com>
 <20160114162227.GY3359@bill-the-cat>
 <569842F2.4080808@rock-chips.com>
 <20160115005950.GC3359@bill-the-cat>
 <5698577B.8040209@rock-chips.com>
Message-ID: <20160125190709.GN3359@bill-the-cat>

On Fri, Jan 15, 2016 at 10:20:43AM +0800, Jeffy Chen wrote:
> Hi Tom,
> 
> On 2016-1-15 8:59, Tom Rini wrote:
> >On Fri, Jan 15, 2016 at 08:53:06AM +0800, Jeffy Chen wrote:
> >>Hi Tom,
> >>
> >>On 2016-1-15 0:22, Tom Rini wrote:
> >>>On Thu, Jan 14, 2016 at 10:31:34AM +0800, Jeffy Chen wrote:
> >>>>Hi Tom,
> >>>>
> >>>>On 2016-1-13 23:28, Tom Rini wrote:
> >>>>>On Wed, Jan 13, 2016 at 04:53:19PM +0800, Jeffy Chen wrote:
> >>>>>
> >>>>>>The android kernel is using appended dtb by default, and store
> >>>>>>ramdisk right after kernel & dtb.
> >>>>>>So we needs to relocate ramdisk, and use atags to pass params.
> >>>>>>
> >>>>>>Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
> >>>>>>Acked-by: Simon Glass <sjg at chromium.org>
> >>>>>>---
> >>>>>>
> >>>>>>Changes in v4: None
> >>>>>>Changes in v3: None
> >>>>>>Changes in v2: None
> >>>>>>
> >>>>>>  include/configs/kylin_rk3036.h | 23 +++++++++++++++++++++++
> >>>>>>  1 file changed, 23 insertions(+)
> >>>>>>
> >>>>>>diff --git a/include/configs/kylin_rk3036.h b/include/configs/kylin_rk3036.h
> >>>>>>index b750b26..49997ec 100644
> >>>>>>--- a/include/configs/kylin_rk3036.h
> >>>>>>+++ b/include/configs/kylin_rk3036.h
> >>>>>>@@ -35,6 +35,29 @@
> >>>>>>  #undef CONFIG_EXTRA_ENV_SETTINGS
> >>>>>>  #define CONFIG_EXTRA_ENV_SETTINGS \
> >>>>>>  	"partitions=" PARTS_DEFAULT \
> >>>>>>+	"mmcdev=0\0" \
> >>>>>>+	"mmcpart=5\0" \
> >>>>>>+	"loadaddr=" __stringify(CONFIG_SYS_LOAD_ADDR) "\0" \
> >>>>>>+
> >>>>>>+#define CONFIG_ANDROID_BOOT_IMAGE
> >>>>>>+#define CONFIG_SYS_BOOT_RAMDISK_HIGH
> >>>>>This should already be set.
> >>>>Right, i'll remove it...
> >>>>>>+#define CONFIG_SYS_HUSH_PARSER
> >>>>>>+
> >>>>>>+#undef CONFIG_BOOTCOMMAND
> >>>>>>+#define CONFIG_BOOTCOMMAND \
> >>>>>>+	"mmc dev ${mmcdev}; if mmc rescan; then " \
> >>>>>>+		"part start mmc ${mmcdev} ${mmcpart} boot_start;" \
> >>>>>>+		"part size mmc ${mmcdev} ${mmcpart} boot_size;" \
> >>>>>>+		"mmc read ${loadaddr} ${boot_start} ${boot_size};" \
> >>>>>>+		"bootm start ${loadaddr}; bootm ramdisk;" \
> >>>>>>+		"bootm prep; bootm go;" \
> >>>>>>+	"fi;" \
> >>>>>>+
> >>>>>>+/* Enable atags */
> >>>>>>+#define CONFIG_SYS_BOOTPARAMS_LEN	(64*1024)
> >>>>>>+#define CONFIG_INITRD_TAG
> >>>>>>+#define CONFIG_SETUP_MEMORY_TAGS
> >>>>>>+#define CONFIG_CMDLINE_TAG
> >>>>>But I'm confused as to what exactly is going on here.  Appended dtb is
> >>>>>not the same as ATAGS.  And you shouldn't need to split up bootm like
> >>>>>that.  Can you please explain a bit more?  Thanks!
> >>>>The u-boot will pass atags to kernel, and kernel will merge those
> >>>>atags into the appended dtb(fdt).
> >>>>
> >>>>The default bootm flow would not pass ramdisk state, but we need it,
> >>>>so we should add this state into default flow, or just use split
> >>>>bootm cmds :)
> >>>That seems very strange.  Is the ramdisk concatenated with the kernel
> >>>and dtb as well (and that's why bootm ramdisk somehow finds it but
> >>>normal bootm doesn't as you aren't passing in a ramdisk address) ?
> >>Yes, the ramdisk concatenated with the kernel and dtb as
> >>well(u-boot/include/android_image.h: struct andr_img_hdr).
> >>
> >>And the normal bootm cmd would find it by parsing andr_img_hdr struct.
> >>But we still need bootm ramdisk state, because it will call
> >>boot_ramdisk_high to relocate ramdisk area :)
> >>
> >>I found if not relocate it to somewhere else, it would be corrupted
> >>after kernel's decompressing(during update fdt area).
> >So 'bootm $loadaddr' of an Android image sees, but does not relocate the
> >ramdisk that is included in the image, but bootm ramdisk does?  That
> >sounds like a bug in the regular bootm handling.
> Yep, the default bootm flow would not contain ramdisk relocate state:
> 
> vi common/cmd_bootm.c
>         return do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START |
>                 BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER |
>                 BOOTM_STATE_LOADOS |
> #if defined(CONFIG_PPC) || defined(CONFIG_MIPS)
>                 BOOTM_STATE_OS_CMDLINE |
> #endif
>                 BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |
>                 BOOTM_STATE_OS_GO, &images, 1);
> 
> But i'm not sure if it's ok to add it here...

So, I've poked aruond a bit more on some of my TI platforms at least.
Can you look at the following things on yours?
1) Do you still see the problem on top of tree? Masahiro fixed at least
one problem just before v2016.01
2) Instead of fdt_high / initrd_high can you look at bootm_low /
bootm_size ?  include/configs/ti_armv7_common.h has these along with a
big comment about what the overall constraints are.

Thanks!

-- 
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160125/e7bb3b24/attachment.sig>

From dianders at chromium.org  Mon Jan 25 11:23:04 2016
From: dianders at chromium.org (Doug Anderson)
Date: Mon, 25 Jan 2016 11:23:04 -0800
Subject: [PATCH] brcmfmac: sdio: Increase the default timeouts a bit
In-Reply-To: <56A64114.3010400@gmail.com>
References: <1453718849-3508-1-git-send-email-sjoerd.simons@collabora.co.uk>
 <56A64114.3010400@gmail.com>
Message-ID: <CAD=FV=X-ejMLnh30TofU7dKP5WwZaXcgLaQwFs__wo9wHsv53Q@mail.gmail.com>

Hi,

On Mon, Jan 25, 2016 at 7:36 AM, Arend van Spriel <aspriel at gmail.com> wrote:
> On 25-01-16 11:47, Sjoerd Simons wrote:
>> On a Radxa Rock2 board with a Ampak AP6335 (Broadcom 4339 core) it seems
>> the card responds very quickly most of the time, unfortunately during
>> initialisation it sometimes seems to take just a bit over 2 seconds to
>> respond.
>>
>> This results intialization failing with message like:
>>   brcmf_c_preinit_dcmds: Retreiving cur_etheraddr failed, -52
>>   brcmf_bus_start: failed: -52
>>   brcmf_sdio_firmware_callback: dongle is not responding
>>
>> Increasing the timeout to allow for a bit more headroom allows the
>> card to initialize reliably.
>
> I would prefer to know where the 2 second response time comes from.
> Could be sdio retuning. Maybe the chromeos people can comment whether
> this has been root caused.

I reviewed Paul's change here
<https://chromium-review.googlesource.com/#/c/225921/> but didn't do
any root causing.

I think that, like Sjoerd saw, we were seeing this problem at boot
time.  Certainly at boot time lots of things are happening all at the
same time in the system and there are often delays, so anything that
might have been close to timing out in the past may now be actually
timing out.

This is the kind of thing that, IMHO, should have a real timeout that
is 10x what was expected and a non-fatal warning whenever we go over
the expected time.  ...but maybe that's overdesign.  :-P

Kinda curious: do we get one or two really slow responses on every
bootup, or just some bootups?  Do we ever succeed even with a slow
(like 1.8 or 1.9 seconds) response, or is it always either "fast" or
"2.1" seconds?


In any case, in my experience the Broadcom firmware is fairly
complicated and has numerous cases where it stretches SDIO more than
the other SDIO WiFi chip I've worked with.  It wouldn't terribly
surprise me if there was a period of time during bootup where it was
non-responsive for 2 seconds.  As unrelated "evidence" showing some of
the Broadcom SDIO limitations, you can see
<https://chromium-review.googlesource.com/#/c/250228/> and also the
fact that Broadcom often holds the SDIO "busy" signal whereas the
other SDIO WiFi chip I've worked never did that.  Also, even with all
fixes the Broadcom WiFi module will still show periodic SDIO errors
that the higher level driver just knows to ignore.

My old debugging from the (sorry, private) bug
http://crosbug.com/p/36975 showed this periodically even with all
known fixes:

[21310.271635] dwmmc_rockchip ff0d0000.dwmmc: CMD ERR: 0x00000104
[21550.583598] dwmmc_rockchip ff0d0000.dwmmc: CMD ERR: 0x00000104
[21550.616035] brcmfmac: brcmf_sdio_readframes: RXHEADER FAILED: -110
[21550.648460] brcmfmac: brcmf_sdio_rxfail: abort command, terminate
frame, send NAK
[21550.683502] dwmmc_rockchip ff0d0000.dwmmc: CMD ERR: 0x00000104
[21550.691214] dwmmc_rockchip ff0d0000.dwmmc: CMD ERR: 0x00000100
[22671.121329] dwmmc_rockchip ff0d0000.dwmmc: CMD ERR: 0x00000104
[22671.153167] dwmmc_rockchip ff0d0000.dwmmc: CMD ERR: 0x01000104
[22671.184581] brcmfmac: brcmf_sdio_readframes: RXHEADER FAILED: -110
[22671.192600] brcmfmac: brcmf_sdio_rxfail: abort command, terminate
frame, send NAK
[22671.201929] dwmmc_rockchip ff0d0000.dwmmc: CMD ERR: 0x00000114
[22671.209536] dwmmc_rockchip ff0d0000.dwmmc: CMD ERR: 0x00000100
[28463.941736] dwmmc_rockchip ff0d0000.dwmmc: CMD ERR: 0x00000104

At the time dekim@ responded:

> There are several sleep/wake control at different level. The one we're talking
> about here is controlled by brcmf_sdio_bus_sleep() in the host driver to turn
> on/off bus core on the chip. There can be a period of time when chip is not
> paying attention to the host command (cmd52 to the
> SBSDIO_FUNC1_SLEEPCSR).

...and we decided that the periodic SDIO errors weren't causing any
huge problems (since they were retried).  As far as I know, they still
happen today.


All of the above may not help you, but it serves as evidence that the
SDIO communication to Broadcom isn't terribly amazing and apparently
that's just the way that the module (or perhaps its firmware) is
designed.  It doesn't seem to affect anything in the real world, so I
suppose it is just something we need to live with.


Obviously if you have access to the firmware source code and can debug
further, that would be awesome.  I'm just not hopeful.


In any case:

Reviewed-by: Douglas Anderson <dianders at chromium.org>


From arend at broadcom.com  Mon Jan 25 12:07:00 2016
From: arend at broadcom.com (Arend van Spriel)
Date: Mon, 25 Jan 2016 21:07:00 +0100
Subject: [PATCH] brcmfmac: sdio: Increase the default timeouts a bit
In-Reply-To: <CAD=FV=X-ejMLnh30TofU7dKP5WwZaXcgLaQwFs__wo9wHsv53Q@mail.gmail.com>
References: <1453718849-3508-1-git-send-email-sjoerd.simons@collabora.co.uk>
 <56A64114.3010400@gmail.com>
 <CAD=FV=X-ejMLnh30TofU7dKP5WwZaXcgLaQwFs__wo9wHsv53Q@mail.gmail.com>
Message-ID: <56A68064.5020702@broadcom.com>

On 25-1-2016 20:23, Doug Anderson wrote:
> Hi,
> 
> On Mon, Jan 25, 2016 at 7:36 AM, Arend van Spriel <aspriel at gmail.com> wrote:
>> On 25-01-16 11:47, Sjoerd Simons wrote:
>>> On a Radxa Rock2 board with a Ampak AP6335 (Broadcom 4339 core) it seems
>>> the card responds very quickly most of the time, unfortunately during
>>> initialisation it sometimes seems to take just a bit over 2 seconds to
>>> respond.
>>>
>>> This results intialization failing with message like:
>>>   brcmf_c_preinit_dcmds: Retreiving cur_etheraddr failed, -52
>>>   brcmf_bus_start: failed: -52
>>>   brcmf_sdio_firmware_callback: dongle is not responding
>>>
>>> Increasing the timeout to allow for a bit more headroom allows the
>>> card to initialize reliably.
>>
>> I would prefer to know where the 2 second response time comes from.
>> Could be sdio retuning. Maybe the chromeos people can comment whether
>> this has been root caused.

Hi Doug,

Thanks for the elaborate response

> I reviewed Paul's change here
> <https://chromium-review.googlesource.com/#/c/225921/> but didn't do
> any root causing.
> 
> I think that, like Sjoerd saw, we were seeing this problem at boot
> time.  Certainly at boot time lots of things are happening all at the
> same time in the system and there are often delays, so anything that
> might have been close to timing out in the past may now be actually
> timing out.
> 
> This is the kind of thing that, IMHO, should have a real timeout that
> is 10x what was expected and a non-fatal warning whenever we go over
> the expected time.  ...but maybe that's overdesign.  :-P
> 
> Kinda curious: do we get one or two really slow responses on every
> bootup, or just some bootups?  Do we ever succeed even with a slow
> (like 1.8 or 1.9 seconds) response, or is it always either "fast" or
> "2.1" seconds?

Now these are interesting questions that I should have spilled out in
the first place. Thanks.

> In any case, in my experience the Broadcom firmware is fairly
> complicated and has numerous cases where it stretches SDIO more than
> the other SDIO WiFi chip I've worked with.  It wouldn't terribly
> surprise me if there was a period of time during bootup where it was
> non-responsive for 2 seconds.  As unrelated "evidence" showing some of
> the Broadcom SDIO limitations, you can see
> <https://chromium-review.googlesource.com/#/c/250228/> and also the
> fact that Broadcom often holds the SDIO "busy" signal whereas the
> other SDIO WiFi chip I've worked never did that.  Also, even with all
> fixes the Broadcom WiFi module will still show periodic SDIO errors
> that the higher level driver just knows to ignore.

The busy signal is in accordance with the SDIO spec. It would be good to
know if that is what is happening. Unfortunately I do have an SDIO
analyzer, but not reproduced it. May retry on veyron device.

> My old debugging from the (sorry, private) bug
> http://crosbug.com/p/36975 showed this periodically even with all
> known fixes:
> 
> [21310.271635] dwmmc_rockchip ff0d0000.dwmmc: CMD ERR: 0x00000104
> [21550.583598] dwmmc_rockchip ff0d0000.dwmmc: CMD ERR: 0x00000104
> [21550.616035] brcmfmac: brcmf_sdio_readframes: RXHEADER FAILED: -110
> [21550.648460] brcmfmac: brcmf_sdio_rxfail: abort command, terminate
> frame, send NAK
> [21550.683502] dwmmc_rockchip ff0d0000.dwmmc: CMD ERR: 0x00000104
> [21550.691214] dwmmc_rockchip ff0d0000.dwmmc: CMD ERR: 0x00000100
> [22671.121329] dwmmc_rockchip ff0d0000.dwmmc: CMD ERR: 0x00000104
> [22671.153167] dwmmc_rockchip ff0d0000.dwmmc: CMD ERR: 0x01000104
> [22671.184581] brcmfmac: brcmf_sdio_readframes: RXHEADER FAILED: -110
> [22671.192600] brcmfmac: brcmf_sdio_rxfail: abort command, terminate
> frame, send NAK
> [22671.201929] dwmmc_rockchip ff0d0000.dwmmc: CMD ERR: 0x00000114
> [22671.209536] dwmmc_rockchip ff0d0000.dwmmc: CMD ERR: 0x00000100
> [28463.941736] dwmmc_rockchip ff0d0000.dwmmc: CMD ERR: 0x00000104
> 
> At the time dekim@ responded:
> 
>> There are several sleep/wake control at different level. The one we're talking
>> about here is controlled by brcmf_sdio_bus_sleep() in the host driver to turn
>> on/off bus core on the chip. There can be a period of time when chip is not
>> paying attention to the host command (cmd52 to the
>> SBSDIO_FUNC1_SLEEPCSR).
> 
> ...and we decided that the periodic SDIO errors weren't causing any
> huge problems (since they were retried).  As far as I know, they still
> happen today.

Were these true periodic errors or random at interval.

> 
> All of the above may not help you, but it serves as evidence that the
> SDIO communication to Broadcom isn't terribly amazing and apparently
> that's just the way that the module (or perhaps its firmware) is
> designed.  It doesn't seem to affect anything in the real world, so I
> suppose it is just something we need to live with.
> 
> 
> Obviously if you have access to the firmware source code and can debug
> further, that would be awesome.  I'm just not hopeful.

I have, but that does not get my hopes up either. The issue may just as
well be in backplane access during boot and then we are talking VHDL.

> In any case:
> 
> Reviewed-by: Douglas Anderson <dianders at chromium.org>

Thanks again ;-)

Regards,
Arend


From dianders at chromium.org  Mon Jan 25 12:32:54 2016
From: dianders at chromium.org (Doug Anderson)
Date: Mon, 25 Jan 2016 12:32:54 -0800
Subject: [PATCH] brcmfmac: sdio: Increase the default timeouts a bit
In-Reply-To: <56A68064.5020702@broadcom.com>
References: <1453718849-3508-1-git-send-email-sjoerd.simons@collabora.co.uk>
 <56A64114.3010400@gmail.com>
 <CAD=FV=X-ejMLnh30TofU7dKP5WwZaXcgLaQwFs__wo9wHsv53Q@mail.gmail.com>
 <56A68064.5020702@broadcom.com>
Message-ID: <CAD=FV=XEuEdUH+cj0qGgszY5_BExsHKrpt31068j_XticsabKA@mail.gmail.com>

Hi,

On Mon, Jan 25, 2016 at 12:07 PM, Arend van Spriel <arend at broadcom.com> wrote:
>> In any case, in my experience the Broadcom firmware is fairly
>> complicated and has numerous cases where it stretches SDIO more than
>> the other SDIO WiFi chip I've worked with.  It wouldn't terribly
>> surprise me if there was a period of time during bootup where it was
>> non-responsive for 2 seconds.  As unrelated "evidence" showing some of
>> the Broadcom SDIO limitations, you can see
>> <https://chromium-review.googlesource.com/#/c/250228/> and also the
>> fact that Broadcom often holds the SDIO "busy" signal whereas the
>> other SDIO WiFi chip I've worked never did that.  Also, even with all
>> fixes the Broadcom WiFi module will still show periodic SDIO errors
>> that the higher level driver just knows to ignore.
>
> The busy signal is in accordance with the SDIO spec.

Sorry, didn't mean to imply that it wasn't.  That particular instance
is definitely legal, but it was also just showing a case where the
Broadcom WiFi needed something that wasn't needed elsewhere.

AKA: it's kinda like saying that other modules work on a UART without
flow control lines and the Broadcom needs the flow control lines.
UARTs are designed with flow control lines and it's totally legal to
need them.  ...but if you saw that one serial device didn't need them
then you would presume that maybe the device on the other end had very
low interrupt latency or had some other hardware help (large FIFOs,
DMA, fast processor, hard real time OS, etc).  A device with low
interrupt latency, large FIFOs, DMA, fast processor, etc would
presumably be less likely to have other pauses in communication.


> It would be good to
> know if that is what is happening. Unfortunately I do have an SDIO
> analyzer, but not reproduced it. May retry on veyron device.

You might just be able to put a trace to see if it hits the busy
signal.  Depending on your Linux version you might just be able to put
a trace in __mmc_start_request().  If not and if you're on dw_mmc you
can put a trace in dw_mci_wait_while_busy().


>> My old debugging from the (sorry, private) bug
>> http://crosbug.com/p/36975 showed this periodically even with all
>> known fixes:
>>
>> [21310.271635] dwmmc_rockchip ff0d0000.dwmmc: CMD ERR: 0x00000104
>> [21550.583598] dwmmc_rockchip ff0d0000.dwmmc: CMD ERR: 0x00000104
>> [21550.616035] brcmfmac: brcmf_sdio_readframes: RXHEADER FAILED: -110
>> [21550.648460] brcmfmac: brcmf_sdio_rxfail: abort command, terminate
>> frame, send NAK
>> [21550.683502] dwmmc_rockchip ff0d0000.dwmmc: CMD ERR: 0x00000104
>> [21550.691214] dwmmc_rockchip ff0d0000.dwmmc: CMD ERR: 0x00000100
>> [22671.121329] dwmmc_rockchip ff0d0000.dwmmc: CMD ERR: 0x00000104
>> [22671.153167] dwmmc_rockchip ff0d0000.dwmmc: CMD ERR: 0x01000104
>> [22671.184581] brcmfmac: brcmf_sdio_readframes: RXHEADER FAILED: -110
>> [22671.192600] brcmfmac: brcmf_sdio_rxfail: abort command, terminate
>> frame, send NAK
>> [22671.201929] dwmmc_rockchip ff0d0000.dwmmc: CMD ERR: 0x00000114
>> [22671.209536] dwmmc_rockchip ff0d0000.dwmmc: CMD ERR: 0x00000100
>> [28463.941736] dwmmc_rockchip ff0d0000.dwmmc: CMD ERR: 0x00000104
>>
>> At the time dekim@ responded:
>>
>>> There are several sleep/wake control at different level. The one we're talking
>>> about here is controlled by brcmf_sdio_bus_sleep() in the host driver to turn
>>> on/off bus core on the chip. There can be a period of time when chip is not
>>> paying attention to the host command (cmd52 to the
>>> SBSDIO_FUNC1_SLEEPCSR).
>>
>> ...and we decided that the periodic SDIO errors weren't causing any
>> huge problems (since they were retried).  As far as I know, they still
>> happen today.
>
> Were these true periodic errors or random at interval.

I believe they were random, and I believe that I needed "power save"
mode turned on for WiFi.  I believe errors were made worse if I had a
TCP/IP ping running (AKA bring out of power save and go back in
constantly).


From julian.calaby at gmail.com  Mon Jan 25 15:41:37 2016
From: julian.calaby at gmail.com (Julian Calaby)
Date: Tue, 26 Jan 2016 10:41:37 +1100
Subject: [PATCH] brcmfmac: sdio: Increase the default timeouts a bit
In-Reply-To: <56A641CC.6020500@gmail.com>
References: <1453718849-3508-1-git-send-email-sjoerd.simons@collabora.co.uk>
 <CAGRGNgXWE=b52Mcy8NsKBZS_4gz-TUiuFipjsuTZh1xjja=uUg@mail.gmail.com>
 <56A641CC.6020500@gmail.com>
Message-ID: <CAGRGNgVftXxnq06MOHKNVzVjvCkwCM8Bc+Sfs4NoOQR=sfYP-g@mail.gmail.com>

Hi Arend,

On Tue, Jan 26, 2016 at 2:39 AM, Arend van Spriel <aspriel at gmail.com> wrote:
> On 25-01-16 12:06, Julian Calaby wrote:
>> Hi Sjoerd,
>>
>> On Mon, Jan 25, 2016 at 9:47 PM, Sjoerd Simons
>> <sjoerd.simons at collabora.co.uk> wrote:
>>> On a Radxa Rock2 board with a Ampak AP6335 (Broadcom 4339 core) it seems
>>> the card responds very quickly most of the time, unfortunately during
>>> initialisation it sometimes seems to take just a bit over 2 seconds to
>>> respond.
>>>
>>> This results intialization failing with message like:
>>>   brcmf_c_preinit_dcmds: Retreiving cur_etheraddr failed, -52
>>>   brcmf_bus_start: failed: -52
>>>   brcmf_sdio_firmware_callback: dongle is not responding
>>>
>>> Increasing the timeout to allow for a bit more headroom allows the
>>> card to initialize reliably.
>>>
>>> A quick search online after diagnosing/fixing this showed that Google
>>> has a similar patch in their ChromeOS tree, so this doesn't seem
>>> specific to the board I'm using.
>>>
>>> Signed-off-by: Sjoerd Simons <sjoerd.simons at collabora.co.uk>
>>
>> Looks sane to me.
>>
>> Reviewed-by: Julian Calaby <julian.calaby at gmail.com>
>
> Not really a cleanup patch :-p , but thanks for the review.

I'm trying to review any "small" patch from (relatively) new people.

Thanks,

-- 
Julian Calaby

Email: julian.calaby at gmail.com
Profile: http://www.google.com/profiles/julian.calaby/


From heiko at sntech.de  Mon Jan 25 16:13:39 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Tue, 26 Jan 2016 01:13:39 +0100
Subject: [PATCH 0/3] Add rk3066 tsadc device tree bindings
In-Reply-To: <1453205361-27267-1-git-send-email-paweljarosz3691@gmail.com>
References: <1453205361-27267-1-git-send-email-paweljarosz3691@gmail.com>
Message-ID: <4176431.EN0Q2CUI1l@diego>

Hi,

Am Dienstag, 19. Januar 2016, 13:09:18 schrieb Pawe? Jarosz:
> This patchset adds devicetree bindings for rockchip rk3066 tsadc with needed
> dependencies.
> 
> Pawe? Jarosz (3):
>   clk: rockchip: Add new id for rk3066 tsadc clock
>   clk: rockchip: add tsadc clock on rk3066
>   ARM: dts: rockchip: add tsadc node

applied all three to appropriate branches for 4.6

I've removed the vref-supply from the dtsi and moved some properties around a 
bit.


Heiko


From heiko at sntech.de  Mon Jan 25 16:21:39 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Tue, 26 Jan 2016 01:21:39 +0100
Subject: [PATCH v4] phy: rockchip-usb: add handler for usb-uart functionality
Message-ID: <2609586.28qbVUeppF@diego>

Most newer Rockchip SoCs provide the possibility to use a usb-phy
as passthrough for the debug uart (uart2), making it possible to
for example get console output without needing to open the device.

This patch adds an early_initcall to enable this functionality
conditionally via the commandline and also disables the corresponding
usb controller in the devicetree.

Currently only data for the rk3288 is provided, but at least the
rk3188 and arm64 rk3368 also provide this functionality and will be
enabled later.

On a spliced usb cable the signals are tx on white wire(D+) and
rx on green wire(D-).

The one caveat is that currently the reconfiguration of the phy
happens as early_initcall, as the code depends on the unflattened
devicetree being available. Everything is fine if only a regular
console is active as the console-replay will happen after the
reconfiguation. But with earlycon active output up to smp-init
currently will get lost.

The phy is an optional property for the connected dwc2 controller,
so we still provide the phy device but fail all phy-ops with -EBUSY
to make sure the dwc2 does not try to transmit anything on the
repurposed phy.

Signed-off-by: Heiko Stuebner <heiko at sntech.de>
---
changes in v4:
- drop the rest of the phy-series, as the patches have gotten applied

So far, this hasn't gotten eyeballs yet, so citing discussion-parts from
the v3 coverletter:

The patch, while not associated with the new pll handling, also builds
on the groundwork introduced there and adds support for the function
repurposing one of the phys as passthrough for uart-data. This enables
attaching a ttl converter to the D+ and D- pins of an usb cable to
receive uart data this way, when it is not really possible to attach
a regular serial console to a board.

One point of critique in my first iteration [0] of this was, that
due to when the reconfiguration happens we may miss parts of the logs
when earlycon is enabled. So far early_initcall gets used as the
unflattened devicetree is necessary to set this up. Doing this for
example in the early_param directly would require parsing the flattened
devicetree to get needed nodes and properties.

I still maintain that if you're working on anything before smp-bringup
you should use a real dev-board instead or try to solder uart cables
on hopefully available test-points  .


 Documentation/kernel-parameters.txt |   6 +
 drivers/phy/phy-rockchip-usb.c      | 231 ++++++++++++++++++++++++++++++------
 2 files changed, 201 insertions(+), 36 deletions(-)

diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 87d40a7..d91417b 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -3486,6 +3486,12 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
 
 	ro		[KNL] Mount root device read-only on boot
 
+	rockchip.usb_uart
+			Enable the uart passthrough on the designated usb port
+			on Rockchip SoCs. When active, the signals of the
+			debug-uart get routed to the D+ and D- pins of the usb
+			port and the regular usb controller gets disabled.
+
 	root=		[KNL] Root filesystem
 			See name_to_dev_t comment in init/do_mounts.c.
 
diff --git a/drivers/phy/phy-rockchip-usb.c b/drivers/phy/phy-rockchip-usb.c
index 33a80eb..e162513 100644
--- a/drivers/phy/phy-rockchip-usb.c
+++ b/drivers/phy/phy-rockchip-usb.c
@@ -30,21 +30,23 @@
 #include <linux/regmap.h>
 #include <linux/mfd/syscon.h>
 
-/*
- * The higher 16-bit of this register is used for write protection
- * only if BIT(13 + 16) set to 1 the BIT(13) can be written.
- */
-#define SIDDQ_WRITE_ENA	BIT(29)
-#define SIDDQ_ON		BIT(13)
-#define SIDDQ_OFF		(0 << 13)
+static int enable_usb_uart;
+
+#define HIWORD_UPDATE(val, mask) \
+		((val) | (mask) << 16)
+
+#define UOC_CON0_SIDDQ BIT(13)
 
 struct rockchip_usb_phys {
 	int reg;
 	const char *pll_name;
 };
 
+struct rockchip_usb_phy_base;
 struct rockchip_usb_phy_pdata {
 	struct rockchip_usb_phys *phys;
+	int (*init_usb_uart)(struct regmap *grf);
+	int usb_uart_phy;
 };
 
 struct rockchip_usb_phy_base {
@@ -61,13 +63,15 @@ struct rockchip_usb_phy {
 	struct clk      *clk480m;
 	struct clk_hw	clk480m_hw;
 	struct phy	*phy;
+	bool		uart_enabled;
 };
 
 static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy,
 					   bool siddq)
 {
-	return regmap_write(phy->base->reg_base, phy->reg_offset,
-			    SIDDQ_WRITE_ENA | (siddq ? SIDDQ_ON : SIDDQ_OFF));
+	u32 val = HIWORD_UPDATE(siddq ? UOC_CON0_SIDDQ : 0, UOC_CON0_SIDDQ);
+
+	return regmap_write(phy->base->reg_base, phy->reg_offset, val);
 }
 
 static unsigned long rockchip_usb_phy480m_recalc_rate(struct clk_hw *hw,
@@ -108,7 +112,7 @@ static int rockchip_usb_phy480m_is_enabled(struct clk_hw *hw)
 	if (ret < 0)
 		return ret;
 
-	return (val & SIDDQ_ON) ? 0 : 1;
+	return (val & UOC_CON0_SIDDQ) ? 0 : 1;
 }
 
 static const struct clk_ops rockchip_usb_phy480m_ops = {
@@ -122,6 +126,9 @@ static int rockchip_usb_phy_power_off(struct phy *_phy)
 {
 	struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
 
+	if (phy->uart_enabled)
+		return -EBUSY;
+
 	clk_disable_unprepare(phy->clk480m);
 
 	return 0;
@@ -131,6 +138,9 @@ static int rockchip_usb_phy_power_on(struct phy *_phy)
 {
 	struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
 
+	if (phy->uart_enabled)
+		return -EBUSY;
+
 	return clk_prepare_enable(phy->clk480m);
 }
 
@@ -144,8 +154,10 @@ static void rockchip_usb_phy_action(void *data)
 {
 	struct rockchip_usb_phy *rk_phy = data;
 
-	of_clk_del_provider(rk_phy->np);
-	clk_unregister(rk_phy->clk480m);
+	if (!rk_phy->uart_enabled) {
+		of_clk_del_provider(rk_phy->np);
+		clk_unregister(rk_phy->clk480m);
+	}
 
 	if (rk_phy->clk)
 		clk_put(rk_phy->clk);
@@ -194,30 +206,35 @@ static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
 		return -EINVAL;
 	}
 
-	if (rk_phy->clk) {
-		clk_name = __clk_get_name(rk_phy->clk);
-		init.flags = 0;
-		init.parent_names = &clk_name;
-		init.num_parents = 1;
+	if (enable_usb_uart && base->pdata->usb_uart_phy == i) {
+		dev_dbg(base->dev, "phy%d used as uart output\n", i);
+		rk_phy->uart_enabled = true;
 	} else {
-		init.flags = CLK_IS_ROOT;
-		init.parent_names = NULL;
-		init.num_parents = 0;
-	}
+		if (rk_phy->clk) {
+			clk_name = __clk_get_name(rk_phy->clk);
+			init.flags = 0;
+			init.parent_names = &clk_name;
+			init.num_parents = 1;
+		} else {
+			init.flags = CLK_IS_ROOT;
+			init.parent_names = NULL;
+			init.num_parents = 0;
+		}
 
-	init.ops = &rockchip_usb_phy480m_ops;
-	rk_phy->clk480m_hw.init = &init;
+		init.ops = &rockchip_usb_phy480m_ops;
+		rk_phy->clk480m_hw.init = &init;
 
-	rk_phy->clk480m = clk_register(base->dev, &rk_phy->clk480m_hw);
-	if (IS_ERR(rk_phy->clk480m)) {
-		err = PTR_ERR(rk_phy->clk480m);
-		goto err_clk;
-	}
+		rk_phy->clk480m = clk_register(base->dev, &rk_phy->clk480m_hw);
+		if (IS_ERR(rk_phy->clk480m)) {
+			err = PTR_ERR(rk_phy->clk480m);
+			goto err_clk;
+		}
 
-	err = of_clk_add_provider(child, of_clk_src_simple_get,
-				  rk_phy->clk480m);
-	if (err < 0)
-		goto err_clk_prov;
+		err = of_clk_add_provider(child, of_clk_src_simple_get,
+					rk_phy->clk480m);
+		if (err < 0)
+			goto err_clk_prov;
+	}
 
 	err = devm_add_action(base->dev, rockchip_usb_phy_action, rk_phy);
 	if (err)
@@ -230,13 +247,21 @@ static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
 	}
 	phy_set_drvdata(rk_phy->phy, rk_phy);
 
-	/* only power up usb phy when it use, so disable it when init*/
-	return rockchip_usb_phy_power(rk_phy, 1);
+	/*
+	 * When acting as uart-pipe, just keep clock on otherwise
+	 * only power up usb phy when it use, so disable it when init
+	 */
+	if (rk_phy->uart_enabled)
+		return clk_prepare_enable(rk_phy->clk);
+	else
+		return rockchip_usb_phy_power(rk_phy, 1);
 
 err_devm_action:
-	of_clk_del_provider(child);
+	if (!rk_phy->uart_enabled)
+		of_clk_del_provider(child);
 err_clk_prov:
-	clk_unregister(rk_phy->clk480m);
+	if (!rk_phy->uart_enabled)
+		clk_unregister(rk_phy->clk480m);
 err_clk:
 	if (rk_phy->clk)
 		clk_put(rk_phy->clk);
@@ -259,6 +284,86 @@ static const struct rockchip_usb_phy_pdata rk3188_pdata = {
 	},
 };
 
+#define RK3288_UOC0_CON0				0x320
+#define RK3288_UOC0_CON0_COMMON_ON_N			BIT(0)
+#define RK3288_UOC0_CON0_DISABLE			BIT(4)
+
+#define RK3288_UOC0_CON2				0x328
+#define RK3288_UOC0_CON2_SOFT_CON_SEL			BIT(2)
+
+#define RK3288_UOC0_CON3				0x32c
+#define RK3288_UOC0_CON3_UTMI_SUSPENDN			BIT(0)
+#define RK3288_UOC0_CON3_UTMI_OPMODE_NODRIVING		(1 << 1)
+#define RK3288_UOC0_CON3_UTMI_OPMODE_MASK		(3 << 1)
+#define RK3288_UOC0_CON3_UTMI_XCVRSEELCT_FSTRANSC	(1 << 3)
+#define RK3288_UOC0_CON3_UTMI_XCVRSEELCT_MASK		(3 << 3)
+#define RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED		BIT(5)
+#define RK3288_UOC0_CON3_BYPASSDMEN			BIT(6)
+#define RK3288_UOC0_CON3_BYPASSSEL			BIT(7)
+
+/*
+ * Enable the bypass of uart2 data through the otg usb phy.
+ * Original description in the TRM.
+ * 1. Disable the OTG block by setting OTGDISABLE0 to 1?b1.
+ * 2. Disable the pull-up resistance on the D+ line by setting
+ *    OPMODE0[1:0] to 2?b01.
+ * 3. To ensure that the XO, Bias, and PLL blocks are powered down in Suspend
+ *    mode, set COMMONONN to 1?b1.
+ * 4. Place the USB PHY in Suspend mode by setting SUSPENDM0 to 1?b0.
+ * 5. Set BYPASSSEL0 to 1?b1.
+ * 6. To transmit data, controls BYPASSDMEN0, and BYPASSDMDATA0.
+ * To receive data, monitor FSVPLUS0.
+ *
+ * The actual code in the vendor kernel does some things differently.
+ */
+static int __init rk3288_init_usb_uart(struct regmap *grf)
+{
+	u32 val;
+	int ret;
+
+	/*
+	 * COMMON_ON and DISABLE settings are described in the TRM,
+	 * but were not present in the original code.
+	 * Also disable the analog phy components to save power.
+	 */
+	val = HIWORD_UPDATE(RK3288_UOC0_CON0_COMMON_ON_N
+				| RK3288_UOC0_CON0_DISABLE
+				| UOC_CON0_SIDDQ,
+			    RK3288_UOC0_CON0_COMMON_ON_N
+				| RK3288_UOC0_CON0_DISABLE
+				| UOC_CON0_SIDDQ);
+	ret = regmap_write(grf, RK3288_UOC0_CON0, val);
+	if (ret)
+		return ret;
+
+	val = HIWORD_UPDATE(RK3288_UOC0_CON2_SOFT_CON_SEL,
+			    RK3288_UOC0_CON2_SOFT_CON_SEL);
+	ret = regmap_write(grf, RK3288_UOC0_CON2, val);
+	if (ret)
+		return ret;
+
+	val = HIWORD_UPDATE(RK3288_UOC0_CON3_UTMI_OPMODE_NODRIVING
+				| RK3288_UOC0_CON3_UTMI_XCVRSEELCT_FSTRANSC
+				| RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED,
+			    RK3288_UOC0_CON3_UTMI_SUSPENDN
+				| RK3288_UOC0_CON3_UTMI_OPMODE_MASK
+				| RK3288_UOC0_CON3_UTMI_XCVRSEELCT_MASK
+				| RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED);
+	ret = regmap_write(grf, RK3288_UOC0_CON3, val);
+	if (ret)
+		return ret;
+
+	val = HIWORD_UPDATE(RK3288_UOC0_CON3_BYPASSSEL
+				| RK3288_UOC0_CON3_BYPASSDMEN,
+			    RK3288_UOC0_CON3_BYPASSSEL
+				| RK3288_UOC0_CON3_BYPASSDMEN);
+	ret = regmap_write(grf, RK3288_UOC0_CON3, val);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
 static const struct rockchip_usb_phy_pdata rk3288_pdata = {
 	.phys = (struct rockchip_usb_phys[]){
 		{ .reg = 0x320, .pll_name = "sclk_otgphy0_480m" },
@@ -266,6 +371,8 @@ static const struct rockchip_usb_phy_pdata rk3288_pdata = {
 		{ .reg = 0x348, .pll_name = "sclk_otgphy2_480m" },
 		{ /* sentinel */ }
 	},
+	.init_usb_uart = rk3288_init_usb_uart,
+	.usb_uart_phy = 0,
 };
 
 static int rockchip_usb_phy_probe(struct platform_device *pdev)
@@ -328,6 +435,58 @@ static struct platform_driver rockchip_usb_driver = {
 
 module_platform_driver(rockchip_usb_driver);
 
+static int __init rockchip_init_usb_uart(void)
+{
+	const struct of_device_id *match;
+	const struct rockchip_usb_phy_pdata *data;
+	struct device_node *np;
+	struct regmap *grf;
+	int ret;
+
+	if (!enable_usb_uart)
+		return 0;
+
+	np = of_find_matching_node_and_match(NULL, rockchip_usb_phy_dt_ids,
+					     &match);
+	if (!np) {
+		pr_err("%s: failed to find usbphy node\n", __func__);
+		return -ENOTSUPP;
+	}
+
+	pr_debug("%s: using settings for %s\n", __func__, match->compatible);
+	data = match->data;
+
+	if (!data->init_usb_uart) {
+		pr_err("%s: usb-uart not available on %s\n",
+		       __func__, match->compatible);
+		return -ENOTSUPP;
+	}
+
+	grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+	if (IS_ERR(grf)) {
+		pr_err("%s: Missing rockchip,grf property, %lu\n",
+		       __func__, PTR_ERR(grf));
+		return PTR_ERR(grf);
+	}
+
+	ret = data->init_usb_uart(grf);
+	if (ret) {
+		pr_err("%s: could not init usb_uart, %d\n", __func__, ret);
+		enable_usb_uart = 0;
+		return ret;
+	}
+
+	return 0;
+}
+early_initcall(rockchip_init_usb_uart);
+
+static int __init rockchip_usb_uart(char *buf)
+{
+	enable_usb_uart = true;
+	return 0;
+}
+early_param("rockchip.usb_uart", rockchip_usb_uart);
+
 MODULE_AUTHOR("Yunzhi Li <lyz at rock-chips.com>");
 MODULE_DESCRIPTION("Rockchip USB 2.0 PHY driver");
 MODULE_LICENSE("GPL v2");
-- 
2.6.4




From pebolle at tiscali.nl  Mon Jan 25 16:45:19 2016
From: pebolle at tiscali.nl (Paul Bolle)
Date: Tue, 26 Jan 2016 01:45:19 +0100
Subject: [PATCH v6] drm/rockchip: hdmi: add Innosilicon HDMI support
In-Reply-To: <1453686430-21470-1-git-send-email-ykk@rock-chips.com>
References: <1453686197-20975-1-git-send-email-ykk@rock-chips.com>
 <1453686430-21470-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1453769119.17181.37.camel@tiscali.nl>

On ma, 2016-01-25 at 09:47 +0800, Yakir Yang wrote:
> --- a/drivers/gpu/drm/rockchip/Kconfig
> +++ b/drivers/gpu/drm/rockchip/Kconfig

> +config ROCKCHIP_INNO_HDMI
> +	tristate "Rockchip specific extensions for Innosilicon HDMI"
> +        depends on DRM_ROCKCHIP
> +        help
> +	  This selects support for Rockchip SoC specific extensions
> +	  for the Innosilicon HDMI driver. If you want to enable
> +	  HDMI on RK3036 based SoC, you should selet this option.

(Really trivial: start indentation with tabs only, please. And
s/selet/select/.)

> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/inno_hdmi.c

> + * This software is licensed under the terms of the GNU General
> Public
> + * License version 2, as published by the Free Software Foundation,
> and
> + * may be copied, distributed, and modified under those terms.

This states this file is licensed GPL v2 only.

> +MODULE_LICENSE("GPL");

And, according to include/linux/module.h, this means "GNU Public License
v2 or later".

So I think there's a (subtle) mismatch between the license ident used
for this driver and the comment above.

Thanks,


Paul Bolle


From shawn.lin at rock-chips.com  Mon Jan 25 18:06:43 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Tue, 26 Jan 2016 10:06:43 +0800
Subject: [PATCH] arm: dts: rockchip: remove broken-cd from emmc and sdio
Message-ID: <1453774003-10184-1-git-send-email-shawn.lin@rock-chips.com>

Only one of "broken-cd" and "non-removable" should be supplied
according to Documentation/devicetree/bindings/mmc/mmc.txt.
Obviously emmc and sdio-wifi are non-removable devices, while
broken-cd is for removable device whose card detect pin is broken.

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
---

 arch/arm/boot/dts/rk3036-kylin.dts     | 1 -
 arch/arm/boot/dts/rk3036.dtsi          | 1 -
 arch/arm/boot/dts/rk3066a-rayeager.dts | 2 --
 arch/arm/boot/dts/rk3228-evb.dts       | 1 -
 arch/arm/boot/dts/rk3288-evb.dtsi      | 1 -
 arch/arm/boot/dts/rk3288-firefly.dtsi  | 2 --
 arch/arm/boot/dts/rk3288-popmetal.dts  | 1 -
 arch/arm/boot/dts/rk3288-veyron.dtsi   | 2 --
 8 files changed, 11 deletions(-)

diff --git a/arch/arm/boot/dts/rk3036-kylin.dts b/arch/arm/boot/dts/rk3036-kylin.dts
index 992f9ca..023153a 100644
--- a/arch/arm/boot/dts/rk3036-kylin.dts
+++ b/arch/arm/boot/dts/rk3036-kylin.dts
@@ -262,7 +262,6 @@
 &sdio {
 	status = "okay";
 
-	broken-cd;
 	bus-width = <4>;
 	cap-sdio-irq;
 	default-sample-phase = <90>;
diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
index b9567c1..cf54066 100644
--- a/arch/arm/boot/dts/rk3036.dtsi
+++ b/arch/arm/boot/dts/rk3036.dtsi
@@ -214,7 +214,6 @@
 		compatible = "rockchip,rk3288-dw-mshc";
 		reg = <0x1021c000 0x4000>;
 		interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
-		broken-cd;
 		bus-width = <8>;
 		cap-mmc-highspeed;
 		clock-frequency = <37500000>;
diff --git a/arch/arm/boot/dts/rk3066a-rayeager.dts b/arch/arm/boot/dts/rk3066a-rayeager.dts
index 341c1f8..e9db017 100644
--- a/arch/arm/boot/dts/rk3066a-rayeager.dts
+++ b/arch/arm/boot/dts/rk3066a-rayeager.dts
@@ -170,7 +170,6 @@
 };
 
 &emmc {
-	broken-cd;
 	bus-width = <8>;
 	cap-mmc-highspeed;
 	disable-wp;
@@ -336,7 +335,6 @@
 };
 
 &mmc1 {
-	broken-cd;
 	bus-width = <4>;
 	disable-wp;
 	non-removable;
diff --git a/arch/arm/boot/dts/rk3228-evb.dts b/arch/arm/boot/dts/rk3228-evb.dts
index e3898b8..a6d6a7b 100644
--- a/arch/arm/boot/dts/rk3228-evb.dts
+++ b/arch/arm/boot/dts/rk3228-evb.dts
@@ -53,7 +53,6 @@
 };
 
 &emmc {
-	broken-cd;
 	cap-mmc-highspeed;
 	mmc-ddr-1_8v;
 	disable-wp;
diff --git a/arch/arm/boot/dts/rk3288-evb.dtsi b/arch/arm/boot/dts/rk3288-evb.dtsi
index 4faabdb..f90e408 100644
--- a/arch/arm/boot/dts/rk3288-evb.dtsi
+++ b/arch/arm/boot/dts/rk3288-evb.dtsi
@@ -172,7 +172,6 @@
 };
 
 &emmc {
-	broken-cd;
 	bus-width = <8>;
 	cap-mmc-highspeed;
 	disable-wp;
diff --git a/arch/arm/boot/dts/rk3288-firefly.dtsi b/arch/arm/boot/dts/rk3288-firefly.dtsi
index 4e3fd9a..3f36af7 100644
--- a/arch/arm/boot/dts/rk3288-firefly.dtsi
+++ b/arch/arm/boot/dts/rk3288-firefly.dtsi
@@ -208,7 +208,6 @@
 };
 
 &emmc {
-	broken-cd;
 	bus-width = <8>;
 	cap-mmc-highspeed;
 	disable-wp;
@@ -485,7 +484,6 @@
 };
 
 &sdio0 {
-	broken-cd;
 	bus-width = <4>;
 	disable-wp;
 	non-removable;
diff --git a/arch/arm/boot/dts/rk3288-popmetal.dts b/arch/arm/boot/dts/rk3288-popmetal.dts
index 65c4756..cc2be77 100644
--- a/arch/arm/boot/dts/rk3288-popmetal.dts
+++ b/arch/arm/boot/dts/rk3288-popmetal.dts
@@ -162,7 +162,6 @@
 };
 
 &emmc {
-	broken-cd;
 	bus-width = <8>;
 	cap-mmc-highspeed;
 	disable-wp;
diff --git a/arch/arm/boot/dts/rk3288-veyron.dtsi b/arch/arm/boot/dts/rk3288-veyron.dtsi
index 9fce91f..725e08b 100644
--- a/arch/arm/boot/dts/rk3288-veyron.dtsi
+++ b/arch/arm/boot/dts/rk3288-veyron.dtsi
@@ -146,7 +146,6 @@
 &emmc {
 	status = "okay";
 
-	broken-cd;
 	bus-width = <8>;
 	cap-mmc-highspeed;
 	rockchip,default-sample-phase = <158>;
@@ -352,7 +351,6 @@
 &sdio0 {
 	status = "okay";
 
-	broken-cd;
 	bus-width = <4>;
 	cap-sd-highspeed;
 	cap-sdio-irq;
-- 
2.3.7




From shawn.lin at rock-chips.com  Mon Jan 25 18:06:59 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Tue, 26 Jan 2016 10:06:59 +0800
Subject: [PATCH] arm64: dts: rockchip: remove broken-cd from emmc and sdio
Message-ID: <1453774019-10225-1-git-send-email-shawn.lin@rock-chips.com>

Only one of "broken-cd" and "non-removable" should be supplied
according to Documentation/devicetree/bindings/mmc/mmc.txt.
Obviously emmc and sdio-wifi are non-removable devices, while
broken-cd is for removable device whose card detect pin is broken.

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
---

 arch/arm64/boot/dts/rockchip/rk3368-evb.dtsi | 1 -
 arch/arm64/boot/dts/rockchip/rk3368-r88.dts  | 1 -
 2 files changed, 2 deletions(-)

diff --git a/arch/arm64/boot/dts/rockchip/rk3368-evb.dtsi b/arch/arm64/boot/dts/rockchip/rk3368-evb.dtsi
index 8c219cc..19616a4 100644
--- a/arch/arm64/boot/dts/rockchip/rk3368-evb.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3368-evb.dtsi
@@ -152,7 +152,6 @@
 };
 
 &emmc {
-	broken-cd;
 	bus-width = <8>;
 	cap-mmc-highspeed;
 	disable-wp;
diff --git a/arch/arm64/boot/dts/rockchip/rk3368-r88.dts b/arch/arm64/boot/dts/rockchip/rk3368-r88.dts
index 104cbee..047c8f5 100644
--- a/arch/arm64/boot/dts/rockchip/rk3368-r88.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3368-r88.dts
@@ -185,7 +185,6 @@
 };
 
 &emmc {
-	broken-cd;
 	bus-width = <8>;
 	cap-mmc-highspeed;
 	disable-wp;
-- 
2.3.7




From shawn.lin at rock-chips.com  Mon Jan 25 18:23:27 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Tue, 26 Jan 2016 10:23:27 +0800
Subject: [PATCH] arm: dts: rockchip: remove broken-cd from emmc and sdio
In-Reply-To: <1453774003-10184-1-git-send-email-shawn.lin@rock-chips.com>
References: <1453774003-10184-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <56A6D89F.9020602@rock-chips.com>

+ Xing and Caesar

Hi Xing and Caesar,

For future dts, we should avoid to add these two properties
together. :)

On 2016/1/26 10:06, Shawn Lin wrote:
> Only one of "broken-cd" and "non-removable" should be supplied
> according to Documentation/devicetree/bindings/mmc/mmc.txt.
> Obviously emmc and sdio-wifi are non-removable devices, while
> broken-cd is for removable device whose card detect pin is broken.
>
> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
> ---
>
>   arch/arm/boot/dts/rk3036-kylin.dts     | 1 -
>   arch/arm/boot/dts/rk3036.dtsi          | 1 -
>   arch/arm/boot/dts/rk3066a-rayeager.dts | 2 --
>   arch/arm/boot/dts/rk3228-evb.dts       | 1 -
>   arch/arm/boot/dts/rk3288-evb.dtsi      | 1 -
>   arch/arm/boot/dts/rk3288-firefly.dtsi  | 2 --
>   arch/arm/boot/dts/rk3288-popmetal.dts  | 1 -
>   arch/arm/boot/dts/rk3288-veyron.dtsi   | 2 --
>   8 files changed, 11 deletions(-)
>
> diff --git a/arch/arm/boot/dts/rk3036-kylin.dts b/arch/arm/boot/dts/rk3036-kylin.dts
> index 992f9ca..023153a 100644
> --- a/arch/arm/boot/dts/rk3036-kylin.dts
> +++ b/arch/arm/boot/dts/rk3036-kylin.dts
> @@ -262,7 +262,6 @@
>   &sdio {
>   	status = "okay";
>
> -	broken-cd;
>   	bus-width = <4>;
>   	cap-sdio-irq;
>   	default-sample-phase = <90>;
> diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
> index b9567c1..cf54066 100644
> --- a/arch/arm/boot/dts/rk3036.dtsi
> +++ b/arch/arm/boot/dts/rk3036.dtsi
> @@ -214,7 +214,6 @@
>   		compatible = "rockchip,rk3288-dw-mshc";
>   		reg = <0x1021c000 0x4000>;
>   		interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
> -		broken-cd;
>   		bus-width = <8>;
>   		cap-mmc-highspeed;
>   		clock-frequency = <37500000>;
> diff --git a/arch/arm/boot/dts/rk3066a-rayeager.dts b/arch/arm/boot/dts/rk3066a-rayeager.dts
> index 341c1f8..e9db017 100644
> --- a/arch/arm/boot/dts/rk3066a-rayeager.dts
> +++ b/arch/arm/boot/dts/rk3066a-rayeager.dts
> @@ -170,7 +170,6 @@
>   };
>
>   &emmc {
> -	broken-cd;
>   	bus-width = <8>;
>   	cap-mmc-highspeed;
>   	disable-wp;
> @@ -336,7 +335,6 @@
>   };
>
>   &mmc1 {
> -	broken-cd;
>   	bus-width = <4>;
>   	disable-wp;
>   	non-removable;
> diff --git a/arch/arm/boot/dts/rk3228-evb.dts b/arch/arm/boot/dts/rk3228-evb.dts
> index e3898b8..a6d6a7b 100644
> --- a/arch/arm/boot/dts/rk3228-evb.dts
> +++ b/arch/arm/boot/dts/rk3228-evb.dts
> @@ -53,7 +53,6 @@
>   };
>
>   &emmc {
> -	broken-cd;
>   	cap-mmc-highspeed;
>   	mmc-ddr-1_8v;
>   	disable-wp;
> diff --git a/arch/arm/boot/dts/rk3288-evb.dtsi b/arch/arm/boot/dts/rk3288-evb.dtsi
> index 4faabdb..f90e408 100644
> --- a/arch/arm/boot/dts/rk3288-evb.dtsi
> +++ b/arch/arm/boot/dts/rk3288-evb.dtsi
> @@ -172,7 +172,6 @@
>   };
>
>   &emmc {
> -	broken-cd;
>   	bus-width = <8>;
>   	cap-mmc-highspeed;
>   	disable-wp;
> diff --git a/arch/arm/boot/dts/rk3288-firefly.dtsi b/arch/arm/boot/dts/rk3288-firefly.dtsi
> index 4e3fd9a..3f36af7 100644
> --- a/arch/arm/boot/dts/rk3288-firefly.dtsi
> +++ b/arch/arm/boot/dts/rk3288-firefly.dtsi
> @@ -208,7 +208,6 @@
>   };
>
>   &emmc {
> -	broken-cd;
>   	bus-width = <8>;
>   	cap-mmc-highspeed;
>   	disable-wp;
> @@ -485,7 +484,6 @@
>   };
>
>   &sdio0 {
> -	broken-cd;
>   	bus-width = <4>;
>   	disable-wp;
>   	non-removable;
> diff --git a/arch/arm/boot/dts/rk3288-popmetal.dts b/arch/arm/boot/dts/rk3288-popmetal.dts
> index 65c4756..cc2be77 100644
> --- a/arch/arm/boot/dts/rk3288-popmetal.dts
> +++ b/arch/arm/boot/dts/rk3288-popmetal.dts
> @@ -162,7 +162,6 @@
>   };
>
>   &emmc {
> -	broken-cd;
>   	bus-width = <8>;
>   	cap-mmc-highspeed;
>   	disable-wp;
> diff --git a/arch/arm/boot/dts/rk3288-veyron.dtsi b/arch/arm/boot/dts/rk3288-veyron.dtsi
> index 9fce91f..725e08b 100644
> --- a/arch/arm/boot/dts/rk3288-veyron.dtsi
> +++ b/arch/arm/boot/dts/rk3288-veyron.dtsi
> @@ -146,7 +146,6 @@
>   &emmc {
>   	status = "okay";
>
> -	broken-cd;
>   	bus-width = <8>;
>   	cap-mmc-highspeed;
>   	rockchip,default-sample-phase = <158>;
> @@ -352,7 +351,6 @@
>   &sdio0 {
>   	status = "okay";
>
> -	broken-cd;
>   	bus-width = <4>;
>   	cap-sd-highspeed;
>   	cap-sdio-irq;
>


-- 
Best Regards
Shawn Lin



From ykk at rock-chips.com  Mon Jan 25 18:28:03 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Tue, 26 Jan 2016 10:28:03 +0800
Subject: [PATCH v6] drm/rockchip: hdmi: add Innosilicon HDMI support
In-Reply-To: <1453769119.17181.37.camel@tiscali.nl>
References: <1453686197-20975-1-git-send-email-ykk@rock-chips.com>
 <1453686430-21470-1-git-send-email-ykk@rock-chips.com>
 <1453769119.17181.37.camel@tiscali.nl>
Message-ID: <56A6D9B3.4070006@rock-chips.com>

Hi Paul,

On 01/26/2016 08:45 AM, Paul Bolle wrote:
> On ma, 2016-01-25 at 09:47 +0800, Yakir Yang wrote:
>> --- a/drivers/gpu/drm/rockchip/Kconfig
>> +++ b/drivers/gpu/drm/rockchip/Kconfig
>> +config ROCKCHIP_INNO_HDMI
>> +	tristate "Rockchip specific extensions for Innosilicon HDMI"
>> +        depends on DRM_ROCKCHIP
>> +        help
>> +	  This selects support for Rockchip SoC specific extensions
>> +	  for the Innosilicon HDMI driver. If you want to enable
>> +	  HDMI on RK3036 based SoC, you should selet this option.
> (Really trivial: start indentation with tabs only, please. And
> s/selet/select/.)
Got it
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
>> + * This software is licensed under the terms of the GNU General
>> Public
>> + * License version 2, as published by the Free Software Foundation,
>> and
>> + * may be copied, distributed, and modified under those terms.
> This states this file is licensed GPL v2 only.
>
>> +MODULE_LICENSE("GPL");
> And, according to include/linux/module.h, this means "GNU Public License
> v2 or later".
>
> So I think there's a (subtle) mismatch between the license ident used
> for this driver and the comment above.

Ah, thanks.

- Yakir

> Thanks,
>
>
> Paul Bolle
>
>
>




From ykk at rock-chips.com  Mon Jan 25 18:34:40 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Tue, 26 Jan 2016 10:34:40 +0800
Subject: [PATCH v6 0/1] Introduce Innosilicon HDMI driver on Rockchip
 platforms
In-Reply-To: <30308769.0Ysu5UYYQv@diego>
References: <1453686197-20975-1-git-send-email-ykk@rock-chips.com>
 <30308769.0Ysu5UYYQv@diego>
Message-ID: <56A6DB40.9030209@rock-chips.com>

Hi Heiko,

On 01/25/2016 08:47 PM, Heiko St?bner wrote:
> Am Montag, 25. Januar 2016, 09:43:17 schrieb Yakir Yang:
>> Here are a brief introduction to Innosilicon HDMI IP:
>>    - Support HDMI 1.4a, HDCP 1.2 and DVI 1.0 standard compliant transmitter
>>    - Support HDMI1.4 a/b 3D function defined in HDMI 1.4 a/b spec
>>    - Digital video interface supports a pixel size of 24, 30, 36, 48bits
>> color depth in RGB - S/PDIF output supports PCM, Dolby Digital, DTS digital
>> audio transmission (32-192kHz Fs) using IEC60958 and IEC 61937
>>    - The EDID and CEC function are also supported by Innosilicon HDMI
>> Transmitter Controlle
> have you lost separate the devicetree-binding patch somewhere?
>
> I don't remember it getting applied and Mark may miss it if it's only
> contained in some old series.

Ops, I may misunderstand the rule, I thought if patch was acked by 
maintainer, then I should keep it no changed. Hmmm, so not Acked but 
Applied, thanks, I would update now.

Yakir
>
> Heiko
>
>
>




From shawn.lin at rock-chips.com  Mon Jan 25 18:34:14 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Tue, 26 Jan 2016 10:34:14 +0800
Subject: [PATCH] Documentation: rockchip-dw-mshc: add RK3036 dw-mshc
 description
Message-ID: <1453775654-16105-1-git-send-email-shawn.lin@rock-chips.com>

rk3036 dtsi file add dw-mshc compatible "rockchip,rk3036-dw-mshc"
but didn't add it into rockchip-dw-mshc.txt.

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
---

 Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt
index 634e249..ea5614b 100644
--- a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt
+++ b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt
@@ -13,6 +13,7 @@ Required Properties:
 	- "rockchip,rk2928-dw-mshc": for Rockchip RK2928 and following,
 							before RK3288
 	- "rockchip,rk3288-dw-mshc": for Rockchip RK3288
+	- "rockchip,rk3036-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK3036
 	- "rockchip,rk3368-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK3368
 
 Optional Properties:
-- 
2.3.7




From ykk at rock-chips.com  Mon Jan 25 18:59:02 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Tue, 26 Jan 2016 10:59:02 +0800
Subject: [PATCH v7 0/2] Add Rockchip Inno-HDMI driver
Message-ID: <1453777142-30119-1-git-send-email-ykk@rock-chips.com>


Here are a brief introduction to Innosilicon HDMI IP:
  - Support HDMI 1.4a, HDCP 1.2 and DVI 1.0 standard compliant transmitter
  - Support HDMI1.4 a/b 3D function defined in HDMI 1.4 a/b spec
  - Digital video interface supports a pixel size of 24, 30, 36, 48bits color depth in RGB
  - S/PDIF output supports PCM, Dolby Digital, DTS digital audio transmission
    (32-192kHz Fs) using IEC60958 and IEC 61937
  - The EDID and CEC function are also supported by Innosilicon HDMI Transmitter Controlle

Changes in v7:
- Correct the module licnese statement (Paul)
     - MODULE_LICENSE("GPL");
     + MODULE_LICENSE("GPL v2");
- Start indentation with tabs and fix the misspell in Kconfig (Paul)
- Carry the lost device-binding document (Heiko)

Changes in v6:
- Rebase the Makefile/Kconfig files which add by Chris's rockchip-mipi driver (Caeser)

Changes in v5:
- Use hdmi_infoframe helper functions to packed the infoframe (Russell)
- Remove the unused double wait_for_completion_timeout for ddc transfer (Russell)
- Remove the unused local variable in "inno_hdmi_i2c_write()" function (Russell)

Changes in v4:
- Modify the commit title "drm/rockchip: hdmi: ..." (Mark)
- Correct the "DKMS" to "DPMS" (Mark)
- Fix over 80 characters problems (Mark)
- Remove encoder .prepare/.commit helper functions, and move the vop mode
configure function into encoder .enable helper functions. (Mark)

Changes in v3:
- Use encoder enable/disable function, and remove the encoder DPMS function
- Keep HDMI PLL power on in standby mode

Changes in v2:
- Using DRM atomic helper functions for connector init (Mark)
- Remove "hdmi->connector.encoder = encoder;" (Mark)
- Add the Acked-by tags from Rob
- Correct the misspell "rk3036-dw-hdmi" (Heiko)

Yakir Yang (2):
  drm/rockchip: hdmi: add Innosilicon HDMI support
  dt-bindings: add document for Innosilicon HDMI on Rockchip platform

 .../display/rockchip/inno_hdmi-rockchip.txt        |  50 ++
 drivers/gpu/drm/rockchip/Kconfig                   |   8 +
 drivers/gpu/drm/rockchip/Makefile                  |   1 +
 drivers/gpu/drm/rockchip/inno_hdmi.c               | 941 +++++++++++++++++++++
 drivers/gpu/drm/rockchip/inno_hdmi.h               | 362 ++++++++
 5 files changed, 1362 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt
 create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.c
 create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.h

-- 
1.9.1




From ykk at rock-chips.com  Mon Jan 25 19:03:28 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Tue, 26 Jan 2016 11:03:28 +0800
Subject: [PATCH v7 1/2] drm/rockchip: hdmi: add Innosilicon HDMI support
In-Reply-To: <1453777142-30119-1-git-send-email-ykk@rock-chips.com>
References: <1453777142-30119-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1453777408-30263-1-git-send-email-ykk@rock-chips.com>

The Innosilicon HDMI is a low power HDMI 1.4 transmitter
IP, and it have been integrated on some rockchip CPUs
(like RK3036, RK312x).

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v7:
- Correct the module licnese statement (Paul)
     - MODULE_LICENSE("GPL");
     + MODULE_LICENSE("GPL v2");
- Start indentation with tabs and fix the misspell in Kconfig (Paul)
- Carry the lost device-binding document (Heiko)

Changes in v6:
- Rebase the Makefile/Kconfig files which add by Chris's rockchip-mipi driver (Caeser)

Changes in v5:
- Use hdmi_infoframe helper functions to packed the infoframe (Russell)
- Remove the unused double wait_for_completion_timeout for ddc transfer (Russell)
- Remove the unused local variable in "inno_hdmi_i2c_write()" function (Russell)

Changes in v4:
- Modify the commit title "drm/rockchip: hdmi: ..." (Mark)
- Correct the "DKMS" to "DPMS" (Mark)
- Fix over 80 characters problems (Mark)
- Remove encoder .prepare/.commit helper functions, and move the vop mode
configure function into encoder .enable helper functions. (Mark)

Changes in v3:
- Use encoder enable/disable function, and remove the encoder DPMS function
- Keep HDMI PLL power on in standby mode

Changes in v2:
- Using DRM atomic helper functions for connector init (Mark)
- Remove "hdmi->connector.encoder = encoder;" (Mark)

 drivers/gpu/drm/rockchip/Kconfig     |   8 +
 drivers/gpu/drm/rockchip/Makefile    |   1 +
 drivers/gpu/drm/rockchip/inno_hdmi.c | 941 +++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/inno_hdmi.h | 362 ++++++++++++++
 4 files changed, 1312 insertions(+)
 create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.c
 create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.h

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 8573985..76b3362 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -35,3 +35,11 @@ config ROCKCHIP_DW_MIPI_DSI
 	 for the Synopsys DesignWare HDMI driver. If you want to
 	 enable MIPI DSI on RK3288 based SoC, you should selet this
 	 option.
+
+config ROCKCHIP_INNO_HDMI
+	tristate "Rockchip specific extensions for Innosilicon HDMI"
+	depends on DRM_ROCKCHIP
+	help
+	  This selects support for Rockchip SoC specific extensions
+	  for the Innosilicon HDMI driver. If you want to enable
+	  HDMI on RK3036 based SoC, you should select this option.
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index f6a809a..df8fbef 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -8,5 +8,6 @@ rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
 
 obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
 obj-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
+obj-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
 
 obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_vop_reg.o
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
new file mode 100644
index 0000000..086c710
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -0,0 +1,941 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ *    Zheng Yang <zhengyang at rock-chips.com>
+ *    Yakir Yang <ykk at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/hdmi.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+
+#include <drm/drm_of.h>
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_encoder_slave.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_vop.h"
+
+#include "inno_hdmi.h"
+
+#define to_inno_hdmi(x)	container_of(x, struct inno_hdmi, x)
+
+struct hdmi_data_info {
+	int vic;
+	bool sink_is_hdmi;
+	bool sink_has_audio;
+	unsigned int enc_in_format;
+	unsigned int enc_out_format;
+	unsigned int colorimetry;
+};
+
+struct inno_hdmi_i2c {
+	struct i2c_adapter adap;
+
+	u8 ddc_addr;
+	u8 segment_addr;
+
+	struct mutex lock;
+	struct completion cmp;
+};
+
+struct inno_hdmi {
+	struct device *dev;
+	struct drm_device *drm_dev;
+
+	int irq;
+	struct clk *pclk;
+	void __iomem *regs;
+
+	struct drm_connector	connector;
+	struct drm_encoder	encoder;
+
+	struct inno_hdmi_i2c *i2c;
+	struct i2c_adapter *ddc;
+
+	unsigned int tmds_rate;
+
+	struct hdmi_data_info	hdmi_data;
+	struct drm_display_mode previous_mode;
+};
+
+enum {
+	CSC_ITU601_16_235_TO_RGB_0_255_8BIT,
+	CSC_ITU601_0_255_TO_RGB_0_255_8BIT,
+	CSC_ITU709_16_235_TO_RGB_0_255_8BIT,
+	CSC_RGB_0_255_TO_ITU601_16_235_8BIT,
+	CSC_RGB_0_255_TO_ITU709_16_235_8BIT,
+	CSC_RGB_0_255_TO_RGB_16_235_8BIT,
+};
+
+static const char coeff_csc[][24] = {
+	/*
+	 * YUV2RGB:601 SD mode(Y[16:235], UV[16:240], RGB[0:255]):
+	 *   R = 1.164*Y + 1.596*V - 204
+	 *   G = 1.164*Y - 0.391*U - 0.813*V + 154
+	 *   B = 1.164*Y + 2.018*U - 258
+	 */
+	{
+		0x04, 0xa7, 0x00, 0x00, 0x06, 0x62, 0x02, 0xcc,
+		0x04, 0xa7, 0x11, 0x90, 0x13, 0x40, 0x00, 0x9a,
+		0x04, 0xa7, 0x08, 0x12, 0x00, 0x00, 0x03, 0x02
+	},
+	/*
+	 * YUV2RGB:601 SD mode(YUV[0:255],RGB[0:255]):
+	 *   R = Y + 1.402*V - 248
+	 *   G = Y - 0.344*U - 0.714*V + 135
+	 *   B = Y + 1.772*U - 227
+	 */
+	{
+		0x04, 0x00, 0x00, 0x00, 0x05, 0x9b, 0x02, 0xf8,
+		0x04, 0x00, 0x11, 0x60, 0x12, 0xdb, 0x00, 0x87,
+		0x04, 0x00, 0x07, 0x16, 0x00, 0x00, 0x02, 0xe3
+	},
+	/*
+	 * YUV2RGB:709 HD mode(Y[16:235],UV[16:240],RGB[0:255]):
+	 *   R = 1.164*Y + 1.793*V - 248
+	 *   G = 1.164*Y - 0.213*U - 0.534*V + 77
+	 *   B = 1.164*Y + 2.115*U - 289
+	 */
+	{
+		0x04, 0xa7, 0x00, 0x00, 0x07, 0x2c, 0x02, 0xf8,
+		0x04, 0xa7, 0x10, 0xda, 0x12, 0x22, 0x00, 0x4d,
+		0x04, 0xa7, 0x08, 0x74, 0x00, 0x00, 0x03, 0x21
+	},
+
+	/*
+	 * RGB2YUV:601 SD mode:
+	 *   Cb = -0.291G - 0.148R + 0.439B + 128
+	 *   Y  = 0.504G  + 0.257R + 0.098B + 16
+	 *   Cr = -0.368G + 0.439R - 0.071B + 128
+	 */
+	{
+		0x11, 0x5f, 0x01, 0x82, 0x10, 0x23, 0x00, 0x80,
+		0x02, 0x1c, 0x00, 0xa1, 0x00, 0x36, 0x00, 0x1e,
+		0x11, 0x29, 0x10, 0x59, 0x01, 0x82, 0x00, 0x80
+	},
+	/*
+	 * RGB2YUV:709 HD mode:
+	 *   Cb = - 0.338G - 0.101R + 0.439B + 128
+	 *   Y  = 0.614G   + 0.183R + 0.062B + 16
+	 *   Cr = - 0.399G + 0.439R - 0.040B + 128
+	 */
+	{
+		0x11, 0x98, 0x01, 0xc1, 0x10, 0x28, 0x00, 0x80,
+		0x02, 0x74, 0x00, 0xbb, 0x00, 0x3f, 0x00, 0x10,
+		0x11, 0x5a, 0x10, 0x67, 0x01, 0xc1, 0x00, 0x80
+	},
+	/*
+	 * RGB[0:255]2RGB[16:235]:
+	 *   R' = R x (235-16)/255 + 16;
+	 *   G' = G x (235-16)/255 + 16;
+	 *   B' = B x (235-16)/255 + 16;
+	 */
+	{
+		0x00, 0x00, 0x03, 0x6F, 0x00, 0x00, 0x00, 0x10,
+		0x03, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+		0x00, 0x00, 0x00, 0x00, 0x03, 0x6F, 0x00, 0x10
+	},
+};
+
+static inline u8 hdmi_readb(struct inno_hdmi *hdmi, u16 offset)
+{
+	return readl_relaxed(hdmi->regs + (offset) * 0x04);
+}
+
+static inline void hdmi_writeb(struct inno_hdmi *hdmi, u16 offset, u32 val)
+{
+	writel_relaxed(val, hdmi->regs + (offset) * 0x04);
+}
+
+static inline void hdmi_modb(struct inno_hdmi *hdmi, u16 offset,
+			     u32 msk, u32 val)
+{
+	u8 temp = hdmi_readb(hdmi, offset) & ~msk;
+
+	temp |= val & msk;
+	hdmi_writeb(hdmi, offset, temp);
+}
+
+static void inno_hdmi_i2c_init(struct inno_hdmi *hdmi)
+{
+	int ddc_bus_freq;
+
+	ddc_bus_freq = (hdmi->tmds_rate >> 2) / HDMI_SCL_RATE;
+
+	hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
+	hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
+
+	/* Clear the EDID interrupt flag and mute the interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+}
+
+static void inno_hdmi_sys_power(struct inno_hdmi *hdmi, bool enable)
+{
+	if (enable)
+		hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON);
+	else
+		hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
+}
+
+static void inno_hdmi_set_pwr_mode(struct inno_hdmi *hdmi, int mode)
+{
+	switch (mode) {
+	case NORMAL:
+		inno_hdmi_sys_power(hdmi, false);
+
+		hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x6f);
+		hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0xbb);
+
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x14);
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x10);
+		hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x0f);
+		hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x01);
+
+		inno_hdmi_sys_power(hdmi, true);
+		break;
+
+	case LOWER_PWR:
+		inno_hdmi_sys_power(hdmi, false);
+		hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
+
+		break;
+
+	default:
+		dev_err(hdmi->dev, "Unknown power mode %d\n", mode);
+	}
+}
+
+static void inno_hdmi_reset(struct inno_hdmi *hdmi)
+{
+	u32 val;
+	u32 msk;
+
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_DIGITAL, v_NOT_RST_DIGITAL);
+	udelay(100);
+
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_ANALOG, v_NOT_RST_ANALOG);
+	udelay(100);
+
+	msk = m_REG_CLK_INV | m_REG_CLK_SOURCE | m_POWER | m_INT_POL;
+	val = v_REG_CLK_INV | v_REG_CLK_SOURCE_SYS | v_PWR_ON | v_INT_POL_HIGH;
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, msk, val);
+
+	inno_hdmi_set_pwr_mode(hdmi, NORMAL);
+}
+
+static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi, int setup_rc,
+				  union hdmi_infoframe *frame, u32 frame_index,
+				  u32 mask, u32 disable, u32 enable)
+{
+	if (mask)
+		hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, disable);
+
+	hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, frame_index);
+
+	if (setup_rc >= 0) {
+		u8 packed_frame[HDMI_MAXIMUM_INFO_FRAME_SIZE];
+		ssize_t rc, i;
+
+		rc = hdmi_infoframe_pack(frame, packed_frame,
+					 sizeof(packed_frame));
+		if (rc < 0)
+			return rc;
+
+		for (i = 0; i < rc; i++)
+			hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i,
+				    packed_frame[i]);
+
+		if (mask)
+			hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, enable);
+	}
+
+	return setup_rc;
+}
+
+static int inno_hdmi_config_video_vsi(struct inno_hdmi *hdmi,
+				      struct drm_display_mode *mode)
+{
+	union hdmi_infoframe frame;
+	int rc;
+
+	rc = drm_hdmi_vendor_infoframe_from_display_mode(&frame.vendor.hdmi,
+							 mode);
+
+	return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_VSI,
+		m_PACKET_VSI_EN, v_PACKET_VSI_EN(0), v_PACKET_VSI_EN(1));
+}
+
+static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
+				      struct drm_display_mode *mode)
+{
+	union hdmi_infoframe frame;
+	int rc;
+
+	rc = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, mode);
+
+	if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_RGB)
+		frame.avi.colorspace = HDMI_COLORSPACE_RGB;
+	else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV444)
+		frame.avi.colorspace = HDMI_COLORSPACE_YUV444;
+	else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV422)
+		frame.avi.colorspace = HDMI_COLORSPACE_YUV422;
+	else
+		frame.avi.colorspace = HDMI_COLORSPACE_RGB;
+
+	return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_AVI, 0, 0, 0);
+}
+
+static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
+{
+	struct hdmi_data_info *data = &hdmi->hdmi_data;
+	int c0_c2_change = 0;
+	int csc_enable = 0;
+	int csc_mode = 0;
+	int auto_csc = 0;
+	int value;
+	int i;
+
+	/* Input video mode is SDR RGB24bit, data enable signal from external */
+	hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL1, v_DE_EXTERNAL |
+		    v_VIDEO_INPUT_FORMAT(VIDEO_INPUT_SDR_RGB444));
+
+	/* Input color hardcode to RGB, and output color hardcode to RGB888 */
+	value = v_VIDEO_INPUT_BITS(VIDEO_INPUT_8BITS) |
+		v_VIDEO_OUTPUT_COLOR(0) |
+		v_VIDEO_INPUT_CSP(0);
+	hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL2, value);
+
+	if (data->enc_out_format == data->enc_out_format) {
+		if ((data->enc_in_format == HDMI_COLORSPACE_RGB) ||
+		    (data->enc_in_format >= HDMI_COLORSPACE_YUV444)) {
+			value = v_SOF_DISABLE | v_COLOR_DEPTH_NOT_INDICATED(1);
+			hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
+
+			hdmi_modb(hdmi, HDMI_VIDEO_CONTRL,
+				  m_VIDEO_AUTO_CSC | m_VIDEO_C0_C2_SWAP,
+				  v_VIDEO_AUTO_CSC(AUTO_CSC_DISABLE) |
+				  v_VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE));
+			return 0;
+		}
+	}
+
+	if (data->colorimetry == HDMI_COLORIMETRY_ITU_601) {
+		if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
+		    (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
+			csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT;
+			auto_csc = AUTO_CSC_DISABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = v_CSC_ENABLE;
+		} else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
+			   (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
+			csc_mode = CSC_ITU601_16_235_TO_RGB_0_255_8BIT;
+			auto_csc = AUTO_CSC_ENABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = v_CSC_DISABLE;
+		}
+	} else {
+		if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
+		    (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
+			csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT;
+			auto_csc = AUTO_CSC_DISABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = v_CSC_ENABLE;
+		} else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
+			   (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
+			csc_mode = CSC_ITU709_16_235_TO_RGB_0_255_8BIT;
+			auto_csc = AUTO_CSC_ENABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = v_CSC_DISABLE;
+		}
+	}
+
+	for (i = 0; i < 24; i++)
+		hdmi_writeb(hdmi, HDMI_VIDEO_CSC_COEF + i,
+			    coeff_csc[csc_mode][i]);
+
+	value = v_SOF_DISABLE | csc_enable | v_COLOR_DEPTH_NOT_INDICATED(1);
+	hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
+	hdmi_modb(hdmi, HDMI_VIDEO_CONTRL, m_VIDEO_AUTO_CSC |
+		  m_VIDEO_C0_C2_SWAP, v_VIDEO_AUTO_CSC(auto_csc) |
+		  v_VIDEO_C0_C2_SWAP(c0_c2_change));
+
+	return 0;
+}
+
+static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi,
+					 struct drm_display_mode *mode)
+{
+	int value;
+
+	/* Set detail external video timing polarity and interlace mode */
+	value = v_EXTERANL_VIDEO(1);
+	value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
+		 v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
+	value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
+		 v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
+	value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
+		 v_INETLACE(1) : v_INETLACE(0);
+	hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
+
+	/* Set detail external video timing */
+	value = mode->htotal;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
+
+	value = mode->htotal - mode->hdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
+
+	value = mode->hsync_start - mode->hdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
+
+	value = mode->hsync_end - mode->hsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
+
+	value = mode->vtotal;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
+
+	value = mode->vtotal - mode->vdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
+
+	value = mode->vsync_start - mode->vdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
+
+	value = mode->vsync_end - mode->vsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
+
+	hdmi_writeb(hdmi, HDMI_PHY_PRE_DIV_RATIO, 0x1e);
+	hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_LOW, 0x2c);
+	hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH, 0x01);
+
+	return 0;
+}
+
+static int inno_hdmi_setup(struct inno_hdmi *hdmi,
+			   struct drm_display_mode *mode)
+{
+	hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
+
+	hdmi->hdmi_data.enc_in_format = HDMI_COLORSPACE_RGB;
+	hdmi->hdmi_data.enc_out_format = HDMI_COLORSPACE_RGB;
+
+	if ((hdmi->hdmi_data.vic == 6) || (hdmi->hdmi_data.vic == 7) ||
+	    (hdmi->hdmi_data.vic == 21) || (hdmi->hdmi_data.vic == 22) ||
+	    (hdmi->hdmi_data.vic == 2) || (hdmi->hdmi_data.vic == 3) ||
+	    (hdmi->hdmi_data.vic == 17) || (hdmi->hdmi_data.vic == 18))
+		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601;
+	else
+		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
+
+	/* Mute video and audio output */
+	hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
+		  v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1));
+
+	/* Set HDMI Mode */
+	hdmi_writeb(hdmi, HDMI_HDCP_CTRL,
+		    v_HDMI_DVI(hdmi->hdmi_data.sink_is_hdmi));
+
+	inno_hdmi_config_video_timing(hdmi, mode);
+
+	inno_hdmi_config_video_csc(hdmi);
+
+	if (hdmi->hdmi_data.sink_is_hdmi) {
+		inno_hdmi_config_video_avi(hdmi, mode);
+		inno_hdmi_config_video_vsi(hdmi, mode);
+	}
+
+	/*
+	 * When IP controller have configured to an accurate video
+	 * timing, then the TMDS clock source would be switched to
+	 * DCLK_LCDC, so we need to init the TMDS rate to mode pixel
+	 * clock rate, and reconfigure the DDC clock.
+	 */
+	hdmi->tmds_rate = mode->clock * 1000;
+	inno_hdmi_i2c_init(hdmi);
+
+	/* Unmute video and audio output */
+	hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
+		  v_AUDIO_MUTE(0) | v_VIDEO_MUTE(0));
+
+	return 0;
+}
+
+static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder,
+				       struct drm_display_mode *mode,
+				       struct drm_display_mode *adj_mode)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+
+	inno_hdmi_setup(hdmi, adj_mode);
+
+	/* Store the display mode for plugin/DPMS poweron events */
+	memcpy(&hdmi->previous_mode, adj_mode, sizeof(hdmi->previous_mode));
+}
+
+static void inno_hdmi_encoder_enable(struct drm_encoder *encoder)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+
+	rockchip_drm_crtc_mode_config(encoder->crtc, DRM_MODE_CONNECTOR_HDMIA,
+				      ROCKCHIP_OUT_MODE_P888);
+
+	inno_hdmi_set_pwr_mode(hdmi, NORMAL);
+}
+
+static void inno_hdmi_encoder_disable(struct drm_encoder *encoder)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+
+	inno_hdmi_set_pwr_mode(hdmi, LOWER_PWR);
+}
+
+static bool inno_hdmi_encoder_mode_fixup(struct drm_encoder *encoder,
+					 const struct drm_display_mode *mode,
+					 struct drm_display_mode *adj_mode)
+{
+	return true;
+}
+
+static struct drm_encoder_helper_funcs inno_hdmi_encoder_helper_funcs = {
+	.enable     = inno_hdmi_encoder_enable,
+	.disable    = inno_hdmi_encoder_disable,
+	.mode_fixup = inno_hdmi_encoder_mode_fixup,
+	.mode_set   = inno_hdmi_encoder_mode_set,
+};
+
+static struct drm_encoder_funcs inno_hdmi_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static enum drm_connector_status
+inno_hdmi_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+
+	return (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
+		connector_status_connected : connector_status_disconnected;
+}
+
+static int inno_hdmi_connector_get_modes(struct drm_connector *connector)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+	struct edid *edid;
+	int ret = 0;
+
+	if (!hdmi->ddc)
+		return 0;
+
+	edid = drm_get_edid(connector, hdmi->ddc);
+	if (edid) {
+		hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
+		hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
+		drm_mode_connector_update_edid_property(connector, edid);
+		ret = drm_add_edid_modes(connector, edid);
+		kfree(edid);
+	}
+
+	return ret;
+}
+
+static enum drm_mode_status
+inno_hdmi_connector_mode_valid(struct drm_connector *connector,
+			       struct drm_display_mode *mode)
+{
+	return MODE_OK;
+}
+
+static struct drm_encoder *
+inno_hdmi_connector_best_encoder(struct drm_connector *connector)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+
+	return &hdmi->encoder;
+}
+
+static int
+inno_hdmi_probe_single_connector_modes(struct drm_connector *connector,
+				       uint32_t maxX, uint32_t maxY)
+{
+	return drm_helper_probe_single_connector_modes(connector, 1920, 1080);
+}
+
+static void inno_hdmi_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs inno_hdmi_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.fill_modes = inno_hdmi_probe_single_connector_modes,
+	.detect = inno_hdmi_connector_detect,
+	.destroy = inno_hdmi_connector_destroy,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static struct drm_connector_helper_funcs inno_hdmi_connector_helper_funcs = {
+	.get_modes = inno_hdmi_connector_get_modes,
+	.mode_valid = inno_hdmi_connector_mode_valid,
+	.best_encoder = inno_hdmi_connector_best_encoder,
+};
+
+static int inno_hdmi_register(struct drm_device *drm, struct inno_hdmi *hdmi)
+{
+	struct drm_encoder *encoder = &hdmi->encoder;
+	struct device *dev = hdmi->dev;
+
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
+
+	/*
+	 * If we failed to find the CRTC(s) which this encoder is
+	 * supposed to be connected to, it's because the CRTC has
+	 * not been registered yet.  Defer probing, and hope that
+	 * the required CRTC is added later.
+	 */
+	if (encoder->possible_crtcs == 0)
+		return -EPROBE_DEFER;
+
+	drm_encoder_helper_add(encoder, &inno_hdmi_encoder_helper_funcs);
+	drm_encoder_init(drm, encoder, &inno_hdmi_encoder_funcs,
+			 DRM_MODE_ENCODER_TMDS, NULL);
+
+	hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
+
+	drm_connector_helper_add(&hdmi->connector,
+				 &inno_hdmi_connector_helper_funcs);
+	drm_connector_init(drm, &hdmi->connector, &inno_hdmi_connector_funcs,
+			   DRM_MODE_CONNECTOR_HDMIA);
+
+	drm_mode_connector_attach_encoder(&hdmi->connector, encoder);
+
+	return 0;
+}
+
+static irqreturn_t inno_hdmi_i2c_irq(struct inno_hdmi *hdmi)
+{
+	struct inno_hdmi_i2c *i2c = hdmi->i2c;
+	u8 stat;
+
+	stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
+	if (!(stat & m_INT_EDID_READY))
+		return IRQ_NONE;
+
+	/* Clear HDMI EDID interrupt flag */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+
+	complete(&i2c->cmp);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t inno_hdmi_hardirq(int irq, void *dev_id)
+{
+	struct inno_hdmi *hdmi = dev_id;
+	irqreturn_t ret = IRQ_NONE;
+	u8 interrupt;
+
+	if (hdmi->i2c)
+		ret = inno_hdmi_i2c_irq(hdmi);
+
+	interrupt = hdmi_readb(hdmi, HDMI_STATUS);
+	if (interrupt & m_INT_HOTPLUG) {
+		hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG);
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	return ret;
+}
+
+static irqreturn_t inno_hdmi_irq(int irq, void *dev_id)
+{
+	struct inno_hdmi *hdmi = dev_id;
+
+	drm_helper_hpd_irq_event(hdmi->connector.dev);
+
+	return IRQ_HANDLED;
+}
+
+static int inno_hdmi_i2c_read(struct inno_hdmi *hdmi, struct i2c_msg *msgs)
+{
+	int length = msgs->len;
+	u8 *buf = msgs->buf;
+	int ret;
+
+	ret = wait_for_completion_timeout(&hdmi->i2c->cmp, HZ / 10);
+	if (!ret)
+		return -EAGAIN;
+
+	while (length--)
+		*buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
+
+	return 0;
+}
+
+static int inno_hdmi_i2c_write(struct inno_hdmi *hdmi, struct i2c_msg *msgs)
+{
+	/*
+	 * The DDC module only support read EDID message, so
+	 * we assume that each word write to this i2c adapter
+	 * should be the offset of EDID word address.
+	 */
+	if ((msgs->len != 1) ||
+	    ((msgs->addr != DDC_ADDR) && (msgs->addr != DDC_SEGMENT_ADDR)))
+		return -EINVAL;
+
+	reinit_completion(&hdmi->i2c->cmp);
+
+	if (msgs->addr == DDC_SEGMENT_ADDR)
+		hdmi->i2c->segment_addr = msgs->buf[0];
+	if (msgs->addr == DDC_ADDR)
+		hdmi->i2c->ddc_addr = msgs->buf[0];
+
+	/* Set edid fifo first addr */
+	hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
+
+	/* Set edid word address 0x00/0x80 */
+	hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
+
+	/* Set edid segment pointer */
+	hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr);
+
+	return 0;
+}
+
+static int inno_hdmi_i2c_xfer(struct i2c_adapter *adap,
+			      struct i2c_msg *msgs, int num)
+{
+	struct inno_hdmi *hdmi = i2c_get_adapdata(adap);
+	struct inno_hdmi_i2c *i2c = hdmi->i2c;
+	int i, ret = 0;
+
+	mutex_lock(&i2c->lock);
+
+	/* Clear the EDID interrupt flag and unmute the interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY);
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+
+	for (i = 0; i < num; i++) {
+		dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n",
+			i + 1, num, msgs[i].len, msgs[i].flags);
+
+		if (msgs[i].flags & I2C_M_RD)
+			ret = inno_hdmi_i2c_read(hdmi, &msgs[i]);
+		else
+			ret = inno_hdmi_i2c_write(hdmi, &msgs[i]);
+
+		if (ret < 0)
+			break;
+	}
+
+	if (!ret)
+		ret = num;
+
+	/* Mute HDMI EDID interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
+
+	mutex_unlock(&i2c->lock);
+
+	return ret;
+}
+
+static u32 inno_hdmi_i2c_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm inno_hdmi_algorithm = {
+	.master_xfer	= inno_hdmi_i2c_xfer,
+	.functionality	= inno_hdmi_i2c_func,
+};
+
+static struct i2c_adapter *inno_hdmi_i2c_adapter(struct inno_hdmi *hdmi)
+{
+	struct i2c_adapter *adap;
+	struct inno_hdmi_i2c *i2c;
+	int ret;
+
+	i2c = devm_kzalloc(hdmi->dev, sizeof(*i2c), GFP_KERNEL);
+	if (!i2c)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_init(&i2c->lock);
+	init_completion(&i2c->cmp);
+
+	adap = &i2c->adap;
+	adap->class = I2C_CLASS_DDC;
+	adap->owner = THIS_MODULE;
+	adap->dev.parent = hdmi->dev;
+	adap->dev.of_node = hdmi->dev->of_node;
+	adap->algo = &inno_hdmi_algorithm;
+	strlcpy(adap->name, "Inno HDMI", sizeof(adap->name));
+	i2c_set_adapdata(adap, hdmi);
+
+	ret = i2c_add_adapter(adap);
+	if (ret) {
+		dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name);
+		devm_kfree(hdmi->dev, i2c);
+		return ERR_PTR(ret);
+	}
+
+	hdmi->i2c = i2c;
+
+	dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name);
+
+	return adap;
+}
+
+static int inno_hdmi_bind(struct device *dev, struct device *master,
+				 void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = data;
+	struct inno_hdmi *hdmi;
+	struct resource *iores;
+	int irq;
+	int ret;
+
+	hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
+	if (!hdmi)
+		return -ENOMEM;
+
+	hdmi->dev = dev;
+	hdmi->drm_dev = drm;
+
+	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!iores)
+		return -ENXIO;
+
+	hdmi->regs = devm_ioremap_resource(dev, iores);
+	if (IS_ERR(hdmi->regs))
+		return PTR_ERR(hdmi->regs);
+
+	hdmi->pclk = devm_clk_get(hdmi->dev, "pclk");
+	if (IS_ERR(hdmi->pclk)) {
+		dev_err(hdmi->dev, "Unable to get HDMI pclk clk\n");
+		return PTR_ERR(hdmi->pclk);
+	}
+
+	ret = clk_prepare_enable(hdmi->pclk);
+	if (ret) {
+		dev_err(hdmi->dev, "Cannot enable HDMI pclk clock: %d\n", ret);
+		return ret;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	inno_hdmi_reset(hdmi);
+
+	hdmi->ddc = inno_hdmi_i2c_adapter(hdmi);
+	if (IS_ERR(hdmi->ddc)) {
+		hdmi->ddc = NULL;
+		return PTR_ERR(hdmi->ddc);
+	}
+
+	/*
+	 * When IP controller haven't configured to an accurate video
+	 * timing, then the TMDS clock source would be switched to
+	 * PCLK_HDMI, so we need to init the TMDS rate to PCLK rate,
+	 * and reconfigure the DDC clock.
+	 */
+	hdmi->tmds_rate = clk_get_rate(hdmi->pclk);
+	inno_hdmi_i2c_init(hdmi);
+
+	ret = inno_hdmi_register(drm, hdmi);
+	if (ret)
+		return ret;
+
+	dev_set_drvdata(dev, hdmi);
+
+	/* Unmute hotplug interrupt */
+	hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
+
+	ret = devm_request_threaded_irq(dev, irq, inno_hdmi_hardirq,
+					inno_hdmi_irq, IRQF_SHARED,
+					dev_name(dev), hdmi);
+
+	return ret;
+}
+
+static void inno_hdmi_unbind(struct device *dev, struct device *master,
+			     void *data)
+{
+	struct inno_hdmi *hdmi = dev_get_drvdata(dev);
+
+	hdmi->connector.funcs->destroy(&hdmi->connector);
+	hdmi->encoder.funcs->destroy(&hdmi->encoder);
+
+	clk_disable_unprepare(hdmi->pclk);
+	i2c_put_adapter(hdmi->ddc);
+}
+
+static const struct component_ops inno_hdmi_ops = {
+	.bind	= inno_hdmi_bind,
+	.unbind	= inno_hdmi_unbind,
+};
+
+static int inno_hdmi_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &inno_hdmi_ops);
+}
+
+static int inno_hdmi_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &inno_hdmi_ops);
+
+	return 0;
+}
+
+static const struct of_device_id inno_hdmi_dt_ids[] = {
+	{ .compatible = "rockchip,rk3036-inno-hdmi",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, inno_hdmi_dt_ids);
+
+static struct platform_driver inno_hdmi_driver = {
+	.probe  = inno_hdmi_probe,
+	.remove = inno_hdmi_remove,
+	.driver = {
+		.name = "innohdmi-rockchip",
+		.of_match_table = inno_hdmi_dt_ids,
+	},
+};
+
+module_platform_driver(inno_hdmi_driver);
+
+MODULE_AUTHOR("Zheng Yang <zhengyang at rock-chips.com>");
+MODULE_AUTHOR("Yakir Yang <ykk at rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip Specific INNO-HDMI Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:innohdmi-rockchip");
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.h b/drivers/gpu/drm/rockchip/inno_hdmi.h
new file mode 100644
index 0000000..aa7c415
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.h
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ *    Zheng Yang <zhengyang at rock-chips.com>
+ *    Yakir Yang <ykk at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __INNO_HDMI_H__
+#define __INNO_HDMI_H__
+
+#define DDC_SEGMENT_ADDR		0x30
+
+enum PWR_MODE {
+	NORMAL,
+	LOWER_PWR,
+};
+
+#define HDMI_SCL_RATE			(100*1000)
+#define DDC_BUS_FREQ_L			0x4b
+#define DDC_BUS_FREQ_H			0x4c
+
+#define HDMI_SYS_CTRL			0x00
+#define m_RST_ANALOG			(1 << 6)
+#define v_RST_ANALOG			(0 << 6)
+#define v_NOT_RST_ANALOG		(1 << 6)
+#define m_RST_DIGITAL			(1 << 5)
+#define v_RST_DIGITAL			(0 << 5)
+#define v_NOT_RST_DIGITAL		(1 << 5)
+#define m_REG_CLK_INV			(1 << 4)
+#define v_REG_CLK_NOT_INV		(0 << 4)
+#define v_REG_CLK_INV			(1 << 4)
+#define m_VCLK_INV			(1 << 3)
+#define v_VCLK_NOT_INV			(0 << 3)
+#define v_VCLK_INV			(1 << 3)
+#define m_REG_CLK_SOURCE		(1 << 2)
+#define v_REG_CLK_SOURCE_TMDS		(0 << 2)
+#define v_REG_CLK_SOURCE_SYS		(1 << 2)
+#define m_POWER				(1 << 1)
+#define v_PWR_ON			(0 << 1)
+#define v_PWR_OFF			(1 << 1)
+#define m_INT_POL			(1 << 0)
+#define v_INT_POL_HIGH			1
+#define v_INT_POL_LOW			0
+
+#define HDMI_VIDEO_CONTRL1		0x01
+#define m_VIDEO_INPUT_FORMAT		(7 << 1)
+#define m_DE_SOURCE			(1 << 0)
+#define v_VIDEO_INPUT_FORMAT(n)		(n << 1)
+#define v_DE_EXTERNAL			1
+#define v_DE_INTERNAL			0
+enum {
+	VIDEO_INPUT_SDR_RGB444 = 0,
+	VIDEO_INPUT_DDR_RGB444 = 5,
+	VIDEO_INPUT_DDR_YCBCR422 = 6
+};
+
+#define HDMI_VIDEO_CONTRL2		0x02
+#define m_VIDEO_OUTPUT_COLOR		(3 << 6)
+#define m_VIDEO_INPUT_BITS		(3 << 4)
+#define m_VIDEO_INPUT_CSP		(1 << 0)
+#define v_VIDEO_OUTPUT_COLOR(n)		(((n) & 0x3) << 6)
+#define v_VIDEO_INPUT_BITS(n)		(n << 4)
+#define v_VIDEO_INPUT_CSP(n)		(n << 0)
+enum {
+	VIDEO_INPUT_12BITS = 0,
+	VIDEO_INPUT_10BITS = 1,
+	VIDEO_INPUT_REVERT = 2,
+	VIDEO_INPUT_8BITS = 3,
+};
+
+#define HDMI_VIDEO_CONTRL		0x03
+#define m_VIDEO_AUTO_CSC		(1 << 7)
+#define v_VIDEO_AUTO_CSC(n)		(n << 7)
+#define m_VIDEO_C0_C2_SWAP		(1 << 0)
+#define v_VIDEO_C0_C2_SWAP(n)		(n << 0)
+enum {
+	C0_C2_CHANGE_ENABLE = 0,
+	C0_C2_CHANGE_DISABLE = 1,
+	AUTO_CSC_DISABLE = 0,
+	AUTO_CSC_ENABLE = 1,
+};
+
+#define HDMI_VIDEO_CONTRL3		0x04
+#define m_COLOR_DEPTH_NOT_INDICATED	(1 << 4)
+#define m_SOF				(1 << 3)
+#define m_COLOR_RANGE			(1 << 2)
+#define m_CSC				(1 << 0)
+#define v_COLOR_DEPTH_NOT_INDICATED(n)	((n) << 4)
+#define v_SOF_ENABLE			(0 << 3)
+#define v_SOF_DISABLE			(1 << 3)
+#define v_COLOR_RANGE_FULL		(1 << 2)
+#define v_COLOR_RANGE_LIMITED		(0 << 2)
+#define v_CSC_ENABLE			1
+#define v_CSC_DISABLE			0
+
+#define HDMI_AV_MUTE			0x05
+#define m_AVMUTE_CLEAR			(1 << 7)
+#define m_AVMUTE_ENABLE			(1 << 6)
+#define m_AUDIO_MUTE			(1 << 1)
+#define m_VIDEO_BLACK			(1 << 0)
+#define v_AVMUTE_CLEAR(n)		(n << 7)
+#define v_AVMUTE_ENABLE(n)		(n << 6)
+#define v_AUDIO_MUTE(n)			(n << 1)
+#define v_VIDEO_MUTE(n)			(n << 0)
+
+#define HDMI_VIDEO_TIMING_CTL		0x08
+#define v_HSYNC_POLARITY(n)		(n << 3)
+#define v_VSYNC_POLARITY(n)		(n << 2)
+#define v_INETLACE(n)			(n << 1)
+#define v_EXTERANL_VIDEO(n)		(n << 0)
+
+#define HDMI_VIDEO_EXT_HTOTAL_L		0x09
+#define HDMI_VIDEO_EXT_HTOTAL_H		0x0a
+#define HDMI_VIDEO_EXT_HBLANK_L		0x0b
+#define HDMI_VIDEO_EXT_HBLANK_H		0x0c
+#define HDMI_VIDEO_EXT_HDELAY_L		0x0d
+#define HDMI_VIDEO_EXT_HDELAY_H		0x0e
+#define HDMI_VIDEO_EXT_HDURATION_L	0x0f
+#define HDMI_VIDEO_EXT_HDURATION_H	0x10
+#define HDMI_VIDEO_EXT_VTOTAL_L		0x11
+#define HDMI_VIDEO_EXT_VTOTAL_H		0x12
+#define HDMI_VIDEO_EXT_VBLANK		0x13
+#define HDMI_VIDEO_EXT_VDELAY		0x14
+#define HDMI_VIDEO_EXT_VDURATION	0x15
+
+#define HDMI_VIDEO_CSC_COEF		0x18
+
+#define HDMI_AUDIO_CTRL1		0x35
+enum {
+	CTS_SOURCE_INTERNAL = 0,
+	CTS_SOURCE_EXTERNAL = 1,
+};
+#define v_CTS_SOURCE(n)			(n << 7)
+
+enum {
+	DOWNSAMPLE_DISABLE = 0,
+	DOWNSAMPLE_1_2 = 1,
+	DOWNSAMPLE_1_4 = 2,
+};
+#define v_DOWN_SAMPLE(n)		(n << 5)
+
+enum {
+	AUDIO_SOURCE_IIS = 0,
+	AUDIO_SOURCE_SPDIF = 1,
+};
+#define v_AUDIO_SOURCE(n)		(n << 3)
+
+#define v_MCLK_ENABLE(n)		(n << 2)
+enum {
+	MCLK_128FS = 0,
+	MCLK_256FS = 1,
+	MCLK_384FS = 2,
+	MCLK_512FS = 3,
+};
+#define v_MCLK_RATIO(n)			(n)
+
+#define AUDIO_SAMPLE_RATE		0x37
+enum {
+	AUDIO_32K = 0x3,
+	AUDIO_441K = 0x0,
+	AUDIO_48K = 0x2,
+	AUDIO_882K = 0x8,
+	AUDIO_96K = 0xa,
+	AUDIO_1764K = 0xc,
+	AUDIO_192K = 0xe,
+};
+
+#define AUDIO_I2S_MODE			0x38
+enum {
+	I2S_CHANNEL_1_2 = 1,
+	I2S_CHANNEL_3_4 = 3,
+	I2S_CHANNEL_5_6 = 7,
+	I2S_CHANNEL_7_8 = 0xf
+};
+#define v_I2S_CHANNEL(n)		((n) << 2)
+enum {
+	I2S_STANDARD = 0,
+	I2S_LEFT_JUSTIFIED = 1,
+	I2S_RIGHT_JUSTIFIED = 2,
+};
+#define v_I2S_MODE(n)			(n)
+
+#define AUDIO_I2S_MAP			0x39
+#define AUDIO_I2S_SWAPS_SPDIF		0x3a
+#define v_SPIDF_FREQ(n)			(n)
+
+#define N_32K				0x1000
+#define N_441K				0x1880
+#define N_882K				0x3100
+#define N_1764K				0x6200
+#define N_48K				0x1800
+#define N_96K				0x3000
+#define N_192K				0x6000
+
+#define HDMI_AUDIO_CHANNEL_STATUS	0x3e
+#define m_AUDIO_STATUS_NLPCM		(1 << 7)
+#define m_AUDIO_STATUS_USE		(1 << 6)
+#define m_AUDIO_STATUS_COPYRIGHT	(1 << 5)
+#define m_AUDIO_STATUS_ADDITION		(3 << 2)
+#define m_AUDIO_STATUS_CLK_ACCURACY	(2 << 0)
+#define v_AUDIO_STATUS_NLPCM(n)		((n & 1) << 7)
+#define AUDIO_N_H			0x3f
+#define AUDIO_N_M			0x40
+#define AUDIO_N_L			0x41
+
+#define HDMI_AUDIO_CTS_H		0x45
+#define HDMI_AUDIO_CTS_M		0x46
+#define HDMI_AUDIO_CTS_L		0x47
+
+#define HDMI_DDC_CLK_L			0x4b
+#define HDMI_DDC_CLK_H			0x4c
+
+#define HDMI_EDID_SEGMENT_POINTER	0x4d
+#define HDMI_EDID_WORD_ADDR		0x4e
+#define HDMI_EDID_FIFO_OFFSET		0x4f
+#define HDMI_EDID_FIFO_ADDR		0x50
+
+#define HDMI_PACKET_SEND_MANUAL		0x9c
+#define HDMI_PACKET_SEND_AUTO		0x9d
+#define m_PACKET_GCP_EN			(1 << 7)
+#define m_PACKET_MSI_EN			(1 << 6)
+#define m_PACKET_SDI_EN			(1 << 5)
+#define m_PACKET_VSI_EN			(1 << 4)
+#define v_PACKET_GCP_EN(n)		((n & 1) << 7)
+#define v_PACKET_MSI_EN(n)		((n & 1) << 6)
+#define v_PACKET_SDI_EN(n)		((n & 1) << 5)
+#define v_PACKET_VSI_EN(n)		((n & 1) << 4)
+
+#define HDMI_CONTROL_PACKET_BUF_INDEX	0x9f
+enum {
+	INFOFRAME_VSI = 0x05,
+	INFOFRAME_AVI = 0x06,
+	INFOFRAME_AAI = 0x08,
+};
+
+#define HDMI_CONTROL_PACKET_ADDR	0xa0
+#define HDMI_MAXIMUM_INFO_FRAME_SIZE	0x11
+enum {
+	AVI_COLOR_MODE_RGB = 0,
+	AVI_COLOR_MODE_YCBCR422 = 1,
+	AVI_COLOR_MODE_YCBCR444 = 2,
+	AVI_COLORIMETRY_NO_DATA = 0,
+
+	AVI_COLORIMETRY_SMPTE_170M = 1,
+	AVI_COLORIMETRY_ITU709 = 2,
+	AVI_COLORIMETRY_EXTENDED = 3,
+
+	AVI_CODED_FRAME_ASPECT_NO_DATA = 0,
+	AVI_CODED_FRAME_ASPECT_4_3 = 1,
+	AVI_CODED_FRAME_ASPECT_16_9 = 2,
+
+	ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME = 0x08,
+	ACTIVE_ASPECT_RATE_4_3 = 0x09,
+	ACTIVE_ASPECT_RATE_16_9 = 0x0A,
+	ACTIVE_ASPECT_RATE_14_9 = 0x0B,
+};
+
+#define HDMI_HDCP_CTRL			0x52
+#define m_HDMI_DVI			(1 << 1)
+#define v_HDMI_DVI(n)			(n << 1)
+
+#define HDMI_INTERRUPT_MASK1		0xc0
+#define HDMI_INTERRUPT_STATUS1		0xc1
+#define	m_INT_ACTIVE_VSYNC		(1 << 5)
+#define m_INT_EDID_READY		(1 << 2)
+
+#define HDMI_INTERRUPT_MASK2		0xc2
+#define HDMI_INTERRUPT_STATUS2		0xc3
+#define m_INT_HDCP_ERR			(1 << 7)
+#define m_INT_BKSV_FLAG			(1 << 6)
+#define m_INT_HDCP_OK			(1 << 4)
+
+#define HDMI_STATUS			0xc8
+#define m_HOTPLUG			(1 << 7)
+#define m_MASK_INT_HOTPLUG		(1 << 5)
+#define m_INT_HOTPLUG			(1 << 1)
+#define v_MASK_INT_HOTPLUG(n)		((n & 0x1) << 5)
+
+#define HDMI_COLORBAR                   0xc9
+
+#define HDMI_PHY_SYNC			0xce
+#define HDMI_PHY_SYS_CTL		0xe0
+#define m_TMDS_CLK_SOURCE		(1 << 5)
+#define v_TMDS_FROM_PLL			(0 << 5)
+#define v_TMDS_FROM_GEN			(1 << 5)
+#define m_PHASE_CLK			(1 << 4)
+#define v_DEFAULT_PHASE			(0 << 4)
+#define v_SYNC_PHASE			(1 << 4)
+#define m_TMDS_CURRENT_PWR		(1 << 3)
+#define v_TURN_ON_CURRENT		(0 << 3)
+#define v_CAT_OFF_CURRENT		(1 << 3)
+#define m_BANDGAP_PWR			(1 << 2)
+#define v_BANDGAP_PWR_UP		(0 << 2)
+#define v_BANDGAP_PWR_DOWN		(1 << 2)
+#define m_PLL_PWR			(1 << 1)
+#define v_PLL_PWR_UP			(0 << 1)
+#define v_PLL_PWR_DOWN			(1 << 1)
+#define m_TMDS_CHG_PWR			(1 << 0)
+#define v_TMDS_CHG_PWR_UP		(0 << 0)
+#define v_TMDS_CHG_PWR_DOWN		(1 << 0)
+
+#define HDMI_PHY_CHG_PWR		0xe1
+#define v_CLK_CHG_PWR(n)		((n & 1) << 3)
+#define v_DATA_CHG_PWR(n)		((n & 7) << 0)
+
+#define HDMI_PHY_DRIVER			0xe2
+#define v_CLK_MAIN_DRIVER(n)		(n << 4)
+#define v_DATA_MAIN_DRIVER(n)		(n << 0)
+
+#define HDMI_PHY_PRE_EMPHASIS		0xe3
+#define v_PRE_EMPHASIS(n)		((n & 7) << 4)
+#define v_CLK_PRE_DRIVER(n)		((n & 3) << 2)
+#define v_DATA_PRE_DRIVER(n)		((n & 3) << 0)
+
+#define HDMI_PHY_FEEDBACK_DIV_RATIO_LOW		0xe7
+#define v_FEEDBACK_DIV_LOW(n)			(n & 0xff)
+#define HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH	0xe8
+#define v_FEEDBACK_DIV_HIGH(n)			(n & 1)
+
+#define HDMI_PHY_PRE_DIV_RATIO		0xed
+#define v_PRE_DIV_RATIO(n)		(n & 0x1f)
+
+#define HDMI_CEC_CTRL			0xd0
+#define m_ADJUST_FOR_HISENSE		(1 << 6)
+#define m_REJECT_RX_BROADCAST		(1 << 5)
+#define m_BUSFREETIME_ENABLE		(1 << 2)
+#define m_REJECT_RX			(1 << 1)
+#define m_START_TX			(1 << 0)
+
+#define HDMI_CEC_DATA			0xd1
+#define HDMI_CEC_TX_OFFSET		0xd2
+#define HDMI_CEC_RX_OFFSET		0xd3
+#define HDMI_CEC_CLK_H			0xd4
+#define HDMI_CEC_CLK_L			0xd5
+#define HDMI_CEC_TX_LENGTH		0xd6
+#define HDMI_CEC_RX_LENGTH		0xd7
+#define HDMI_CEC_TX_INT_MASK		0xd8
+#define m_TX_DONE			(1 << 3)
+#define m_TX_NOACK			(1 << 2)
+#define m_TX_BROADCAST_REJ		(1 << 1)
+#define m_TX_BUSNOTFREE			(1 << 0)
+
+#define HDMI_CEC_RX_INT_MASK		0xd9
+#define m_RX_LA_ERR			(1 << 4)
+#define m_RX_GLITCH			(1 << 3)
+#define m_RX_DONE			(1 << 0)
+
+#define HDMI_CEC_TX_INT			0xda
+#define HDMI_CEC_RX_INT			0xdb
+#define HDMI_CEC_BUSFREETIME_L		0xdc
+#define HDMI_CEC_BUSFREETIME_H		0xdd
+#define HDMI_CEC_LOGICADDR		0xde
+
+#endif /* __INNO_HDMI_H__ */
-- 
1.9.1




From ykk at rock-chips.com  Mon Jan 25 19:08:57 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Tue, 26 Jan 2016 11:08:57 +0800
Subject: [PATCH v7 2/2] dt-bindings: add document for Innosilicon HDMI on
 Rockchip platform
In-Reply-To: <1453777142-30119-1-git-send-email-ykk@rock-chips.com>
References: <1453777142-30119-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1453777737-1386-1-git-send-email-ykk@rock-chips.com>

Signed-off-by: Yakir Yang <ykk at rock-chips.com>

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Acked-by: Rob Herring <robh at kernel.org>
---
Changes in v7: None
Changes in v6: None
Changes in v5: None
Changes in v4: None
Changes in v3: None
Changes in v2:
- Add the Acked-by tags from Rob
- Correct the misspell "rk3036-dw-hdmi" (Heiko)

 .../display/rockchip/inno_hdmi-rockchip.txt        | 50 ++++++++++++++++++++++
 1 file changed, 50 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt

diff --git a/Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt
new file mode 100644
index 0000000..8096a29
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt
@@ -0,0 +1,50 @@
+Rockchip specific extensions to the Innosilicon HDMI
+================================
+
+Required properties:
+- compatible:
+	"rockchip,rk3036-inno-hdmi";
+- reg:
+	Physical base address and length of the controller's registers.
+- clocks, clock-names:
+	Phandle to hdmi controller clock, name should be "pclk"
+- interrupts:
+	HDMI interrupt number
+- ports:
+	Contain one port node with endpoint definitions as defined in
+	Documentation/devicetree/bindings/graph.txt.
+- pinctrl-0, pinctrl-name:
+	Switch the iomux of HPD/CEC pins to HDMI function.
+
+Example:
+hdmi: hdmi at 20034000 {
+	compatible = "rockchip,rk3036-inno-hdmi";
+	reg = <0x20034000 0x4000>;
+	interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
+	clocks = <&cru  PCLK_HDMI>;
+	clock-names = "pclk";
+	pinctrl-names = "default";
+	pinctrl-0 = <&hdmi_ctl>;
+	status = "disabled";
+
+	hdmi_in: port {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		hdmi_in_lcdc: endpoint at 0 {
+			reg = <0>;
+			remote-endpoint = <&lcdc_out_hdmi>;
+		};
+	};
+};
+
+&pinctrl {
+	hdmi {
+		hdmi_ctl: hdmi-ctl {
+			rockchip,pins = <1 8  RK_FUNC_1 &pcfg_pull_none>,
+					<1 9  RK_FUNC_1 &pcfg_pull_none>,
+					<1 10 RK_FUNC_1 &pcfg_pull_none>,
+					<1 11 RK_FUNC_1 &pcfg_pull_none>;
+		};
+	};
+
+};
-- 
1.9.1




From shuyu.w at outlook.com  Mon Jan 25 19:33:59 2016
From: shuyu.w at outlook.com (Shuyu Wei)
Date: Tue, 26 Jan 2016 03:33:59 +0000
Subject: rk3188 emac stuck at high load
In-Reply-To: <SNT151-W396F79221B612690406E88E7C70@phx.gbl>
References: <SNT151-W399C6D5938D0EC43FAD0F0E7E70@phx.gbl>,
 <56A619E0.7060901@gmail.com>, <SNT151-W396F79221B612690406E88E7C70@phx.gbl>
Message-ID: <SNT151-W6114F60BBAA0ECF6857EE2E7D80@phx.gbl>

Hi Caesar,
I checked /sys/kernel/debug/clk/clk_summary, sclk_macref is under dpll.
Besides, the problem exists even if I build the kernel with cpufreq disabled.
Maybe my previous patch is right. Shall I resend it to get it merged?

----------------------------------------
> From: shuyu.w at outlook.com
> To: caesar.upstream at gmail.com
> CC: linux-rockchip at lists.infradead.org; heiko at sntech.de
> Subject: RE: rk3188 emac stuck at high load
> Date: Mon, 25 Jan 2016 13:33:02 +0000
>
> Hi Caesar,
>
> Thanks for your reply, I thought I have fixed the issue by this patch:
> http://lists.infradead.org/pipermail/linux-rockchip/2016-January/006412.html
> Maybe I sent it to the wrong maintainer so it didn't get noticed.
> I'll try your solution and report as soon as possible.
> ----------------------------------------
>> Subject: Re: rk3188 emac stuck at high load
>> To: shuyu.w at outlook.com
>> CC: linux-rockchip at lists.infradead.org; heiko at sntech.de
>> From: caesar.upstream at gmail.com
>> Date: Mon, 25 Jan 2016 20:49:36 +0800
>>
>> Hi Shuyu,
>>
>> Thanks your report! Do you have fixed this issue?
>> I guess that caused by the cpufreq.
>>
>> if you run 'cat sys/kernel/debug/clk/clk_summary' to check the emac
>> parent clock, Is it the APLL?
>> Maybe you can change the parent clock to fix this issue if it's.
>>
>> Fox example:
>> uboot?
>> https://github.com/rockchip-linux/u-boot/commit/d64ef6b272d84c92bd02a7925f500880633c8599
>>
>> kernel:
>> https://patchwork.kernel.org/patch/8108231/
>>
>>
>> I try to install many somethings with ubuntu os on rk3036. (that's also
>> working with the emac..)
>>
>> .....
>> Get:35 http://ports.ubuntu.com/ubuntu-ports/ wily/main
>> libpython2.7-minimal armhf 2.7.10-4ubuntu1 [338 kB]
>> Get:36 http://ports.ubuntu.com/ubuntu-ports/ wily/main
>> libpython2.7-stdlib armhf 2.7.10-4ubuntu1 [1750 kB]
>> Get:37 http://ports.ubuntu.com/ubuntu-ports/ wily/main libpython2.7
>> armhf 2.7.10-4ubuntu1 [935 kB]
>> Get:38 http://ports.ubuntu.com/ubuntu-ports/ wily/main vim-runtime all
>> 2:7.4.712-2ubuntu4 [4984 kB]
>> 85% [38 vim-runtime 4405 kB/4984 kB 88%] 195 kB/s 11s[ 314.685641] BUG:
>> Bad page state in process swapper/0 pfn:7e0
>> [ 314.691607] page:dffcee00 count:-1 mapcount:0 mapping: (null) index:0x0
>> [ 314.698311] flags: 0x0()
>> [ 314.700866] page dumped because: nonzero _count
>> 88% [38 vim-runtime 4738 kB/4984 kB 95%] 195 kB/s 9s[ 316.096013]
>> skbuff: skb_over_panic: text:c04ae19c len:2878 p>
>> [ 316.108471] ------------[ cut here ]------------
>> [ 316.113109] kernel BUG at
>> /home/wxt/work/android/brillo/kernel/net/core/skbuff.c:102!
>> [ 316.120938] Internal error: Oops - BUG: 0 [#1] SMP ARM
>> [ 316.126079] Modules linked in:
>> [ 316.129166] CPU: 0 PID: 0 Comm: swapper/0 Tainted: G B
>> 4.4.0-rc8+ #35
>> [ 316.136732] Hardware name: Rockchip (Device Tree)
>> [ 316.141444] task: c0f0c0b0 ti: c0f00000 task.ti: c0f00000
>> [ 316.146862] PC is at skb_panic+0x60/0x6c
>> [ 316.150799] LR is at _raw_spin_unlock_irqrestore+0x1c/0x24
>> [ 316.156291] pc : [<c093f694>] lr : [<c0947fb0>] psr: 60000113
>> [ 316.156291] sp : c0f01d88 ip : c0f01c08 fp : c0f01dbc
>> [ 316.167761] r10: df718540 r9 : deabbcc0 r8 : 000003a0
>> [ 316.172989] r7 : c0c3ba0a r6 : debb0c00 r5 : debb0c64 r4 : debb17a2
>> [ 316.179517] r3 : c0e933b8 r2 : 00000000 r1 : 60000113 r0 : 0000007d
>> ...
>> [ 316.372994] [<c093f694>] (skb_panic) from [<c07353b8>]
>> (skb_put+0x54/0x60)
>> [ 316.379888] [<c07353b8>] (skb_put) from [<c04ae19c>]
>> (arc_emac_poll+0x26c/0x4bc)
>> [ 316.387299] [<c04ae19c>] (arc_emac_poll) from [<c07456b4>]
>> (net_rx_action+0xf4/0x308)
>> [ 316.395146] [<c07456b4>] (net_rx_action) from [<c012365c>]
>> (__do_softirq+0x10c/0x2d8)
>> [ 316.402991] [<c012365c>] (__do_softirq) from [<c0123adc>]
>> (irq_exit+0x94/0x104)
>> [ 316.410313] [<c0123adc>] (irq_exit) from [<c016a778>]
>> (__handle_domain_irq+0x98/0xbc)
>> [ 316.418156] [<c016a778>] (__handle_domain_irq) from [<c010143c>]
>> (gic_handle_irq+0x58/0xa0)
>> [ 316.426514] [<c010143c>] (gic_handle_irq) from [<c010be94>]
>> (__irq_svc+0x54/
>>
>> ? 2015?12?24? 23:52, Shuyu Wei ??:
>>> Hi,
>>> I'm testing Radxa Rock Pro board with mainline kernel 4.4.0-rc6.
>>> The problem can be reproduced by simultaneously wget two large files at high speed to /dev/null.
>>>
>>> $wget https://192.168.1.1/file.bin -O /dev/null -q &
>>> $wget https://192.168.1.1/file.bin -O /dev/null
>>>
>>> or open two sftp session to download two large files to /dev/null.
>>>
>>> Some addtional information that might help:
>>> Downloading from encrypted https links or sftp server triggers the problem much easier.
>>> Disabling SMP in kernel seems to fix the problem.
>>> Sometimes the emac just stops sending and receiving frames, sometimes it produces kernel panics like below.
>>>
>>> [ 2191.975729] BUG: Bad page state in process swapper/0 pfn:8d960
>>> [ 2191.982354] page:ef463180 count:-1 mapcount:0 mapping: (null) index:0x0
>>> [ 2191.989047] flags: 0x0()
>>> [ 2191.991594] page dumped because: nonzero _count
>>> [ 2191.996127] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.4.0-rc6 #114
>>> [ 2192.002475] Hardware name: Rockchip (Device Tree)
>>> [ 2192.007174] Backtrace:
>>> [ 2192.009658] [<c00134d4>] (dump_backtrace) from [<c0013680>] (show_stack+0x18/0x1c)
>>> [ 2192.017220] r7:c051c4f8 r6:ef463180 r5:c05b7000 r4:00000000
>>> [ 2192.022948] [<c0013668>] (show_stack) from [<c0219d90>] (dump_stack+0x90/0xa0)
>>> [ 2192.030176] [<c0219d00>] (dump_stack) from [<c00b2cd4>] (bad_page+0xdc/0x12c)
>>> [ 2192.037302] r5:c059a100 r4:c05f430c
>>> [ 2192.040913] [<c00b2bf8>] (bad_page) from [<c00b606c>] (get_page_from_freelist+0x388/0x95c)
>>> [ 2192.049166] r9:00000008 r8:ef463180 r7:c051c4d0 r6:00000000 r5:00000000 r4:c051c4e4
>>> [ 2192.056982] [<c00b5ce4>] (get_page_from_freelist) from [<c00b6880>] (__alloc_pages_nodemask+0xd8/0x8e8)
>>> [ 2192.066362] r10:c001b068 r9:00000000 r8:ee0b02b0 r7:60000113 r6:00000003 r5:02095220
>>> [ 2192.074254] r4:c05ca1c0
>>> [ 2192.076809] [<c00b67a8>] (__alloc_pages_nodemask) from [<c00b7140>] (__alloc_page_frag+0xb0/0x160)
>>> [ 2192.085757] r10:c001b068 r9:00000000 r8:ee0b02b0 r7:60000113 r6:02080020 r5:00000740
>>> [ 2192.093650] r4:eedbc884
>>> [ 2192.096207] [<c00b7090>] (__alloc_page_frag) from [<c03273b4>] (__netdev_alloc_skb+0xa0/0x104)
>>> [ 2192.104806] r7:60000113 r6:eedbc884 r5:ee0b0000 r4:00000740
>>> [ 2192.110525] [<c0327314>] (__netdev_alloc_skb) from [<c02aac00>] (arc_emac_poll+0x318/0x57c)
>>> [ 2192.118865] r9:00000000 r8:ee0b02b0 r7:0000019c r6:ee163780 r5:00000670 r4:ee0b0000
>>> [ 2192.126683] [<c02aa8e8>] (arc_emac_poll) from [<c0339ed8>] (net_rx_action+0x1f0/0x2ec)
>>> [ 2192.134590] r10:c0599df8 r9:c059a100 r8:00073760 r7:0000012c r6:00000028 r5:c02aa8e8
>>> [ 2192.142483] r4:ee0b04e0
>>> [ 2192.145040] [<c0339ce8>] (net_rx_action) from [<c0026f5c>] (__do_softirq+0x134/0x258)
>>> [ 2192.152860] r10:c059a080 r9:40000003 r8:00000003 r7:00000100 r6:c0598000 r5:c059a08c
>>> [ 2192.160751] r4:00000000
>>> [ 2192.163303] [<c0026e28>] (__do_softirq) from [<c0027344>] (irq_exit+0xb8/0x120)
>>> [ 2192.170604] r10:c04818a4 r9:c059a4a0 r8:ee808000 r7:00000000 r6:00000000 r5:0000001a
>>> [ 2192.178496] r4:c05943bc
>>> [ 2192.181051] [<c002728c>] (irq_exit) from [<c00656a4>] (__handle_domain_irq+0x68/0xbc)
>>> [ 2192.188871] r5:0000001a r4:c05943bc
>>> [ 2192.192479] [<c006563c>] (__handle_domain_irq) from [<c0009438>] (gic_handle_irq+0x40/0x78)
>>> [ 2192.200818] r9:c059a4a0 r8:f0803100 r7:f0802100 r6:c0599f00 r5:f080210c r4:c059a87c
>>> [ 2192.208630] [<c00093f8>] (gic_handle_irq) from [<c0014214>] (__irq_svc+0x54/0x70)
>>> [ 2192.216105] Exception stack(0xc0599f00 to 0xc0599f48)
>>> [ 2192.221161] 9f00: 00000001 00000000 00000000 c001e940 c0598000 c059a454 00000000 c05ca291
>>> [ 2192.229336] 9f20: 00000000 c059a4a0 c04818a4 c0599f5c c0599f60 c0599f50 c0010208 c001020c
>>> [ 2192.237505] 9f40: 60000013 ffffffff
>>> [ 2192.240990] r9:c059a4a0 r8:00000000 r7:c0599f34 r6:ffffffff r5:60000013 r4:c001020c
>>> [ 2192.248810] [<c00101cc>] (arch_cpu_idle) from [<c005bbec>] (default_idle_call+0x28/0x34)
>>> [ 2192.256899] [<c005bbc4>] (default_idle_call) from [<c005be60>] (cpu_startup_entry+0x214/0x270)
>>> [ 2192.265515] [<c005bc4c>] (cpu_startup_entry) from [<c047a6f4>] (rest_init+0x80/0x84)
>>> [ 2192.273248] r7:ffffffff
>>> [ 2192.275808] [<c047a674>] (rest_init) from [<c055fca0>] (start_kernel+0x348/0x354)
>>> [ 2192.283288] [<c055f958>] (start_kernel) from [<60008078>] (0x60008078)
>>> [ 2192.289798] Disabling lock debugging due to kernel taint
>>> [ 2192.300779] rockchip_emac 10204000.ethernet eth0: BUG! Tx Ring full when queue awake!
>>>
>>> I'm glad to provide more information if needed.
>>>
>>> Shuyu Wei
>>>
>>> _______________________________________________
>>> Linux-rockchip mailing list
>>> Linux-rockchip at lists.infradead.org
>>> http://lists.infradead.org/mailman/listinfo/linux-rockchip
>>
>>
>> --
>> Thanks,
>> Caesar
>>
>
 		 	   		  

From shawn.lin at rock-chips.com  Mon Jan 25 19:30:18 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Tue, 26 Jan 2016 11:30:18 +0800
Subject: [PATCH] clk: rockchip: fix wrong mmc phase shift for rk3228
Message-ID: <1453779018-30666-1-git-send-email-shawn.lin@rock-chips.com>

mmc sample shift is 0 for rk3228 refer to user manaul.
So it's broken if we enable mmc tuning for rk3228.

Fixes: 307a2e9ac ("clk: rockchip: add clock controller for rk3228")
Cc: Xing Zheng <zhengxing at rock-chips.com>
Cc: Jeffy Chen <jeffy.chen at rock-chips.com>
Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
---

 drivers/clk/rockchip/clk-rk3228.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/clk/rockchip/clk-rk3228.c b/drivers/clk/rockchip/clk-rk3228.c
index 981a502..97f49aa 100644
--- a/drivers/clk/rockchip/clk-rk3228.c
+++ b/drivers/clk/rockchip/clk-rk3228.c
@@ -605,13 +605,13 @@ static struct rockchip_clk_branch rk3228_clk_branches[] __initdata = {
 
 	/* PD_MMC */
 	MMC(SCLK_SDMMC_DRV,    "sdmmc_drv",    "sclk_sdmmc", RK3228_SDMMC_CON0, 1),
-	MMC(SCLK_SDMMC_SAMPLE, "sdmmc_sample", "sclk_sdmmc", RK3228_SDMMC_CON1, 1),
+	MMC(SCLK_SDMMC_SAMPLE, "sdmmc_sample", "sclk_sdmmc", RK3228_SDMMC_CON1, 0),
 
 	MMC(SCLK_SDIO_DRV,     "sdio_drv",     "sclk_sdio",  RK3228_SDIO_CON0,  1),
-	MMC(SCLK_SDIO_SAMPLE,  "sdio_sample",  "sclk_sdio",  RK3228_SDIO_CON1,  1),
+	MMC(SCLK_SDIO_SAMPLE,  "sdio_sample",  "sclk_sdio",  RK3228_SDIO_CON1,  0),
 
 	MMC(SCLK_EMMC_DRV,     "emmc_drv",     "sclk_emmc",  RK3228_EMMC_CON0,  1),
-	MMC(SCLK_EMMC_SAMPLE,  "emmc_sample",  "sclk_emmc",  RK3228_EMMC_CON1,  1),
+	MMC(SCLK_EMMC_SAMPLE,  "emmc_sample",  "sclk_emmc",  RK3228_EMMC_CON1,  0),
 };
 
 static const char *const rk3228_critical_clocks[] __initconst = {
-- 
2.3.7




From wxt at rock-chips.com  Mon Jan 25 19:41:38 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Tue, 26 Jan 2016 11:41:38 +0800
Subject: rk3188 emac stuck at high load
In-Reply-To: <SNT151-W6114F60BBAA0ECF6857EE2E7D80@phx.gbl>
References: <SNT151-W399C6D5938D0EC43FAD0F0E7E70@phx.gbl>
 <56A619E0.7060901@gmail.com> <SNT151-W396F79221B612690406E88E7C70@phx.gbl>
 <SNT151-W6114F60BBAA0ECF6857EE2E7D80@phx.gbl>
Message-ID: <56A6EAF2.9010307@rock-chips.com>

Hi Shuyu,


? 2016?01?26? 11:33, Shuyu Wei ??:
> Hi Caesar,
> I checked /sys/kernel/debug/clk/clk_summary, sclk_macref is under dpll.
> Besides, the problem exists even if I build the kernel with cpufreq disabled.
> Maybe my previous patch is right. Shall I resend it to get it merged?

Thanks your tests. Yep, up to you.
Can you resend with cc the linux-rockchip at lists.infradead.org?


> ----------------------------------------
>> From: shuyu.w at outlook.com
>> To: caesar.upstream at gmail.com
>> CC: linux-rockchip at lists.infradead.org; heiko at sntech.de
>> Subject: RE: rk3188 emac stuck at high load
>> Date: Mon, 25 Jan 2016 13:33:02 +0000
>>
>> Hi Caesar,
>>
>> Thanks for your reply, I thought I have fixed the issue by this patch:
>> http://lists.infradead.org/pipermail/linux-rockchip/2016-January/006412.html
>> Maybe I sent it to the wrong maintainer so it didn't get noticed.
>> I'll try your solution and report as soon as possible.
>> ----------------------------------------
>>> Subject: Re: rk3188 emac stuck at high load
>>> To: shuyu.w at outlook.com
>>> CC: linux-rockchip at lists.infradead.org; heiko at sntech.de
>>> From: caesar.upstream at gmail.com
>>> Date: Mon, 25 Jan 2016 20:49:36 +0800
>>>
>>> Hi Shuyu,
>>>
>>> Thanks your report! Do you have fixed this issue?
>>> I guess that caused by the cpufreq.
>>>
>>> if you run 'cat sys/kernel/debug/clk/clk_summary' to check the emac
>>> parent clock, Is it the APLL?
>>> Maybe you can change the parent clock to fix this issue if it's.
>>>
>>> Fox example:
>>> uboot?
>>> https://github.com/rockchip-linux/u-boot/commit/d64ef6b272d84c92bd02a7925f500880633c8599
>>>
>>> kernel:
>>> https://patchwork.kernel.org/patch/8108231/
>>>
>>>
>>> I try to install many somethings with ubuntu os on rk3036. (that's also
>>> working with the emac..)
>>>
>>> .....
>>> Get:35 http://ports.ubuntu.com/ubuntu-ports/ wily/main
>>> libpython2.7-minimal armhf 2.7.10-4ubuntu1 [338 kB]
>>> Get:36 http://ports.ubuntu.com/ubuntu-ports/ wily/main
>>> libpython2.7-stdlib armhf 2.7.10-4ubuntu1 [1750 kB]
>>> Get:37 http://ports.ubuntu.com/ubuntu-ports/ wily/main libpython2.7
>>> armhf 2.7.10-4ubuntu1 [935 kB]
>>> Get:38 http://ports.ubuntu.com/ubuntu-ports/ wily/main vim-runtime all
>>> 2:7.4.712-2ubuntu4 [4984 kB]
>>> 85% [38 vim-runtime 4405 kB/4984 kB 88%] 195 kB/s 11s[ 314.685641] BUG:
>>> Bad page state in process swapper/0 pfn:7e0
>>> [ 314.691607] page:dffcee00 count:-1 mapcount:0 mapping: (null) index:0x0
>>> [ 314.698311] flags: 0x0()
>>> [ 314.700866] page dumped because: nonzero _count
>>> 88% [38 vim-runtime 4738 kB/4984 kB 95%] 195 kB/s 9s[ 316.096013]
>>> skbuff: skb_over_panic: text:c04ae19c len:2878 p>
>>> [ 316.108471] ------------[ cut here ]------------
>>> [ 316.113109] kernel BUG at
>>> /home/wxt/work/android/brillo/kernel/net/core/skbuff.c:102!
>>> [ 316.120938] Internal error: Oops - BUG: 0 [#1] SMP ARM
>>> [ 316.126079] Modules linked in:
>>> [ 316.129166] CPU: 0 PID: 0 Comm: swapper/0 Tainted: G B
>>> 4.4.0-rc8+ #35
>>> [ 316.136732] Hardware name: Rockchip (Device Tree)
>>> [ 316.141444] task: c0f0c0b0 ti: c0f00000 task.ti: c0f00000
>>> [ 316.146862] PC is at skb_panic+0x60/0x6c
>>> [ 316.150799] LR is at _raw_spin_unlock_irqrestore+0x1c/0x24
>>> [ 316.156291] pc : [<c093f694>] lr : [<c0947fb0>] psr: 60000113
>>> [ 316.156291] sp : c0f01d88 ip : c0f01c08 fp : c0f01dbc
>>> [ 316.167761] r10: df718540 r9 : deabbcc0 r8 : 000003a0
>>> [ 316.172989] r7 : c0c3ba0a r6 : debb0c00 r5 : debb0c64 r4 : debb17a2
>>> [ 316.179517] r3 : c0e933b8 r2 : 00000000 r1 : 60000113 r0 : 0000007d
>>> ...
>>> [ 316.372994] [<c093f694>] (skb_panic) from [<c07353b8>]
>>> (skb_put+0x54/0x60)
>>> [ 316.379888] [<c07353b8>] (skb_put) from [<c04ae19c>]
>>> (arc_emac_poll+0x26c/0x4bc)
>>> [ 316.387299] [<c04ae19c>] (arc_emac_poll) from [<c07456b4>]
>>> (net_rx_action+0xf4/0x308)
>>> [ 316.395146] [<c07456b4>] (net_rx_action) from [<c012365c>]
>>> (__do_softirq+0x10c/0x2d8)
>>> [ 316.402991] [<c012365c>] (__do_softirq) from [<c0123adc>]
>>> (irq_exit+0x94/0x104)
>>> [ 316.410313] [<c0123adc>] (irq_exit) from [<c016a778>]
>>> (__handle_domain_irq+0x98/0xbc)
>>> [ 316.418156] [<c016a778>] (__handle_domain_irq) from [<c010143c>]
>>> (gic_handle_irq+0x58/0xa0)
>>> [ 316.426514] [<c010143c>] (gic_handle_irq) from [<c010be94>]
>>> (__irq_svc+0x54/
>>>
>>> ? 2015?12?24? 23:52, Shuyu Wei ??:
>>>> Hi,
>>>> I'm testing Radxa Rock Pro board with mainline kernel 4.4.0-rc6.
>>>> The problem can be reproduced by simultaneously wget two large files at high speed to /dev/null.
>>>>
>>>> $wget https://192.168.1.1/file.bin -O /dev/null -q &
>>>> $wget https://192.168.1.1/file.bin -O /dev/null
>>>>
>>>> or open two sftp session to download two large files to /dev/null.
>>>>
>>>> Some addtional information that might help:
>>>> Downloading from encrypted https links or sftp server triggers the problem much easier.
>>>> Disabling SMP in kernel seems to fix the problem.
>>>> Sometimes the emac just stops sending and receiving frames, sometimes it produces kernel panics like below.
>>>>
>>>> [ 2191.975729] BUG: Bad page state in process swapper/0 pfn:8d960
>>>> [ 2191.982354] page:ef463180 count:-1 mapcount:0 mapping: (null) index:0x0
>>>> [ 2191.989047] flags: 0x0()
>>>> [ 2191.991594] page dumped because: nonzero _count
>>>> [ 2191.996127] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.4.0-rc6 #114
>>>> [ 2192.002475] Hardware name: Rockchip (Device Tree)
>>>> [ 2192.007174] Backtrace:
>>>> [ 2192.009658] [<c00134d4>] (dump_backtrace) from [<c0013680>] (show_stack+0x18/0x1c)
>>>> [ 2192.017220] r7:c051c4f8 r6:ef463180 r5:c05b7000 r4:00000000
>>>> [ 2192.022948] [<c0013668>] (show_stack) from [<c0219d90>] (dump_stack+0x90/0xa0)
>>>> [ 2192.030176] [<c0219d00>] (dump_stack) from [<c00b2cd4>] (bad_page+0xdc/0x12c)
>>>> [ 2192.037302] r5:c059a100 r4:c05f430c
>>>> [ 2192.040913] [<c00b2bf8>] (bad_page) from [<c00b606c>] (get_page_from_freelist+0x388/0x95c)
>>>> [ 2192.049166] r9:00000008 r8:ef463180 r7:c051c4d0 r6:00000000 r5:00000000 r4:c051c4e4
>>>> [ 2192.056982] [<c00b5ce4>] (get_page_from_freelist) from [<c00b6880>] (__alloc_pages_nodemask+0xd8/0x8e8)
>>>> [ 2192.066362] r10:c001b068 r9:00000000 r8:ee0b02b0 r7:60000113 r6:00000003 r5:02095220
>>>> [ 2192.074254] r4:c05ca1c0
>>>> [ 2192.076809] [<c00b67a8>] (__alloc_pages_nodemask) from [<c00b7140>] (__alloc_page_frag+0xb0/0x160)
>>>> [ 2192.085757] r10:c001b068 r9:00000000 r8:ee0b02b0 r7:60000113 r6:02080020 r5:00000740
>>>> [ 2192.093650] r4:eedbc884
>>>> [ 2192.096207] [<c00b7090>] (__alloc_page_frag) from [<c03273b4>] (__netdev_alloc_skb+0xa0/0x104)
>>>> [ 2192.104806] r7:60000113 r6:eedbc884 r5:ee0b0000 r4:00000740
>>>> [ 2192.110525] [<c0327314>] (__netdev_alloc_skb) from [<c02aac00>] (arc_emac_poll+0x318/0x57c)
>>>> [ 2192.118865] r9:00000000 r8:ee0b02b0 r7:0000019c r6:ee163780 r5:00000670 r4:ee0b0000
>>>> [ 2192.126683] [<c02aa8e8>] (arc_emac_poll) from [<c0339ed8>] (net_rx_action+0x1f0/0x2ec)
>>>> [ 2192.134590] r10:c0599df8 r9:c059a100 r8:00073760 r7:0000012c r6:00000028 r5:c02aa8e8
>>>> [ 2192.142483] r4:ee0b04e0
>>>> [ 2192.145040] [<c0339ce8>] (net_rx_action) from [<c0026f5c>] (__do_softirq+0x134/0x258)
>>>> [ 2192.152860] r10:c059a080 r9:40000003 r8:00000003 r7:00000100 r6:c0598000 r5:c059a08c
>>>> [ 2192.160751] r4:00000000
>>>> [ 2192.163303] [<c0026e28>] (__do_softirq) from [<c0027344>] (irq_exit+0xb8/0x120)
>>>> [ 2192.170604] r10:c04818a4 r9:c059a4a0 r8:ee808000 r7:00000000 r6:00000000 r5:0000001a
>>>> [ 2192.178496] r4:c05943bc
>>>> [ 2192.181051] [<c002728c>] (irq_exit) from [<c00656a4>] (__handle_domain_irq+0x68/0xbc)
>>>> [ 2192.188871] r5:0000001a r4:c05943bc
>>>> [ 2192.192479] [<c006563c>] (__handle_domain_irq) from [<c0009438>] (gic_handle_irq+0x40/0x78)
>>>> [ 2192.200818] r9:c059a4a0 r8:f0803100 r7:f0802100 r6:c0599f00 r5:f080210c r4:c059a87c
>>>> [ 2192.208630] [<c00093f8>] (gic_handle_irq) from [<c0014214>] (__irq_svc+0x54/0x70)
>>>> [ 2192.216105] Exception stack(0xc0599f00 to 0xc0599f48)
>>>> [ 2192.221161] 9f00: 00000001 00000000 00000000 c001e940 c0598000 c059a454 00000000 c05ca291
>>>> [ 2192.229336] 9f20: 00000000 c059a4a0 c04818a4 c0599f5c c0599f60 c0599f50 c0010208 c001020c
>>>> [ 2192.237505] 9f40: 60000013 ffffffff
>>>> [ 2192.240990] r9:c059a4a0 r8:00000000 r7:c0599f34 r6:ffffffff r5:60000013 r4:c001020c
>>>> [ 2192.248810] [<c00101cc>] (arch_cpu_idle) from [<c005bbec>] (default_idle_call+0x28/0x34)
>>>> [ 2192.256899] [<c005bbc4>] (default_idle_call) from [<c005be60>] (cpu_startup_entry+0x214/0x270)
>>>> [ 2192.265515] [<c005bc4c>] (cpu_startup_entry) from [<c047a6f4>] (rest_init+0x80/0x84)
>>>> [ 2192.273248] r7:ffffffff
>>>> [ 2192.275808] [<c047a674>] (rest_init) from [<c055fca0>] (start_kernel+0x348/0x354)
>>>> [ 2192.283288] [<c055f958>] (start_kernel) from [<60008078>] (0x60008078)
>>>> [ 2192.289798] Disabling lock debugging due to kernel taint
>>>> [ 2192.300779] rockchip_emac 10204000.ethernet eth0: BUG! Tx Ring full when queue awake!
>>>>
>>>> I'm glad to provide more information if needed.
>>>>
>>>> Shuyu Wei
>>>>
>>>> _______________________________________________
>>>> Linux-rockchip mailing list
>>>> Linux-rockchip at lists.infradead.org
>>>> http://lists.infradead.org/mailman/listinfo/linux-rockchip
>>>
>>> --
>>> Thanks,
>>> Caesar
>>>
>  		 	   		  
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip


-- 
caesar wang | software engineer | wxt at rock-chip.com




From sy.w at outlook.com  Mon Jan 25 20:47:54 2016
From: sy.w at outlook.com (Shuyu Wei)
Date: Tue, 26 Jan 2016 12:47:54 +0800
Subject: [PATCH RESEND] ethernet:arc: Move arc_emac_tx_clean() into
 arc_emac_tx() and disable tx interrut
Message-ID: <BLU437-SMTP334CF4A4131F7C0E3397498FD80@phx.gbl>

Doing tx_clean() inside poll() may scramble the tx ring buffer if 
tx() is running. This will cause tx to stop working, which can be
reproduced by simultaneously downloading two large files at high speed.

Moving tx_clean() into tx() will prevent this. And tx interrupt is no
longer needed now.

Signed-off-by: Shuyu Wei <shuyu.w at outlook.com>
---

diff --git a/drivers/net/ethernet/arc/emac_main.c b/drivers/net/ethernet/arc/emac_main.c
index abe1eab..34531b3 100644
--- a/drivers/net/ethernet/arc/emac_main.c
+++ b/drivers/net/ethernet/arc/emac_main.c
@@ -311,12 +311,10 @@ static int arc_emac_poll(struct napi_struct *napi, int budget)
 	struct arc_emac_priv *priv = netdev_priv(ndev);
 	unsigned int work_done;
 
-	arc_emac_tx_clean(ndev);
-
 	work_done = arc_emac_rx(ndev, budget);
 	if (work_done < budget) {
 		napi_complete(napi);
-		arc_reg_or(priv, R_ENABLE, RXINT_MASK | TXINT_MASK);
+		arc_reg_or(priv, R_ENABLE, RXINT_MASK);
 	}
 
 	return work_done;
@@ -345,9 +343,9 @@ static irqreturn_t arc_emac_intr(int irq, void *dev_instance)
 	/* Reset all flags except "MDIO complete" */
 	arc_reg_set(priv, R_STATUS, status);
 
-	if (status & (RXINT_MASK | TXINT_MASK)) {
+	if (status & RXINT_MASK) {
 		if (likely(napi_schedule_prep(&priv->napi))) {
-			arc_reg_clr(priv, R_ENABLE, RXINT_MASK | TXINT_MASK);
+			arc_reg_clr(priv, R_ENABLE, RXINT_MASK);
 			__napi_schedule(&priv->napi);
 		}
 	}
@@ -458,7 +456,7 @@ static int arc_emac_open(struct net_device *ndev)
 	arc_reg_set(priv, R_TX_RING, (unsigned int)priv->txbd_dma);
 
 	/* Enable interrupts */
-	arc_reg_set(priv, R_ENABLE, RXINT_MASK | TXINT_MASK | ERR_MASK);
+	arc_reg_set(priv, R_ENABLE, RXINT_MASK | ERR_MASK);
 
 	/* Set CONTROL */
 	arc_reg_set(priv, R_CTRL,
@@ -529,7 +527,7 @@ static int arc_emac_stop(struct net_device *ndev)
 	netif_stop_queue(ndev);
 
 	/* Disable interrupts */
-	arc_reg_clr(priv, R_ENABLE, RXINT_MASK | TXINT_MASK | ERR_MASK);
+	arc_reg_clr(priv, R_ENABLE, RXINT_MASK | ERR_MASK);
 
 	/* Disable EMAC */
 	arc_reg_clr(priv, R_CTRL, EN_MASK);
@@ -587,6 +585,8 @@ static int arc_emac_tx(struct sk_buff *skb, struct net_device *ndev)
 	__le32 *info = &priv->txbd[*txbd_curr].info;
 	dma_addr_t addr;
 
+	arc_emac_tx_clean(ndev);
+
 	if (skb_padto(skb, ETH_ZLEN))
 		return NETDEV_TX_OK;



From aspriel at gmail.com  Mon Jan 25 22:29:54 2016
From: aspriel at gmail.com (Arend van Spriel)
Date: Tue, 26 Jan 2016 07:29:54 +0100
Subject: [PATCH] brcmfmac: sdio: Increase the default timeouts a bit
In-Reply-To: <CAGRGNgVftXxnq06MOHKNVzVjvCkwCM8Bc+Sfs4NoOQR=sfYP-g@mail.gmail.com>
References: <1453718849-3508-1-git-send-email-sjoerd.simons@collabora.co.uk>
 <CAGRGNgXWE=b52Mcy8NsKBZS_4gz-TUiuFipjsuTZh1xjja=uUg@mail.gmail.com>
 <56A641CC.6020500@gmail.com>
 <CAGRGNgVftXxnq06MOHKNVzVjvCkwCM8Bc+Sfs4NoOQR=sfYP-g@mail.gmail.com>
Message-ID: <56A71262.8000804@gmail.com>

On 26-01-16 00:41, Julian Calaby wrote:
> Hi Arend,
> 
> On Tue, Jan 26, 2016 at 2:39 AM, Arend van Spriel <aspriel at gmail.com> wrote:
>> On 25-01-16 12:06, Julian Calaby wrote:
>>> Hi Sjoerd,
>>>
>>> On Mon, Jan 25, 2016 at 9:47 PM, Sjoerd Simons
>>> <sjoerd.simons at collabora.co.uk> wrote:
>>>> On a Radxa Rock2 board with a Ampak AP6335 (Broadcom 4339 core) it seems
>>>> the card responds very quickly most of the time, unfortunately during
>>>> initialisation it sometimes seems to take just a bit over 2 seconds to
>>>> respond.
>>>>
>>>> This results intialization failing with message like:
>>>>   brcmf_c_preinit_dcmds: Retreiving cur_etheraddr failed, -52
>>>>   brcmf_bus_start: failed: -52
>>>>   brcmf_sdio_firmware_callback: dongle is not responding
>>>>
>>>> Increasing the timeout to allow for a bit more headroom allows the
>>>> card to initialize reliably.
>>>>
>>>> A quick search online after diagnosing/fixing this showed that Google
>>>> has a similar patch in their ChromeOS tree, so this doesn't seem
>>>> specific to the board I'm using.
>>>>
>>>> Signed-off-by: Sjoerd Simons <sjoerd.simons at collabora.co.uk>
>>>
>>> Looks sane to me.
>>>
>>> Reviewed-by: Julian Calaby <julian.calaby at gmail.com>
>>
>> Not really a cleanup patch :-p , but thanks for the review.
> 
> I'm trying to review any "small" patch from (relatively) new people.

And it is surely appreciated. Just read your reply in "cleanup patch
pile" thread and felt I had to make the stupid remark with just fun
intended.

Regards,
Arend


From andy.yan at rock-chips.com  Mon Jan 25 23:35:33 2016
From: andy.yan at rock-chips.com (Andy Yan)
Date: Tue, 26 Jan 2016 15:35:33 +0800
Subject: [PATCH v2 1/4] dt-bindings: power: reset: add document for
 reboot-mode driver
In-Reply-To: <20160125171134.GA21713@rob-hp-laptop>
References: <1452598029-8222-1-git-send-email-andy.yan@rock-chips.com>
 <1452598189-8272-1-git-send-email-andy.yan@rock-chips.com>
 <20160120182840.GA15741@rob-hp-laptop> <56A07A6D.3010406@rock-chips.com>
 <20160125171134.GA21713@rob-hp-laptop>
Message-ID: <56A721C5.4050606@rock-chips.com>

Hi Rob:

On 2016?01?26? 01:11, Rob Herring wrote:
> On Thu, Jan 21, 2016 at 02:27:57PM +0800, Andy Yan wrote:
>> Hi Rob:
>>     thanks for your review.
>> On 2016?01?21? 02:28, Rob Herring wrote:
>>> On Tue, Jan 12, 2016 at 07:29:49PM +0800, Andy Yan wrote:
>>>> add device tree binding document for reboot-mode driver
>>>>
>>>> Signed-off-by: Andy Yan <andy.yan at rock-chips.com>
>>>>
>>>> ---
>>>>
>>>> Changes in v2: None
>>>> Changes in v1: None
>>>>
>>>>   .../bindings/power/reset/reboot-mode.txt           | 41 +++++++++++++++++
>>>>   .../bindings/power/reset/syscon-reboot-mode.txt    | 52 ++++++++++++++++++++++
>>>>   2 files changed, 93 insertions(+)
>>>>   create mode 100644 Documentation/devicetree/bindings/power/reset/reboot-mode.txt
>>>>   create mode 100644 Documentation/devicetree/bindings/power/reset/syscon-reboot-mode.txt
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/power/reset/reboot-mode.txt b/Documentation/devicetree/bindings/power/reset/reboot-mode.txt
>>>> new file mode 100644
>>>> index 0000000..81d9f66
>>>> --- /dev/null
>>>> +++ b/Documentation/devicetree/bindings/power/reset/reboot-mode.txt
>>>> @@ -0,0 +1,41 @@
>>>> +Generic reboot mode core map driver
> [...]
>
>>>> +		compatible = "syscon-reboot-mode";
>>>> +		offset = <0x40>;
>>> This doc by itself is a little confusing. For example, is a child of the
>>> syscon node? I would remove offset (and perhaps compatible) from this
>>> example.
>>     Yes, is a child of a syscon mapped node. For example, Rockchip platform
>> use a register of PMU(rk3066/rk3288) or GRF(rk3036), PMU and GRF are aleady
>> mapped by syscon.
>>     offset and compatible are used by write interface driver like
>> syscon-reboot-mode.c. If you don't like it appear in the core map doc, I
>> will move it to the syscon-reboot-mode.txt?
> Yes, try to make this doc stand on its own. It will obviously be
> incomplete lacking information on where in the DT it goes. So perhaps a
> note stating reboot-mode node location is defined in platform specific
> binding docs.
>
>>>> +
>>>> +		loader {
>>>> +			linux,mode = "loader";
>>>> +			loader,magic = <BOOT_LOADER>;
>>>> +		};
>>> Sorry, my previous suggestion was not clear. I'm suggesting get rid of
>>> the subnodes and just do properties like this:
>>>
>>> loader = <BOOT_LOADER>;
>>> maskrom = <BOOT_MASKROM>;
>>>
>>> That's the same amount of information unless node names and linux,mode
>>> values are going to diverge. Do they need to? I can't see a reason.
>>      Because the command"linux,mode" and value"loader,magic" is vendor
>> specific. I don't know what commands and how many mode other platform will
>> use. So as John says in his reply, this sort of flexibility help us adapt
>> the driver to different hardware/system environments.
> The only part of "reboot to fastboot" that is vendor specific would be
> the magic value. While we can have custom modes, we should standardize
> the common ones as much as possible. As I pointed out in my reply to
> John, we can still support vendor specific modes with just a property.

     Based your reply to John, I rebuild the code like bellow, I hope this
     is what you mean.

     DTS file:
     reboot-mode {
                         compatible = "syscon-reboot-mode";
                         offset = <0x94>;
                         mode-normal = <BOOT_NORMAL>;
                         mode-recovery = <BOOT_RECOVERY>;
                         mode-fastboot = <BOOT_FASTBOOT>;
                         mode-loader = <BOOT_LOADER>;
                         mode-maskrom = <BOOT_MASKROM>;
                 };


    driver:

     #define PREFIX "mode-"

     struct property *prop;
     size_t len = strlen(PREFIX);
     for_each_property_of_node(dev->of_node, prop) {
                 if (len > strlen(prop->name) || strncmp(prop->name, 
PREFIX, len))
                         continue;
                 info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
                 if (!info)
                         return -ENOMEM;
                 strcpy(info->mode, prop->name + len);
                 if (of_property_read_u32(dev->of_node, prop->name, 
&info->magic)) {
                         dev_err(dev, "reboot mode %s without magic 
number\n",
                                 info->mode);
                         devm_kfree(dev, info);
                         continue;
                 }
                 list_add_tail(&info->list, &reboot->head);
         }


>>> We need to be clear what loader means. More specifically, it is boot
>>> into bootloader shell.
>>      Actually, Rockchip platform will reboot into a bootloader download mode
>> with this command. This mode can download faster than maskrom download mode.
> My point is proven. I assumed one thing and you meant something else.
> Doesn't matter what the mode is, just needs to be clear.
>
> Rob
>
>
>




From tomeu at tomeuvizoso.net  Tue Jan 26 00:28:48 2016
From: tomeu at tomeuvizoso.net (Tomeu Vizoso)
Date: Tue, 26 Jan 2016 09:28:48 +0100
Subject: [PATCH 0/5] clk: rockchip: add full support for HDMI clock on
 rk3288
In-Reply-To: <CAD=FV=W=LCJk4m049hBnm7BOYudnVe=oa3jdcSrrZZUU9rFtog@mail.gmail.com>
References: <1415087559-19444-1-git-send-email-kever.yang@rock-chips.com>
 <48806799.thBSifdUp8@diego> <54647142.4080800@rock-chips.com>
 <CAD=FV=Vzec3Qm-kc6zZ4VxSREoZbzVkWbzp-noHYdcEVrLYYSw@mail.gmail.com>
 <CAAObsKChYN5nmcm5OWnAzNRGDi_QGSoKvN+0FizhjeT-3mmeXQ@mail.gmail.com>
 <CAD=FV=Wgu=xQ9PcVqfzcedXVU3Rh-HKbBVxHuqgV9OZJEYhYvg@mail.gmail.com>
 <CAAObsKC1v--mZwkiG-c7P2Bwi2-_txbeqaW4Vd5h-noAVNbhjA@mail.gmail.com>
 <CAD=FV=VB93wYigRJ8wa8YL6P+Q+-fRokPiCB-Fo1pC_zAdh2oA@mail.gmail.com>
 <CAAObsKCMh3Bk_oNJdLbU2F9b7jrnFZOp2uFR_oPqUoJ3W1gfaQ@mail.gmail.com>
 <CAD=FV=W=LCJk4m049hBnm7BOYudnVe=oa3jdcSrrZZUU9rFtog@mail.gmail.com>
Message-ID: <CAAObsKBS45kbvdmdvFxwSo=B9GXRLoX2g7WQYVmZJ0KFnwSejw@mail.gmail.com>

On 22 January 2016 at 18:07, Doug Anderson <dianders at chromium.org> wrote:
> Tomeu,
>
> On Fri, Jan 22, 2016 at 6:00 AM, Tomeu Vizoso <tomeu at tomeuvizoso.net> wrote:
>> On 21 January 2016 at 21:11, Doug Anderson <dianders at chromium.org> wrote:
>>> Hi,
>>>
>>> On Thu, Jan 21, 2016 at 1:03 AM, Tomeu Vizoso <tomeu at tomeuvizoso.net> wrote:
>>>> So we have a mechanism for detecting a conflict in the clock
>>>> hierarchy, and a mechanism to solve it, but we are missing a way for
>>>> userspace to communicate policy regarding which clocks should be given
>>>> priority when solving such a conflict?
>>>
>>> Hrmmm, I guess it could be userspace that makes the decision.  It does
>>> seem a little odd to force it to userspace in all cases, though.  For
>>> a particular laptop that is designed with a specific panel connected
>>> up eDP it seems less than ideal to push this into userspace.  If the
>>> kernel could just work in the expected sane way (or at least work that
>>> way by default) it would be ideal.
>>
>> Ah, I was wrongly assuming that the kernel didn't have enough
>> information to make an informed decision in this case, sorry.
>>
>> Guess the per-user rate limits don't help here because the consumer
>> with higher priority could work with frequencies other than the ideal.
>>
>> And we cannot have a consumer listening for PRE_RATE_CHANGE and
>> aborting unwanted changes or rerouting the ancestors of the clocks of
>> other consumers because that would be a massive violation of
>> separation of concerns.
>>
>> If we were to rearrange the clock topology from within the CCF, then
>> consumers need to have a way to communicate to the core that they are
>> more important than other consumers. clk_set_important(clk, true)
>> could be enough in this case, but would be insufficient in more
>> complex cases where more than two clocks could use the same PLL.
>
> With something like the above I'd still expect some complexity
> depending on the probe order.  If a less important device grabs the
> clock first, it might be forced to re-think its clocks later.  That
> might be disconcerting.

How much disconcerting do you think this could be? Hopefully those
devices should probe quite close to each other, right?

> OK, so I was just involved in a change recently that made me realize
> that maybe our original problems were tied to the fact that our
> builtin panels were trying to specify a clock that was impossible to
> achieve with CPLL / GPLL.  It was a known problem that the request
> would be denied and the CCF would just pick the closest rate it could.
> Probably the right thing is to solve _that_ problem first.  If using
> simple panel you could do a change like
> <https://chromium-review.googlesource.com/#/c/323211/> (though
> presumably you'd have to handle people using the same panel in other
> laptops).  You might also be able to do funny things to fixup the mode
> like dbehr tried to do in
> <https://chromium-review.googlesource.com/#/c/270017/>.  By doing this
> and making sure that

Are we missing something here?

> With that being said, how about this as a solution:
>
> 1. Figure some way (presumably some type of device tree property?) to
> make sure that the builtin panel chose a clock that was achievable by
> dividing CPLL / GPLL on rk3288.  If you have eDP but no builtin panel
> you wouldn't specify this and you'd just let the system pick whatever
> it wanted.
>
> 2. We use "assigned-clocks" to (by default) make NPLL something silly
> and unattractive.  This will keep the common clock framework from
> picking it to use as the source if the clock could be made just as
> well from CPLL / GPLL (or find some other way to make NPLL a lower
> priority muxing option).
>
> 3. We use the "dibs" rule in that the first display driver to claim
> NPLL blocks anyone else from changing things.
>
>
> In the above on a laptop with a builtin panel and an HDMI port you'd
> get the panel using CPLL / GPLL and HDMI getting CPLL, GPLL or NPLL
> (and it would be able to change NPLL if needed).
>
>
> In the above on a dev board with a lot of ports then the first device
> probed would get the most flexibility.  Future devices would be
> limited in their choices.

Not sure of this, sounds a bit ad-hoc and may be avoiding to address
limitations in the CCF that need to be solved anyway? So far it still
looks to me like the CCF needs to get better at allocating limited
resources.

Mike, Stephen, what do you think?

Thanks,

Tomeu


From jung.zhao at rock-chips.com  Tue Jan 26 01:04:03 2016
From: jung.zhao at rock-chips.com (Jung Zhao)
Date: Tue, 26 Jan 2016 17:04:03 +0800
Subject: [PATCH v1 0/3] Add VP8 deocder for rk3229 & rk3288
Message-ID: <1453799046-307-1-git-send-email-jung.zhao@rock-chips.com>

From: zhaojun <jung.zhao at rock-chips.com>


====================
Introduction
====================

The purpose of this series is to add the driver for vp8
decoder on rk3229 & rk3288 platform, and will support
more formats in the future.

The driver uses v4l2 framework and RK IOMMU.
RK IOMMU has not yet been merged.



zhaojun (3):
  media: v4l: Add VP8 format support in V4L2 framework
  media: VPU: support Rockchip VPU
  media: vcodec: rockchip: Add Rockchip VP8 decoder driver

 drivers/media/platform/rockchip-vpu/Makefile       |    7 +
 .../media/platform/rockchip-vpu/rkvpu_hw_vp8d.c    |  798 ++++++++++
 .../platform/rockchip-vpu/rockchip_vp8d_regs.h     | 1594 ++++++++++++++++++++
 drivers/media/platform/rockchip-vpu/rockchip_vpu.c |  799 ++++++++++
 .../platform/rockchip-vpu/rockchip_vpu_common.h    |  439 ++++++
 .../media/platform/rockchip-vpu/rockchip_vpu_dec.c | 1007 +++++++++++++
 .../media/platform/rockchip-vpu/rockchip_vpu_dec.h |   33 +
 .../media/platform/rockchip-vpu/rockchip_vpu_hw.c  |  295 ++++
 .../media/platform/rockchip-vpu/rockchip_vpu_hw.h  |  100 ++
 drivers/media/v4l2-core/v4l2-ctrls.c               |   17 +-
 drivers/media/v4l2-core/v4l2-ioctl.c               |    3 +
 drivers/media/v4l2-core/videobuf2-dma-contig.c     |   51 +-
 include/media/v4l2-ctrls.h                         |    2 +
 include/media/videobuf2-dma-contig.h               |   11 +-
 include/uapi/linux/v4l2-controls.h                 |   98 ++
 include/uapi/linux/videodev2.h                     |    5 +
 16 files changed, 5238 insertions(+), 21 deletions(-)
 create mode 100644 drivers/media/platform/rockchip-vpu/Makefile
 create mode 100644 drivers/media/platform/rockchip-vpu/rkvpu_hw_vp8d.c
 create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vp8d_regs.h
 create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu.c
 create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu_common.h
 create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.c
 create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.h
 create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.c
 create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.h

-- 
1.9.1



From jung.zhao at rock-chips.com  Tue Jan 26 01:04:04 2016
From: jung.zhao at rock-chips.com (Jung Zhao)
Date: Tue, 26 Jan 2016 17:04:04 +0800
Subject: [PATCH v1 1/3] media: v4l: Add VP8 format support in V4L2 framework
In-Reply-To: <1453799046-307-1-git-send-email-jung.zhao@rock-chips.com>
References: <1453799046-307-1-git-send-email-jung.zhao@rock-chips.com>
Message-ID: <1453799046-307-2-git-send-email-jung.zhao@rock-chips.com>

From: zhaojun <jung.zhao at rock-chips.com>

Signed-off-by: zhaojun <jung.zhao at rock-chips.com>
---

 drivers/media/v4l2-core/v4l2-ctrls.c           | 17 ++++-
 drivers/media/v4l2-core/v4l2-ioctl.c           |  3 +
 drivers/media/v4l2-core/videobuf2-dma-contig.c | 51 +++++++++-----
 include/media/v4l2-ctrls.h                     |  2 +
 include/media/videobuf2-dma-contig.h           | 11 ++-
 include/uapi/linux/v4l2-controls.h             | 98 ++++++++++++++++++++++++++
 include/uapi/linux/videodev2.h                 |  5 ++
 7 files changed, 166 insertions(+), 21 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
index 890520d..22821e94 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -761,7 +761,7 @@ const char *v4l2_ctrl_get_name(u32 id)
 	case V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP:		return "VPX I-Frame QP Value";
 	case V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP:		return "VPX P-Frame QP Value";
 	case V4L2_CID_MPEG_VIDEO_VPX_PROFILE:			return "VPX Profile";
-
+	case V4L2_CID_MPEG_VIDEO_VP8_FRAME_HDR:			return "VP8 Frame Header";
 	/* CAMERA controls */
 	/* Keep the order of the 'case's the same as in v4l2-controls.h! */
 	case V4L2_CID_CAMERA_CLASS:		return "Camera Controls";
@@ -1126,6 +1126,9 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
 	case V4L2_CID_RDS_TX_ALT_FREQS:
 		*type = V4L2_CTRL_TYPE_U32;
 		break;
+	case V4L2_CID_MPEG_VIDEO_VP8_FRAME_HDR:
+		*type = V4L2_CTRL_TYPE_VP8_FRAME_HDR;
+		break;
 	default:
 		*type = V4L2_CTRL_TYPE_INTEGER;
 		break;
@@ -1525,6 +1528,13 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx,
 			return -ERANGE;
 		return 0;
 
+	/* FIXME:just return 0 for now */
+	case V4L2_CTRL_TYPE_PRIVATE:
+		return 0;
+
+	case V4L2_CTRL_TYPE_VP8_FRAME_HDR:
+		return 0;
+
 	default:
 		return -EINVAL;
 	}
@@ -2074,6 +2084,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
 	case V4L2_CTRL_TYPE_U32:
 		elem_size = sizeof(u32);
 		break;
+	case V4L2_CTRL_TYPE_VP8_FRAME_HDR:
+		elem_size = sizeof(struct v4l2_ctrl_vp8_frame_hdr);
+		break;
 	default:
 		if (type < V4L2_CTRL_COMPOUND_TYPES)
 			elem_size = sizeof(s32);
@@ -2098,7 +2111,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
 		handler_set_err(hdl, -ERANGE);
 		return NULL;
 	}
-	if (is_array &&
+	if ((is_array || (flags & V4L2_CTRL_FLAG_REQ_KEEP)) &&
 	    (type == V4L2_CTRL_TYPE_BUTTON ||
 	     type == V4L2_CTRL_TYPE_CTRL_CLASS)) {
 		handler_set_err(hdl, -EINVAL);
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 7d028d1..8aa5812 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1259,6 +1259,9 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
 		case V4L2_PIX_FMT_VC1_ANNEX_G:	descr = "VC-1 (SMPTE 412M Annex G)"; break;
 		case V4L2_PIX_FMT_VC1_ANNEX_L:	descr = "VC-1 (SMPTE 412M Annex L)"; break;
 		case V4L2_PIX_FMT_VP8:		descr = "VP8"; break;
+		case V4L2_PIX_FMT_VP8_FRAME:
+			descr = "VP8 FRAME";
+			break;
 		case V4L2_PIX_FMT_CPIA1:	descr = "GSPCA CPiA YUV"; break;
 		case V4L2_PIX_FMT_WNVA:		descr = "WNVA"; break;
 		case V4L2_PIX_FMT_SN9C10X:	descr = "GSPCA SN9C10X"; break;
diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c
index c331272..aebcc7f 100644
--- a/drivers/media/v4l2-core/videobuf2-dma-contig.c
+++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c
@@ -23,13 +23,16 @@
 
 struct vb2_dc_conf {
 	struct device		*dev;
+	struct dma_attrs	attrs;
 };
 
 struct vb2_dc_buf {
 	struct device			*dev;
 	void				*vaddr;
 	unsigned long			size;
+	void				*cookie;
 	dma_addr_t			dma_addr;
+	struct dma_attrs		attrs;
 	enum dma_data_direction		dma_dir;
 	struct sg_table			*dma_sgt;
 	struct frame_vector		*vec;
@@ -131,7 +134,8 @@ static void vb2_dc_put(void *buf_priv)
 		sg_free_table(buf->sgt_base);
 		kfree(buf->sgt_base);
 	}
-	dma_free_coherent(buf->dev, buf->size, buf->vaddr, buf->dma_addr);
+	dma_free_attrs(buf->dev, buf->size, buf->cookie, buf->dma_addr,
+			&buf->attrs);
 	put_device(buf->dev);
 	kfree(buf);
 }
@@ -143,18 +147,22 @@ static void *vb2_dc_alloc(void *alloc_ctx, unsigned long size,
 	struct device *dev = conf->dev;
 	struct vb2_dc_buf *buf;
 
-	buf = kzalloc(sizeof *buf, GFP_KERNEL);
+	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
 	if (!buf)
 		return ERR_PTR(-ENOMEM);
 
-	buf->vaddr = dma_alloc_coherent(dev, size, &buf->dma_addr,
-						GFP_KERNEL | gfp_flags);
-	if (!buf->vaddr) {
+	buf->attrs = conf->attrs;
+	buf->cookie = dma_alloc_attrs(dev, size, &buf->dma_addr,
+					GFP_KERNEL | gfp_flags, &buf->attrs);
+	if (!buf->cookie) {
 		dev_err(dev, "dma_alloc_coherent of size %ld failed\n", size);
 		kfree(buf);
 		return ERR_PTR(-ENOMEM);
 	}
 
+	if (!dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, &buf->attrs))
+		buf->vaddr = buf->cookie;
+
 	/* Prevent the device from being released while the buffer is used */
 	buf->dev = get_device(dev);
 	buf->size = size;
@@ -185,8 +193,8 @@ static int vb2_dc_mmap(void *buf_priv, struct vm_area_struct *vma)
 	 */
 	vma->vm_pgoff = 0;
 
-	ret = dma_mmap_coherent(buf->dev, vma, buf->vaddr,
-		buf->dma_addr, buf->size);
+	ret = dma_mmap_attrs(buf->dev, vma, buf->cookie,
+		buf->dma_addr, buf->size, &buf->attrs);
 
 	if (ret) {
 		pr_err("Remapping memory failed, error: %d\n", ret);
@@ -329,7 +337,7 @@ static void *vb2_dc_dmabuf_ops_kmap(struct dma_buf *dbuf, unsigned long pgnum)
 {
 	struct vb2_dc_buf *buf = dbuf->priv;
 
-	return buf->vaddr + pgnum * PAGE_SIZE;
+	return buf->vaddr ? buf->vaddr + pgnum * PAGE_SIZE : NULL;
 }
 
 static void *vb2_dc_dmabuf_ops_vmap(struct dma_buf *dbuf)
@@ -368,8 +376,8 @@ static struct sg_table *vb2_dc_get_base_sgt(struct vb2_dc_buf *buf)
 		return NULL;
 	}
 
-	ret = dma_get_sgtable(buf->dev, sgt, buf->vaddr, buf->dma_addr,
-		buf->size);
+	ret = dma_get_sgtable_attrs(buf->dev, sgt, buf->cookie, buf->dma_addr,
+		buf->size, &buf->attrs);
 	if (ret < 0) {
 		dev_err(buf->dev, "failed to get scatterlist from DMA API\n");
 		kfree(sgt);
@@ -448,22 +456,26 @@ static void vb2_dc_put_userptr(void *buf_priv)
  */
 
 #ifdef __arch_pfn_to_dma
-static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn)
+static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev,
+					   unsigned long pfn)
 {
 	return (dma_addr_t)__arch_pfn_to_dma(dev, pfn);
 }
 #elif defined(__pfn_to_bus)
-static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn)
+static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev,
+					   unsigned long pfn)
 {
 	return (dma_addr_t)__pfn_to_bus(pfn);
 }
 #elif defined(__pfn_to_phys)
-static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn)
+static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev,
+					   unsigned long pfn)
 {
 	return (dma_addr_t)__pfn_to_phys(pfn);
 }
 #else
-static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn)
+static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev,
+					   unsigned long pfn)
 {
 	/* really, we cannot do anything better at this point */
 	return (dma_addr_t)(pfn) << PAGE_SHIFT;
@@ -497,7 +509,7 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr,
 		return ERR_PTR(-EINVAL);
 	}
 
-	buf = kzalloc(sizeof *buf, GFP_KERNEL);
+	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
 	if (!buf)
 		return ERR_PTR(-ENOMEM);
 
@@ -721,19 +733,22 @@ const struct vb2_mem_ops vb2_dma_contig_memops = {
 };
 EXPORT_SYMBOL_GPL(vb2_dma_contig_memops);
 
-void *vb2_dma_contig_init_ctx(struct device *dev)
+void *vb2_dma_contig_init_ctx_attrs(struct device *dev,
+				    struct dma_attrs *attrs)
 {
 	struct vb2_dc_conf *conf;
 
-	conf = kzalloc(sizeof *conf, GFP_KERNEL);
+	conf = kzalloc(sizeof(*conf), GFP_KERNEL);
 	if (!conf)
 		return ERR_PTR(-ENOMEM);
 
 	conf->dev = dev;
+	if (attrs)
+		conf->attrs = *attrs;
 
 	return conf;
 }
-EXPORT_SYMBOL_GPL(vb2_dma_contig_init_ctx);
+EXPORT_SYMBOL_GPL(vb2_dma_contig_init_ctx_attrs);
 
 void vb2_dma_contig_cleanup_ctx(void *alloc_ctx)
 {
diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
index 5f9526f..0424cdc 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -46,6 +46,7 @@ struct poll_table_struct;
  * @p_u16:	Pointer to a 16-bit unsigned value.
  * @p_u32:	Pointer to a 32-bit unsigned value.
  * @p_char:	Pointer to a string.
+ * @p_vp8_frame_hdr:	Pointer to a struct v4l2_ctrl_vp8_frame_hdr.
  * @p:		Pointer to a compound value.
  */
 union v4l2_ctrl_ptr {
@@ -55,6 +56,7 @@ union v4l2_ctrl_ptr {
 	u16 *p_u16;
 	u32 *p_u32;
 	char *p_char;
+	struct v4l2_ctrl_vp8_frame_hdr *p_vp8_frame_hdr;
 	void *p;
 };
 
diff --git a/include/media/videobuf2-dma-contig.h b/include/media/videobuf2-dma-contig.h
index c33dfa6..2087c9a 100644
--- a/include/media/videobuf2-dma-contig.h
+++ b/include/media/videobuf2-dma-contig.h
@@ -16,6 +16,8 @@
 #include <media/videobuf2-v4l2.h>
 #include <linux/dma-mapping.h>
 
+struct dma_attrs;
+
 static inline dma_addr_t
 vb2_dma_contig_plane_dma_addr(struct vb2_buffer *vb, unsigned int plane_no)
 {
@@ -24,7 +26,14 @@ vb2_dma_contig_plane_dma_addr(struct vb2_buffer *vb, unsigned int plane_no)
 	return *addr;
 }
 
-void *vb2_dma_contig_init_ctx(struct device *dev);
+void *vb2_dma_contig_init_ctx_attrs(struct device *dev,
+				    struct dma_attrs *attrs);
+
+static inline void *vb2_dma_contig_init_ctx(struct device *dev)
+{
+	return vb2_dma_contig_init_ctx_attrs(dev, NULL);
+}
+
 void vb2_dma_contig_cleanup_ctx(void *alloc_ctx);
 
 extern const struct vb2_mem_ops vb2_dma_contig_memops;
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index 2d225bc..63c65d9 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -520,6 +520,7 @@ enum v4l2_mpeg_video_h264_hierarchical_coding_type {
 };
 #define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER	(V4L2_CID_MPEG_BASE+381)
 #define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_QP	(V4L2_CID_MPEG_BASE+382)
+
 #define V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP	(V4L2_CID_MPEG_BASE+400)
 #define V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP	(V4L2_CID_MPEG_BASE+401)
 #define V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP	(V4L2_CID_MPEG_BASE+402)
@@ -578,6 +579,8 @@ enum v4l2_vp8_golden_frame_sel {
 #define V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP		(V4L2_CID_MPEG_BASE+510)
 #define V4L2_CID_MPEG_VIDEO_VPX_PROFILE			(V4L2_CID_MPEG_BASE+511)
 
+#define V4L2_CID_MPEG_VIDEO_VP8_FRAME_HDR		(V4L2_CID_MPEG_BASE+512)
+
 /*  MPEG-class control IDs specific to the CX2341x driver as defined by V4L2 */
 #define V4L2_CID_MPEG_CX2341X_BASE 				(V4L2_CTRL_CLASS_MPEG | 0x1000)
 #define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE 	(V4L2_CID_MPEG_CX2341X_BASE+0)
@@ -963,4 +966,99 @@ enum v4l2_detect_md_mode {
 #define V4L2_CID_DETECT_MD_THRESHOLD_GRID	(V4L2_CID_DETECT_CLASS_BASE + 3)
 #define V4L2_CID_DETECT_MD_REGION_GRID		(V4L2_CID_DETECT_CLASS_BASE + 4)
 
+
+/* Complex controls */
+
+#define V4L2_VP8_SEGMNT_HDR_FLAG_ENABLED              0x01
+#define V4L2_VP8_SEGMNT_HDR_FLAG_UPDATE_MAP           0x02
+#define V4L2_VP8_SEGMNT_HDR_FLAG_UPDATE_FEATURE_DATA  0x04
+struct v4l2_vp8_sgmnt_hdr {
+	__u8 segment_feature_mode;
+
+	__s8 quant_update[4];
+	__s8 lf_update[4];
+	__u8 segment_probs[3];
+
+	__u8 flags;
+};
+
+#define V4L2_VP8_LF_HDR_ADJ_ENABLE	0x01
+#define V4L2_VP8_LF_HDR_DELTA_UPDATE	0x02
+struct v4l2_vp8_loopfilter_hdr {
+	__u8 type;
+	__u8 level;
+	__u8 sharpness_level;
+	__s8 ref_frm_delta_magnitude[4];
+	__s8 mb_mode_delta_magnitude[4];
+
+	__u8 flags;
+};
+
+struct v4l2_vp8_quantization_hdr {
+	__u8 y_ac_qi;
+	__s8 y_dc_delta;
+	__s8 y2_dc_delta;
+	__s8 y2_ac_delta;
+	__s8 uv_dc_delta;
+	__s8 uv_ac_delta;
+	__u16 dequant_factors[4][3][2];
+};
+
+struct v4l2_vp8_entropy_hdr {
+	__u8 coeff_probs[4][8][3][11];
+	__u8 y_mode_probs[4];
+	__u8 uv_mode_probs[3];
+	__u8 mv_probs[2][19];
+};
+
+#define V4L2_VP8_FRAME_HDR_FLAG_EXPERIMENTAL		0x01
+#define V4L2_VP8_FRAME_HDR_FLAG_SHOW_FRAME		0x02
+#define V4L2_VP8_FRAME_HDR_FLAG_MB_NO_SKIP_COEFF	0x04
+struct v4l2_ctrl_vp8_frame_hdr {
+	/* 0: keyframe, 1: not a keyframe */
+	__u8 key_frame;
+	__u8 version;
+
+	/* Populated also if not a key frame */
+	__u16 width;
+	__u8 horizontal_scale;
+	__u16 height;
+	__u8 vertical_scale;
+
+	struct v4l2_vp8_sgmnt_hdr sgmnt_hdr;
+	struct v4l2_vp8_loopfilter_hdr lf_hdr;
+	struct v4l2_vp8_quantization_hdr quant_hdr;
+	struct v4l2_vp8_entropy_hdr entropy_hdr;
+
+	__u8 sign_bias_golden;
+	__u8 sign_bias_alternate;
+
+	__u8 prob_skip_false;
+	__u8 prob_intra;
+	__u8 prob_last;
+	__u8 prob_gf;
+
+	__u32 first_part_size;
+	__u32 first_part_offset;
+	/*
+	 * Offset in bits of MB data in first partition,
+	 * i.e. bit offset starting from first_part_offset.
+	 */
+	__u32 macroblock_bit_offset;
+
+	__u8 num_dct_parts;
+	__u32 dct_part_sizes[8];
+
+	__u8 bool_dec_range;
+	__u8 bool_dec_value;
+	__u8 bool_dec_count;
+
+	/* v4l2_buffer indices of reference frames */
+	__u32 last_frame;
+	__u32 golden_frame;
+	__u32 alt_frame;
+
+	__u8 flags;
+};
+
 #endif
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 29a6b78..191ca19 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -593,6 +593,7 @@ struct v4l2_pix_format {
 #define V4L2_PIX_FMT_VC1_ANNEX_G v4l2_fourcc('V', 'C', '1', 'G') /* SMPTE 421M Annex G compliant stream */
 #define V4L2_PIX_FMT_VC1_ANNEX_L v4l2_fourcc('V', 'C', '1', 'L') /* SMPTE 421M Annex L compliant stream */
 #define V4L2_PIX_FMT_VP8      v4l2_fourcc('V', 'P', '8', '0') /* VP8 */
+#define V4L2_PIX_FMT_VP8_FRAME	v4l2_fourcc('V', 'P', '8', 'F') /* VP8 parsed frames */
 
 /*  Vendor-specific formats   */
 #define V4L2_PIX_FMT_CPIA1    v4l2_fourcc('C', 'P', 'I', 'A') /* cpia1 YUV */
@@ -1473,6 +1474,7 @@ struct v4l2_ext_control {
 		__u8 __user *p_u8;
 		__u16 __user *p_u16;
 		__u32 __user *p_u32;
+		struct v4l2_ctrl_vp8_frame_hdr __user *p_vp8_frame_hdr;
 		void __user *ptr;
 	};
 } __attribute__ ((packed));
@@ -1517,6 +1519,9 @@ enum v4l2_ctrl_type {
 	V4L2_CTRL_TYPE_U8	     = 0x0100,
 	V4L2_CTRL_TYPE_U16	     = 0x0101,
 	V4L2_CTRL_TYPE_U32	     = 0x0102,
+	V4L2_CTRL_TYPE_VP8_FRAME_HDR	= 0x108,
+
+	V4L2_CTRL_TYPE_PRIVATE       = 0xffff,
 };
 
 /*  Used in the VIDIOC_QUERYCTRL ioctl for querying controls */
-- 
1.9.1



From jung.zhao at rock-chips.com  Tue Jan 26 01:04:05 2016
From: jung.zhao at rock-chips.com (Jung Zhao)
Date: Tue, 26 Jan 2016 17:04:05 +0800
Subject: [PATCH v1 2/3] media: VPU: support Rockchip VPU
In-Reply-To: <1453799046-307-1-git-send-email-jung.zhao@rock-chips.com>
References: <1453799046-307-1-git-send-email-jung.zhao@rock-chips.com>
Message-ID: <1453799046-307-3-git-send-email-jung.zhao@rock-chips.com>

From: zhaojun <jung.zhao at rock-chips.com>

The VPU driver for rk3229 & rk3288 platform.
Now only support VP8 decoder.

Signed-off-by: zhaojun <jung.zhao at rock-chips.com>
---

 drivers/media/platform/rockchip-vpu/Makefile       |   6 +
 drivers/media/platform/rockchip-vpu/rockchip_vpu.c | 799 +++++++++++++++++
 .../platform/rockchip-vpu/rockchip_vpu_common.h    | 439 +++++++++
 .../media/platform/rockchip-vpu/rockchip_vpu_dec.c | 994 +++++++++++++++++++++
 .../media/platform/rockchip-vpu/rockchip_vpu_dec.h |  33 +
 .../media/platform/rockchip-vpu/rockchip_vpu_hw.c  | 278 ++++++
 .../media/platform/rockchip-vpu/rockchip_vpu_hw.h  |  78 ++
 7 files changed, 2627 insertions(+)
 create mode 100644 drivers/media/platform/rockchip-vpu/Makefile
 create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu.c
 create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu_common.h
 create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.c
 create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.h
 create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.c
 create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.h

diff --git a/drivers/media/platform/rockchip-vpu/Makefile b/drivers/media/platform/rockchip-vpu/Makefile
new file mode 100644
index 0000000..5aab094
--- /dev/null
+++ b/drivers/media/platform/rockchip-vpu/Makefile
@@ -0,0 +1,6 @@
+
+obj-$(CONFIG_VIDEO_ROCKCHIP_VPU) += rockchip-vpu.o
+
+rockchip-vpu-y += rockchip_vpu.o \
+		rockchip_vpu_dec.o \
+		rockchip_vpu_hw.o
diff --git a/drivers/media/platform/rockchip-vpu/rockchip_vpu.c b/drivers/media/platform/rockchip-vpu/rockchip_vpu.c
new file mode 100644
index 0000000..e7e7cb5
--- /dev/null
+++ b/drivers/media/platform/rockchip-vpu/rockchip_vpu.c
@@ -0,0 +1,799 @@
+/*
+ * Rockchip VPU codec driver
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *	Tomasz Figa <tfiga at chromium.org>
+ *
+ * Based on s5p-mfc driver by Samsung Electronics Co., Ltd.
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "rockchip_vpu_common.h"
+
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-event.h>
+#include <linux/workqueue.h>
+#include <linux/of.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "rockchip_vpu_dec.h"
+#include "rockchip_vpu_hw.h"
+
+int debug;
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug,
+		 "Debug level - higher value produces more verbose messages");
+
+#define DUMP_FILE "/tmp/vpu.out"
+
+int rockchip_vpu_write(const char *file, void *buf, size_t size)
+{
+	const char __user *p = (__force const char __user *)buf;
+	struct file *filp = filp_open(file ? file : DUMP_FILE,
+			O_CREAT | O_RDWR | O_APPEND, 0644);
+	mm_segment_t fs;
+	int ret;
+
+	if (IS_ERR(filp)) {
+		printk(KERN_ERR "open(%s) failed\n", file ? file : DUMP_FILE);
+		return -ENODEV;
+	}
+
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+
+	filp->f_pos = 0;
+	ret = filp->f_op->write(filp, p, size, &filp->f_pos);
+
+	filp_close(filp, NULL);
+	set_fs(fs);
+
+	return ret;
+}
+
+/*
+ * DMA coherent helpers.
+ */
+
+int rockchip_vpu_aux_buf_alloc(struct rockchip_vpu_dev *vpu,
+			       struct rockchip_vpu_aux_buf *buf, size_t size)
+{
+	buf->cpu = dma_alloc_coherent(vpu->dev, size, &buf->dma, GFP_KERNEL);
+	if (!buf->cpu)
+		return -ENOMEM;
+
+	buf->size = size;
+	return 0;
+}
+
+void rockchip_vpu_aux_buf_free(struct rockchip_vpu_dev *vpu,
+			       struct rockchip_vpu_aux_buf *buf)
+{
+	dma_free_coherent(vpu->dev, buf->size, buf->cpu, buf->dma);
+
+	buf->cpu = NULL;
+	buf->dma = 0;
+	buf->size = 0;
+}
+
+/*
+ * Context scheduling.
+ */
+
+static void rockchip_vpu_prepare_run(struct rockchip_vpu_ctx *ctx)
+{
+	if (ctx->run_ops->prepare_run)
+		ctx->run_ops->prepare_run(ctx);
+}
+
+static void __rockchip_vpu_dequeue_run_locked(struct rockchip_vpu_ctx *ctx)
+{
+	struct rockchip_vpu_buf *src, *dst;
+
+	/*
+	 * Since ctx was dequeued from ready_ctxs list, we know that it has
+	 * at least one buffer in each queue.
+	 */
+	src = list_first_entry(&ctx->src_queue, struct rockchip_vpu_buf, list);
+	dst = list_first_entry(&ctx->dst_queue, struct rockchip_vpu_buf, list);
+
+	list_del(&src->list);
+	list_del(&dst->list);
+
+	ctx->run.src = src;
+	ctx->run.dst = dst;
+}
+
+static void rockchip_vpu_try_run(struct rockchip_vpu_dev *dev)
+{
+	struct rockchip_vpu_ctx *ctx = NULL;
+	unsigned long flags;
+
+	vpu_debug_enter();
+
+	spin_lock_irqsave(&dev->irqlock, flags);
+
+	if (list_empty(&dev->ready_ctxs) ||
+	    test_bit(VPU_SUSPENDED, &dev->state))
+		/* Nothing to do. */
+		goto out;
+
+	if (test_and_set_bit(VPU_RUNNING, &dev->state))
+		/*
+		* The hardware is already running. We will pick another
+		* run after we get the notification in rockchip_vpu_run_done().
+		*/
+		goto out;
+
+	ctx = list_entry(dev->ready_ctxs.next, struct rockchip_vpu_ctx, list);
+
+	list_del_init(&ctx->list);
+	__rockchip_vpu_dequeue_run_locked(ctx);
+
+	dev->current_ctx = ctx;
+
+out:
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+
+	if (ctx) {
+		rockchip_vpu_prepare_run(ctx);
+		rockchip_vpu_run(ctx);
+	}
+
+	vpu_debug_leave();
+}
+
+static void __rockchip_vpu_try_context_locked(struct rockchip_vpu_dev *dev,
+					      struct rockchip_vpu_ctx *ctx)
+{
+	if (!list_empty(&ctx->list))
+		/* Context already queued. */
+		return;
+
+	if (!list_empty(&ctx->dst_queue) && !list_empty(&ctx->src_queue))
+		list_add_tail(&ctx->list, &dev->ready_ctxs);
+}
+
+void rockchip_vpu_run_done(struct rockchip_vpu_ctx *ctx,
+			   enum vb2_buffer_state result)
+{
+	struct rockchip_vpu_dev *dev = ctx->dev;
+	unsigned long flags;
+
+	vpu_debug_enter();
+
+	if (ctx->run_ops->run_done)
+		ctx->run_ops->run_done(ctx, result);
+
+	struct vb2_buffer *src = &ctx->run.src->b.vb2_buf;
+	struct vb2_buffer *dst = &ctx->run.dst->b.vb2_buf;
+
+	to_vb2_v4l2_buffer(dst)->timestamp =
+		to_vb2_v4l2_buffer(src)->timestamp;
+	vb2_buffer_done(&ctx->run.src->b.vb2_buf, result);
+	vb2_buffer_done(&ctx->run.dst->b.vb2_buf, result);
+
+	dev->current_ctx = NULL;
+	wake_up_all(&dev->run_wq);
+
+	spin_lock_irqsave(&dev->irqlock, flags);
+
+	__rockchip_vpu_try_context_locked(dev, ctx);
+	clear_bit(VPU_RUNNING, &dev->state);
+
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+
+	/* Try scheduling another run to see if we have anything left to do. */
+	rockchip_vpu_try_run(dev);
+
+	vpu_debug_leave();
+}
+
+void rockchip_vpu_try_context(struct rockchip_vpu_dev *dev,
+			      struct rockchip_vpu_ctx *ctx)
+{
+	unsigned long flags;
+
+	vpu_debug_enter();
+
+	spin_lock_irqsave(&dev->irqlock, flags);
+
+	__rockchip_vpu_try_context_locked(dev, ctx);
+
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+
+	rockchip_vpu_try_run(dev);
+
+	vpu_debug_enter();
+}
+
+/*
+ * Control registration.
+ */
+
+#define IS_VPU_PRIV(x) ((V4L2_CTRL_ID2WHICH(x) == V4L2_CTRL_CLASS_MPEG) && \
+			  V4L2_CTRL_DRIVER_PRIV(x))
+
+int rockchip_vpu_ctrls_setup(struct rockchip_vpu_ctx *ctx,
+			     const struct v4l2_ctrl_ops *ctrl_ops,
+			     struct rockchip_vpu_control *controls,
+			     unsigned num_ctrls,
+			     const char* const* (*get_menu)(u32))
+{
+	struct v4l2_ctrl_config cfg;
+	int i;
+
+	if (num_ctrls > ARRAY_SIZE(ctx->ctrls)) {
+		vpu_err("context control array not large enough\n");
+		return -ENOSPC;
+	}
+
+	v4l2_ctrl_handler_init(&ctx->ctrl_handler, num_ctrls);
+	if (ctx->ctrl_handler.error) {
+		vpu_err("v4l2_ctrl_handler_init failed\n");
+		return ctx->ctrl_handler.error;
+	}
+
+	for (i = 0; i < num_ctrls; i++) {
+		if (IS_VPU_PRIV(controls[i].id)
+		    || controls[i].id >= V4L2_CID_CUSTOM_BASE
+		    || controls[i].type == V4L2_CTRL_TYPE_PRIVATE) {
+			memset(&cfg, 0, sizeof(struct v4l2_ctrl_config));
+
+			cfg.ops = ctrl_ops;
+			cfg.id = controls[i].id;
+			cfg.min = controls[i].minimum;
+			cfg.max = controls[i].maximum;
+			cfg.max_reqs = controls[i].max_reqs;
+			cfg.def = controls[i].default_value;
+			cfg.name = controls[i].name;
+			cfg.type = controls[i].type;
+			cfg.elem_size = controls[i].elem_size;
+			memcpy(cfg.dims, controls[i].dims, sizeof(cfg.dims));
+
+			if (cfg.type == V4L2_CTRL_TYPE_MENU) {
+				cfg.menu_skip_mask = cfg.menu_skip_mask;
+				cfg.qmenu = get_menu(cfg.id);
+			} else {
+				cfg.step = controls[i].step;
+			}
+
+			ctx->ctrls[i] = v4l2_ctrl_new_custom(
+							     &ctx->ctrl_handler,
+							     &cfg, NULL);
+		} else {
+			if (controls[i].type == V4L2_CTRL_TYPE_MENU) {
+				ctx->ctrls[i] =
+					v4l2_ctrl_new_std_menu
+					(&ctx->ctrl_handler,
+					 ctrl_ops,
+					 controls[i].id,
+					 controls[i].maximum,
+					 0,
+					 controls[i].
+					 default_value);
+			} else {
+				ctx->ctrls[i] =
+					v4l2_ctrl_new_std(&ctx->ctrl_handler,
+							  ctrl_ops,
+							  controls[i].id,
+							  controls[i].minimum,
+							  controls[i].maximum,
+							  controls[i].step,
+							  controls[i].
+							  default_value);
+			}
+		}
+
+		if (ctx->ctrl_handler.error) {
+			vpu_err("Adding control (%d) failed\n", i);
+			return ctx->ctrl_handler.error;
+		}
+
+		if (controls[i].is_volatile && ctx->ctrls[i])
+			ctx->ctrls[i]->flags |= V4L2_CTRL_FLAG_VOLATILE;
+		if (controls[i].is_read_only && ctx->ctrls[i])
+			ctx->ctrls[i]->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+		if (controls[i].can_store && ctx->ctrls[i])
+			ctx->ctrls[i]->flags |= V4L2_CTRL_FLAG_REQ_KEEP;
+	}
+
+	v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
+	ctx->num_ctrls = num_ctrls;
+	return 0;
+}
+
+void rockchip_vpu_ctrls_delete(struct rockchip_vpu_ctx *ctx)
+{
+	int i;
+
+	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+	for (i = 0; i < ctx->num_ctrls; i++)
+		ctx->ctrls[i] = NULL;
+}
+
+/*
+ * V4L2 file operations.
+ */
+
+static int rockchip_vpu_open(struct file *filp)
+{
+	struct video_device *vdev = video_devdata(filp);
+	struct rockchip_vpu_dev *dev = video_drvdata(filp);
+	struct rockchip_vpu_ctx *ctx = NULL;
+	struct vb2_queue *q;
+	int ret = 0;
+
+	/*
+	 * We do not need any extra locking here, because we operate only
+	 * on local data here, except reading few fields from dev, which
+	 * do not change through device's lifetime (which is guaranteed by
+	 * reference on module from open()) and V4L2 internal objects (such
+	 * as vdev and ctx->fh), which have proper locking done in respective
+	 * helper functions used here.
+	 */
+
+	vpu_debug_enter();
+
+	/* Allocate memory for context */
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx) {
+		ret = -ENOMEM;
+		goto err_leave;
+	}
+
+	v4l2_fh_init(&ctx->fh, video_devdata(filp));
+	filp->private_data = &ctx->fh;
+	v4l2_fh_add(&ctx->fh);
+	ctx->dev = dev;
+	INIT_LIST_HEAD(&ctx->src_queue);
+	INIT_LIST_HEAD(&ctx->dst_queue);
+	INIT_LIST_HEAD(&ctx->list);
+
+	if (vdev == dev->vfd_dec) {
+		/* only for decoder */
+		ret = rockchip_vpu_dec_init(ctx);
+		if (ret) {
+			vpu_err("Failed to initialize decoder context\n");
+			goto err_fh_free;
+		}
+	} else {
+		ret = -ENOENT;
+		goto err_fh_free;
+	}
+	ctx->fh.ctrl_handler = &ctx->ctrl_handler;
+
+	/* Init videobuf2 queue for CAPTURE */
+	q = &ctx->vq_dst;
+	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+	q->drv_priv = &ctx->fh;
+	q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+	q->lock = &dev->vpu_mutex;
+	q->buf_struct_size = sizeof(struct rockchip_vpu_buf);
+
+	if (vdev == dev->vfd_dec)
+		q->ops = get_dec_queue_ops();
+
+	q->mem_ops = &vb2_dma_contig_memops;
+	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+	q->v4l2_allow_requests = true;
+	ret = vb2_queue_init(q);
+	if (ret) {
+		vpu_err("Failed to initialize videobuf2 queue(capture)\n");
+		goto err_dec_exit;
+	}
+
+	/* Init videobuf2 queue for OUTPUT */
+	q = &ctx->vq_src;
+	q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+	q->drv_priv = &ctx->fh;
+	q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+	q->lock = &dev->vpu_mutex;
+	q->buf_struct_size = sizeof(struct rockchip_vpu_buf);
+
+	if (vdev == dev->vfd_dec)
+		q->ops = get_dec_queue_ops();
+
+	q->mem_ops = &vb2_dma_contig_memops;
+	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+	q->v4l2_allow_requests = true;
+	ret = vb2_queue_init(q);
+	if (ret) {
+		vpu_err("Failed to initialize videobuf2 queue(output)\n");
+		goto err_vq_dst_release;
+	}
+
+	vpu_debug_leave();
+
+	return 0;
+
+err_vq_dst_release:
+	vb2_queue_release(&ctx->vq_dst);
+err_dec_exit:
+	if (vdev == dev->vfd_dec)
+		rockchip_vpu_dec_exit(ctx);
+err_fh_free:
+	v4l2_fh_del(&ctx->fh);
+	v4l2_fh_exit(&ctx->fh);
+	kfree(ctx);
+err_leave:
+	vpu_debug_leave();
+
+	return ret;
+}
+
+static int rockchip_vpu_release(struct file *filp)
+{
+	struct rockchip_vpu_ctx *ctx = fh_to_ctx(filp->private_data);
+	struct video_device *vdev = video_devdata(filp);
+	struct rockchip_vpu_dev *dev = ctx->dev;
+
+	/*
+	 * No need for extra locking because this was the last reference
+	 * to this file.
+	 */
+
+	vpu_debug_enter();
+
+	/*
+	 * vb2_queue_release() ensures that streaming is stopped, which
+	 * in turn means that there are no frames still being processed
+	 * by hardware.
+	 */
+	vb2_queue_release(&ctx->vq_src);
+	vb2_queue_release(&ctx->vq_dst);
+
+	v4l2_fh_del(&ctx->fh);
+	v4l2_fh_exit(&ctx->fh);
+
+	if (vdev == dev->vfd_dec)
+		rockchip_vpu_dec_exit(ctx);
+
+	kfree(ctx);
+
+	vpu_debug_leave();
+
+	return 0;
+}
+
+static unsigned int rockchip_vpu_poll(struct file *filp,
+				      struct poll_table_struct *wait)
+{
+	struct rockchip_vpu_ctx *ctx = fh_to_ctx(filp->private_data);
+	struct vb2_queue *src_q, *dst_q;
+	struct vb2_buffer *src_vb = NULL, *dst_vb = NULL;
+	unsigned int rc = 0;
+	unsigned long flags;
+
+	vpu_debug_enter();
+
+	src_q = &ctx->vq_src;
+	dst_q = &ctx->vq_dst;
+
+	/*
+	 * There has to be at least one buffer queued on each queued_list, which
+	 * means either in driver already or waiting for driver to claim it
+	 * and start processing.
+	 */
+	if ((!vb2_is_streaming(src_q) || list_empty(&src_q->queued_list)) &&
+	    (!vb2_is_streaming(dst_q) || list_empty(&dst_q->queued_list))) {
+		vpu_debug(0, "src q streaming %d, dst q streaming %d, src list empty(%d), dst list empty(%d)\n",
+			  src_q->streaming, dst_q->streaming,
+			  list_empty(&src_q->queued_list),
+			  list_empty(&dst_q->queued_list));
+		return POLLERR;
+	}
+
+	poll_wait(filp, &ctx->fh.wait, wait);
+	poll_wait(filp, &src_q->done_wq, wait);
+	poll_wait(filp, &dst_q->done_wq, wait);
+
+	if (v4l2_event_pending(&ctx->fh))
+		rc |= POLLPRI;
+
+	spin_lock_irqsave(&src_q->done_lock, flags);
+
+	if (!list_empty(&src_q->done_list))
+		src_vb = list_first_entry(&src_q->done_list, struct vb2_buffer,
+					  done_entry);
+
+	if (src_vb && (src_vb->state == VB2_BUF_STATE_DONE ||
+		       src_vb->state == VB2_BUF_STATE_ERROR))
+		rc |= POLLOUT | POLLWRNORM;
+
+	spin_unlock_irqrestore(&src_q->done_lock, flags);
+
+	spin_lock_irqsave(&dst_q->done_lock, flags);
+
+	if (!list_empty(&dst_q->done_list))
+		dst_vb = list_first_entry(&dst_q->done_list, struct vb2_buffer,
+					  done_entry);
+
+	if (dst_vb && (dst_vb->state == VB2_BUF_STATE_DONE ||
+		       dst_vb->state == VB2_BUF_STATE_ERROR))
+		rc |= POLLIN | POLLRDNORM;
+
+	spin_unlock_irqrestore(&dst_q->done_lock, flags);
+
+	return rc;
+}
+
+static int rockchip_vpu_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	struct rockchip_vpu_ctx *ctx = fh_to_ctx(filp->private_data);
+	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+	int ret;
+
+	vpu_debug_enter();
+
+	if (offset < DST_QUEUE_OFF_BASE) {
+		vpu_debug(4, "mmaping source\n");
+
+		ret = vb2_mmap(&ctx->vq_src, vma);
+	} else {        /* capture */
+		vpu_debug(4, "mmaping destination\n");
+
+		vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT);
+		ret = vb2_mmap(&ctx->vq_dst, vma);
+	}
+
+	vpu_debug_leave();
+
+	return ret;
+}
+
+static const struct v4l2_file_operations rockchip_vpu_fops = {
+	.owner = THIS_MODULE,
+	.open = rockchip_vpu_open,
+	.release = rockchip_vpu_release,
+	.poll = rockchip_vpu_poll,
+	.unlocked_ioctl = video_ioctl2,
+	.mmap = rockchip_vpu_mmap,
+};
+
+/*
+ * Platform driver.
+ */
+
+static void *rockchip_get_drv_data(struct platform_device *pdev);
+
+static int rockchip_vpu_probe(struct platform_device *pdev)
+{
+	struct rockchip_vpu_dev *vpu = NULL;
+	DEFINE_DMA_ATTRS(attrs_novm);
+	struct video_device *vfd;
+	int ret = 0;
+
+	vpu_debug_enter();
+
+	vpu = devm_kzalloc(&pdev->dev, sizeof(*vpu), GFP_KERNEL);
+	if (!vpu)
+		return -ENOMEM;
+
+	vpu->dev = &pdev->dev;
+	vpu->pdev = pdev;
+	mutex_init(&vpu->vpu_mutex);
+	spin_lock_init(&vpu->irqlock);
+	INIT_LIST_HEAD(&vpu->ready_ctxs);
+	init_waitqueue_head(&vpu->run_wq);
+
+	vpu->variant = rockchip_get_drv_data(pdev);
+
+	ret = rockchip_vpu_hw_probe(vpu);
+	if (ret) {
+		dev_err(&pdev->dev, "rockchip_vpu_hw_probe failed\n");
+		goto err_hw_probe;
+	}
+
+	dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &attrs_novm);
+	vpu->alloc_ctx = vb2_dma_contig_init_ctx_attrs(&pdev->dev,
+						       &attrs_novm);
+	if (IS_ERR(vpu->alloc_ctx)) {
+		ret = PTR_ERR(vpu->alloc_ctx);
+		goto err_dma_contig;
+	}
+
+	vpu->alloc_ctx_vm = vb2_dma_contig_init_ctx(&pdev->dev);
+	if (IS_ERR(vpu->alloc_ctx_vm)) {
+		ret = PTR_ERR(vpu->alloc_ctx_vm);
+		goto err_dma_contig_vm;
+	}
+
+	ret = v4l2_device_register(&pdev->dev, &vpu->v4l2_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to register v4l2 device\n");
+		goto err_v4l2_dev_reg;
+	}
+
+	platform_set_drvdata(pdev, vpu);
+
+	/* decoder */
+	vfd = video_device_alloc();
+	if (!vfd) {
+		v4l2_err(&vpu->v4l2_dev, "Failed to allocate video device\n");
+		ret = -ENOMEM;
+		goto err_v4l2_dev_reg;
+	}
+
+	vfd->fops = &rockchip_vpu_fops;
+	vfd->ioctl_ops = get_dec_v4l2_ioctl_ops();
+	vfd->release = video_device_release;
+	vfd->lock = &vpu->vpu_mutex;
+	vfd->v4l2_dev = &vpu->v4l2_dev;
+	vfd->vfl_dir = VFL_DIR_M2M;
+	snprintf(vfd->name, sizeof(vfd->name), "%s", ROCKCHIP_VPU_DEC_NAME);
+	vpu->vfd_dec = vfd;
+
+	video_set_drvdata(vfd, vpu);
+
+	ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+	if (ret) {
+		v4l2_err(&vpu->v4l2_dev, "Failed to register video device\n");
+		video_device_release(vfd);
+		goto err_dec_reg;
+	}
+
+	v4l2_info(&vpu->v4l2_dev,
+		  "Rockchip VPU decoder registered as /vpu/video%d\n",
+		  vfd->num);
+
+	vpu_debug_leave();
+
+	return 0;
+
+err_dec_reg:
+	video_device_release(vpu->vfd_dec);
+err_v4l2_dev_reg:
+	vb2_dma_contig_cleanup_ctx(vpu->alloc_ctx_vm);
+err_dma_contig_vm:
+	vb2_dma_contig_cleanup_ctx(vpu->alloc_ctx);
+err_dma_contig:
+	rockchip_vpu_hw_remove(vpu);
+err_hw_probe:
+	pr_debug("%s-- with error\n", __func__);
+	vpu_debug_leave();
+
+	return ret;
+}
+
+static int rockchip_vpu_remove(struct platform_device *pdev)
+{
+	struct rockchip_vpu_dev *vpu = platform_get_drvdata(pdev);
+
+	vpu_debug_enter();
+
+	v4l2_info(&vpu->v4l2_dev, "Removing %s\n", pdev->name);
+
+	/*
+	 * We are safe here assuming that .remove() got called as
+	 * a result of module removal, which guarantees that all
+	 * contexts have been released.
+	 */
+
+	video_unregister_device(vpu->vfd_dec);
+	v4l2_device_unregister(&vpu->v4l2_dev);
+	vb2_dma_contig_cleanup_ctx(vpu->alloc_ctx_vm);
+	vb2_dma_contig_cleanup_ctx(vpu->alloc_ctx);
+	rockchip_vpu_hw_remove(vpu);
+
+	vpu_debug_leave();
+
+	return 0;
+}
+
+/* Supported VPU variants. */
+static const struct rockchip_vpu_variant rk3288_vpu_variant = {
+	.vpu_type = RK3288_VPU,
+	.name = "Rk3288 vpu",
+	.dec_offset = 0x400,
+	.dec_reg_num = 60 + 41,
+};
+
+static const struct rockchip_vpu_variant rk3229_vpu_variant = {
+	.vpu_type = RK3229_VPU,
+	.name = "Rk3229 vpu",
+	.dec_offset = 0x400,
+	.dec_reg_num = 159,
+};
+
+static struct platform_device_id vpu_driver_ids[] = {
+	{
+		.name = "rk3288-vpu",
+		.driver_data = (unsigned long)&rk3288_vpu_variant,
+	}, {
+		.name = "rk3229-vpu",
+		.driver_data = (unsigned long)&rk3229_vpu_variant,
+	},
+	{ /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(platform, vpu_driver_ids);
+
+#ifdef CONFIG_OF
+static const struct of_device_id of_rockchip_vpu_match[] = {
+	{ .compatible = "rockchip,rk3288-vpu", .data = &rk3288_vpu_variant, },
+	{ .compatible = "rockchip,rk3229-vpu", .data = &rk3229_vpu_variant, },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_rockchip_vpu_match);
+#endif
+
+static void *rockchip_get_drv_data(struct platform_device *pdev)
+{
+	void *driver_data = NULL;
+
+	if (pdev->dev.of_node) {
+		const struct of_device_id *match;
+
+		match = of_match_node(of_rockchip_vpu_match,
+				      pdev->dev.of_node);
+		if (match)
+			driver_data = (void *)match->data;
+	} else {
+		driver_data = (void *)platform_get_device_id(pdev)->driver_data;
+	}
+	return driver_data;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int rockchip_vpu_suspend(struct device *dev)
+{
+	struct rockchip_vpu_dev *vpu = dev_get_drvdata(dev);
+
+	set_bit(VPU_SUSPENDED, &vpu->state);
+	wait_event(vpu->run_wq, vpu->current_ctx == NULL);
+
+	return 0;
+}
+
+static int rockchip_vpu_resume(struct device *dev)
+{
+	struct rockchip_vpu_dev *vpu = dev_get_drvdata(dev);
+
+	clear_bit(VPU_SUSPENDED, &vpu->state);
+	rockchip_vpu_try_run(vpu);
+
+	return 0;
+}
+#endif
+
+static const struct dev_pm_ops rockchip_vpu_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(rockchip_vpu_suspend, rockchip_vpu_resume)
+};
+
+static struct platform_driver rockchip_vpu_driver = {
+	.probe = rockchip_vpu_probe,
+	.remove = rockchip_vpu_remove,
+	.id_table = vpu_driver_ids,
+	.driver = {
+		.name = ROCKCHIP_VPU_NAME,
+		.owner = THIS_MODULE,
+#ifdef CONFIG_OF
+		.of_match_table = of_match_ptr(of_rockchip_vpu_match),
+#endif
+		.pm = &rockchip_vpu_pm_ops,
+	},
+};
+module_platform_driver(rockchip_vpu_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Jung Zhao <jung.zhao at rock-chips.com>");
+MODULE_AUTHOR("Alpha Lin <Alpha.Lin at Rock-Chips.com>");
+MODULE_AUTHOR("Tomasz Figa <tfiga at chromium.org>");
+MODULE_DESCRIPTION("Rockchip VPU codec driver");
diff --git a/drivers/media/platform/rockchip-vpu/rockchip_vpu_common.h b/drivers/media/platform/rockchip-vpu/rockchip_vpu_common.h
new file mode 100644
index 0000000..0d77304
--- /dev/null
+++ b/drivers/media/platform/rockchip-vpu/rockchip_vpu_common.h
@@ -0,0 +1,439 @@
+/*
+ * Rockchip VPU codec driver
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *	Tomasz Figa <tfiga at chromium.org>
+ *
+ * Based on s5p-mfc driver by Samsung Electronics Co., Ltd.
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef ROCKCHIP_VPU_COMMON_H_
+#define ROCKCHIP_VPU_COMMON_H_
+
+/* Enable debugging by default for now. */
+#define DEBUG
+
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+#include <linux/wait.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "rockchip_vpu_hw.h"
+
+#define ROCKCHIP_VPU_NAME		"rockchip-vpu"
+#define ROCKCHIP_VPU_DEC_NAME		"rockchip-vpu-dec"
+
+#define V4L2_CID_CUSTOM_BASE		(V4L2_CID_USER_BASE | 0x1000)
+
+#define DST_QUEUE_OFF_BASE		(TASK_SIZE / 2)
+
+#define ROCKCHIP_VPU_MAX_CTRLS		32
+
+#define MB_DIM				16
+#define MB_WIDTH(x_size)		DIV_ROUND_UP(x_size, MB_DIM)
+#define MB_HEIGHT(y_size)		DIV_ROUND_UP(y_size, MB_DIM)
+
+struct rockchip_vpu_ctx;
+struct rockchip_vpu_codec_ops;
+
+/**
+ * struct rockchip_vpu_variant - information about VPU hardware variant
+ *
+ * @hw_id:		Top 16 bits (product ID) of hardware ID register.
+ * @vpu_type:		Vpu type.
+ * @name:		Vpu name.
+ * @dec_offset:		Offset from VPU base to decoder registers.
+ * @dec_reg_num:	Number of registers of decoder block.
+ */
+struct rockchip_vpu_variant {
+	enum rockchip_vpu_type vpu_type;
+	char *name;
+	unsigned dec_offset;
+	unsigned dec_reg_num;
+};
+
+/**
+ * enum rockchip_vpu_codec_mode - codec operating mode.
+ * @RK_VPU_CODEC_NONE:	No operating mode. Used for RAW video formats.
+ * @RK3288_VPU_CODEC_VP8D:	Rk3288 VP8 decoder.
+ * @RK3229_VPU_CODEC_VP8D:	Rk3229 VP8 decoder.
+ */
+enum rockchip_vpu_codec_mode {
+	RK_VPU_CODEC_NONE = -1,
+	RK3288_VPU_CODEC_VP8D,
+	RK3229_VPU_CODEC_VP8D,
+};
+
+/**
+ * enum rockchip_vpu_plane - indices of planes inside a VB2 buffer.
+ * @PLANE_Y:		Plane containing luminance data (also denoted as Y).
+ * @PLANE_CB_CR:	Plane containing interleaved chrominance data (also
+ *			denoted as CbCr).
+ * @PLANE_CB:		Plane containing CB part of chrominance data.
+ * @PLANE_CR:		Plane containing CR part of chrominance data.
+ */
+enum rockchip_vpu_plane {
+	PLANE_Y		= 0,
+	PLANE_CB_CR	= 1,
+	PLANE_CB	= 1,
+	PLANE_CR	= 2,
+};
+
+/**
+ * struct rockchip_vpu_buf - Private data related to each VB2 buffer.
+ * @vb:			Pointer to related VB2 buffer.
+ * @list:		List head for queuing in buffer queue.
+ * @flags:		Buffer state. See enum rockchip_vpu_buf_flags.
+ */
+struct rockchip_vpu_buf {
+	struct vb2_v4l2_buffer b;
+	struct list_head list;
+	/* Mode-specific data. */
+};
+
+/**
+ * enum rockchip_vpu_state - bitwise flags indicating hardware state.
+ * @VPU_RUNNING:	The hardware has been programmed for operation
+ *			and is running at the moment.
+ * @VPU_SUSPENDED:	System is entering sleep state and no more runs
+ *			should be executed on hardware.
+ */
+enum rockchip_vpu_state {
+	VPU_RUNNING	= BIT(0),
+	VPU_SUSPENDED	= BIT(1),
+};
+
+/**
+ * struct rockchip_vpu_dev - driver data
+ * @v4l2_dev:		V4L2 device to register video devices for.
+ * @vfd_dec:		Video device for decoder.
+ * @pdev:		Pointer to VPU platform device.
+ * @dev:		Pointer to device for convenient logging using
+ *			dev_ macros.
+ * @alloc_ctx:		VB2 allocator context
+ *			(for allocations without kernel mapping).
+ * @alloc_ctx_vm:	VB2 allocator context
+ *			(for allocations with kernel mapping).
+ * @aclk:		Handle of ACLK clock.
+ * @hclk:		Handle of HCLK clock.
+ * @base:		Mapped address of VPU registers.
+ * @dec_base:		Mapped address of VPU decoder register for convenience.
+ * @mapping:		DMA IOMMU mapping.
+ * @vpu_mutex:		Mutex to synchronize V4L2 calls.
+ * @irqlock:		Spinlock to synchronize access to data structures
+ *			shared with interrupt handlers.
+ * @state:		Device state.
+ * @ready_ctxs:		List of contexts ready to run.
+ * @variant:		Hardware variant-specific parameters.
+ * @current_ctx:	Context being currently processed by hardware.
+ * @run_wq:		Wait queue to wait for run completion.
+ * @watchdog_work:	Delayed work for hardware timeout handling.
+ */
+struct rockchip_vpu_dev {
+	struct v4l2_device v4l2_dev;
+	struct video_device *vfd_dec;
+	struct platform_device *pdev;
+	struct device *dev;
+	void *alloc_ctx;
+	void *alloc_ctx_vm;
+	struct clk *aclk;
+	struct clk *hclk;
+	void __iomem *base;
+	void __iomem *dec_base;
+	struct dma_iommu_mapping *mapping;
+
+	struct mutex vpu_mutex; /* video_device lock */
+	spinlock_t irqlock;
+	unsigned long state;
+	struct list_head ready_ctxs;
+	const struct rockchip_vpu_variant *variant;
+	struct rockchip_vpu_ctx *current_ctx;
+	wait_queue_head_t run_wq;
+	struct delayed_work watchdog_work;
+};
+
+/**
+ * struct rockchip_vpu_run_ops - per context operations on run data.
+ * @prepare_run:	Called when the context was selected for running
+ *			to prepare operating mode specific data.
+ * @run_done:		Called when hardware completed the run to collect
+ *			operating mode specific data from hardware and
+ *			finalize the processing.
+ */
+struct rockchip_vpu_run_ops {
+	void (*prepare_run)(struct rockchip_vpu_ctx *);
+	void (*run_done)(struct rockchip_vpu_ctx *, enum vb2_buffer_state);
+};
+
+/**
+ * struct rockchip_vpu_vp8d_run - per-run data specific to VP8
+ * decoding.
+ * @frame_hdr: Pointer to a buffer containing per-run frame data which
+ *			is needed by setting vpu register.
+ */
+struct rockchip_vpu_vp8d_run {
+	const struct v4l2_ctrl_vp8_frame_hdr *frame_hdr;
+};
+
+/**
+ * struct rockchip_vpu_run - per-run data for hardware code.
+ * @src:		Source buffer to be processed.
+ * @dst:		Destination buffer to be processed.
+ * @priv_src:		Hardware private source buffer.
+ * @priv_dst:		Hardware private destination buffer.
+ */
+struct rockchip_vpu_run {
+	/* Generic for more than one operating mode. */
+	struct rockchip_vpu_buf *src;
+	struct rockchip_vpu_buf *dst;
+
+	struct rockchip_vpu_aux_buf priv_src;
+	struct rockchip_vpu_aux_buf priv_dst;
+
+	/* Specific for particular operating modes. */
+	union {
+		struct rockchip_vpu_vp8d_run vp8d;
+		/* Other modes will need different data. */
+	};
+};
+
+/**
+ * struct rockchip_vpu_ctx - Context (instance) private data.
+ *
+ * @dev:		VPU driver data to which the context belongs.
+ * @fh:			V4L2 file handler.
+ *
+ * @vpu_src_fmt:	Descriptor of active source format.
+ * @src_fmt:		V4L2 pixel format of active source format.
+ * @vpu_dst_fmt:	Descriptor of active destination format.
+ * @dst_fmt:		V4L2 pixel format of active destination format.
+ *
+ * @vq_src:		Videobuf2 source queue.
+ * @src_queue:		Internal source buffer queue.
+ * @src_crop:		Configured source crop rectangle (encoder-only).
+ * @vq_dst:		Videobuf2 destination queue
+ * @dst_queue:		Internal destination buffer queue.
+ * @dst_bufs:		Private buffers wrapping VB2 buffers (destination).
+ *
+ * @ctrls:		Array containing pointer to registered controls.
+ * @ctrl_handler:	Control handler used to register controls.
+ * @num_ctrls:		Number of registered controls.
+ *
+ * @list:		List head for queue of ready contexts.
+ *
+ * @run:		Structure containing data about currently scheduled
+ *			processing run.
+ * @run_ops:		Set of operations related to currently scheduled run.
+ * @hw:			Structure containing hardware-related context.
+ */
+struct rockchip_vpu_ctx {
+	struct rockchip_vpu_dev *dev;
+	struct v4l2_fh fh;
+
+	/* Format info */
+	struct rockchip_vpu_fmt *vpu_src_fmt;
+	struct v4l2_pix_format_mplane src_fmt;
+	struct rockchip_vpu_fmt *vpu_dst_fmt;
+	struct v4l2_pix_format_mplane dst_fmt;
+
+	/* VB2 queue data */
+	struct vb2_queue vq_src;
+	struct list_head src_queue;
+	struct v4l2_rect src_crop;
+	struct vb2_queue vq_dst;
+	struct list_head dst_queue;
+	struct vb2_buffer *dst_bufs[VIDEO_MAX_FRAME];
+
+	/* Controls */
+	struct v4l2_ctrl *ctrls[ROCKCHIP_VPU_MAX_CTRLS];
+	struct v4l2_ctrl_handler ctrl_handler;
+	unsigned num_ctrls;
+
+	/* Various runtime data */
+	struct list_head list;
+
+	struct rockchip_vpu_run run;
+	const struct rockchip_vpu_run_ops *run_ops;
+	struct rockchip_vpu_hw_ctx hw;
+};
+
+/**
+ * struct rockchip_vpu_fmt - information about supported video formats.
+ * @name:	Human readable name of the format.
+ * @fourcc:	FourCC code of the format. See V4L2_PIX_FMT_*.
+ * @vpu_type:	Vpu_type;
+ * @codec_mode:	Codec mode related to this format. See
+ *		enum rockchip_vpu_codec_mode.
+ * @num_planes:	Number of planes used by this format.
+ * @depth:	Depth of each plane in bits per pixel.
+ */
+struct rockchip_vpu_fmt {
+	char *name;
+	u32 fourcc;
+	enum rockchip_vpu_type vpu_type;
+	enum rockchip_vpu_codec_mode codec_mode;
+	int num_planes;
+	u8 depth[VIDEO_MAX_PLANES];
+};
+
+/**
+ * struct rockchip_vpu_control - information about controls to be registered.
+ * @id:			Control ID.
+ * @type:		Type of the control.
+ * @name:		Human readable name of the control.
+ * @minimum:		Minimum value of the control.
+ * @maximum:		Maximum value of the control.
+ * @step:		Control value increase step.
+ * @menu_skip_mask:	Mask of invalid menu positions.
+ * @default_value:	Initial value of the control.
+ * @max_reqs:		Maximum number of configration request.
+ * @dims:		Size of each dimension of compound control.
+ * @elem_size:		Size of individual element of compound control.
+ * @is_volatile:	Control is volatile.
+ * @is_read_only:	Control is read-only.
+ * @can_store:		Control uses configuration stores.
+ *
+ * See also struct v4l2_ctrl_config.
+ */
+struct rockchip_vpu_control {
+	u32 id;
+
+	enum v4l2_ctrl_type type;
+	const char *name;
+	s32 minimum;
+	s32 maximum;
+	s32 step;
+	u32 menu_skip_mask;
+	s32 default_value;
+	s32 max_reqs;
+	u32 dims[V4L2_CTRL_MAX_DIMS];
+	u32 elem_size;
+
+	bool is_volatile:1;
+	bool is_read_only:1;
+	bool can_store:1;
+};
+
+/* Logging helpers */
+
+/**
+ * debug - Module parameter to control level of debugging messages.
+ *
+ * Level of debugging messages can be controlled by bits of module parameter
+ * called "debug". Meaning of particular bits is as follows:
+ *
+ * bit 0 - global information: mode, size, init, release
+ * bit 1 - each run start/result information
+ * bit 2 - contents of small controls from userspace
+ * bit 3 - contents of big controls from userspace
+ * bit 4 - detail fmt, ctrl, buffer q/dq information
+ * bit 5 - detail function enter/leave trace information
+ * bit 6 - register write/read information
+ */
+extern int debug;
+
+#define vpu_debug(level, fmt, args...)				\
+	do {							\
+		if (debug & BIT(level))				\
+			pr_err("%s:%d: " fmt,	                \
+			       __func__, __LINE__, ##args);	\
+	} while (0)
+
+#define vpu_debug_enter()	vpu_debug(5, "enter\n")
+#define vpu_debug_leave()	vpu_debug(5, "leave\n")
+
+#define vpu_err(fmt, args...)					\
+	pr_err("%s:%d: " fmt, __func__, __LINE__, ##args)
+
+static inline char *fmt2str(u32 fmt, char *str)
+{
+	char a = fmt & 0xFF;
+	char b = (fmt >> 8) & 0xFF;
+	char c = (fmt >> 16) & 0xFF;
+	char d = (fmt >> 24) & 0xFF;
+
+	sprintf(str, "%c%c%c%c", a, b, c, d);
+
+	return str;
+}
+
+/* Structure access helpers. */
+static inline struct rockchip_vpu_ctx *fh_to_ctx(struct v4l2_fh *fh)
+{
+	return container_of(fh, struct rockchip_vpu_ctx, fh);
+}
+
+static inline struct rockchip_vpu_ctx *ctrl_to_ctx(struct v4l2_ctrl *ctrl)
+{
+	return container_of(ctrl->handler,
+			    struct rockchip_vpu_ctx, ctrl_handler);
+}
+
+static inline struct rockchip_vpu_buf *vb_to_buf(struct vb2_buffer *vb)
+{
+	return container_of(to_vb2_v4l2_buffer(vb), struct rockchip_vpu_buf, b);
+}
+
+static inline bool rockchip_vpu_ctx_is_encoder(struct rockchip_vpu_ctx *ctx)
+{
+	return ctx->vpu_dst_fmt->codec_mode != RK_VPU_CODEC_NONE;
+}
+
+int rockchip_vpu_ctrls_setup(struct rockchip_vpu_ctx *ctx,
+			     const struct v4l2_ctrl_ops *ctrl_ops,
+			     struct rockchip_vpu_control *controls,
+			     unsigned num_ctrls,
+			     const char* const* (*get_menu)(u32));
+void rockchip_vpu_ctrls_delete(struct rockchip_vpu_ctx *ctx);
+
+void rockchip_vpu_try_context(struct rockchip_vpu_dev *dev,
+			      struct rockchip_vpu_ctx *ctx);
+
+void rockchip_vpu_run_done(struct rockchip_vpu_ctx *ctx,
+			   enum vb2_buffer_state result);
+
+int rockchip_vpu_aux_buf_alloc(struct rockchip_vpu_dev *vpu,
+			       struct rockchip_vpu_aux_buf *buf, size_t size);
+void rockchip_vpu_aux_buf_free(struct rockchip_vpu_dev *vpu,
+			       struct rockchip_vpu_aux_buf *buf);
+
+static inline void vdpu_write_relaxed(struct rockchip_vpu_dev *vpu,
+				      u32 val, u32 reg)
+{
+	vpu_debug(6, "MARK: set reg[%03d]: %08x\n", reg / 4, val);
+	writel_relaxed(val, vpu->dec_base + reg);
+}
+
+static inline void vdpu_write(struct rockchip_vpu_dev *vpu, u32 val, u32 reg)
+{
+	vpu_debug(6, "MARK: set reg[%03d]: %08x\n", reg / 4, val);
+	writel(val, vpu->dec_base + reg);
+}
+
+static inline u32 vdpu_read(struct rockchip_vpu_dev *vpu, u32 reg)
+{
+	u32 val = readl(vpu->dec_base + reg);
+
+	vpu_debug(6, "MARK: get reg[%03d]: %08x\n", reg / 4, val);
+	return val;
+}
+
+int rockchip_vpu_write(const char *file, void *buf, size_t size);
+
+#endif /* ROCKCHIP_VPU_COMMON_H_ */
diff --git a/drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.c b/drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.c
new file mode 100644
index 0000000..33e9a89
--- /dev/null
+++ b/drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.c
@@ -0,0 +1,994 @@
+/*
+ * Rockchip VPU codec driver
+ *
+ * Copyright (C) 2014 Rockchip Electronics Co., Ltd.
+ *	Hertz Wong <hertz.wong at rock-chips.com>
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *	Tomasz Figa <tfiga at chromium.org>
+ *
+ * Based on s5p-mfc driver by Samsung Electronics Co., Ltd.
+ *
+ * Copyright (C) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "rockchip_vpu_common.h"
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-sg.h>
+
+#include "rockchip_vpu_dec.h"
+#include "rockchip_vpu_hw.h"
+
+#define DEF_SRC_FMT_DEC				V4L2_PIX_FMT_H264_SLICE
+#define DEF_DST_FMT_DEC				V4L2_PIX_FMT_NV12
+
+#define ROCKCHIP_DEC_MIN_WIDTH			48U
+#define ROCKCHIP_DEC_MAX_WIDTH			3840U
+#define ROCKCHIP_DEC_MIN_HEIGHT			48U
+#define ROCKCHIP_DEC_MAX_HEIGHT			2160U
+
+static struct rockchip_vpu_fmt formats[] = {
+	{
+		.name = "4:2:0 1 plane Y/CbCr",
+		.fourcc = V4L2_PIX_FMT_NV12,
+		.vpu_type = RK_VPU_NONE,
+		.codec_mode = RK_VPU_CODEC_NONE,
+		.num_planes = 1,
+		.depth = { 12 },
+	},
+	{
+		.name = "Frames of VP8 Decoded Stream",
+		.fourcc = V4L2_PIX_FMT_VP8_FRAME,
+		.vpu_type = RK3288_VPU,
+		.codec_mode = RK3288_VPU_CODEC_VP8D,
+		.num_planes = 1,
+	},
+	{
+		.name = "Frames of VP8 Decoded Stream",
+		.fourcc = V4L2_PIX_FMT_VP8_FRAME,
+		.vpu_type = RK3229_VPU,
+		.codec_mode = RK3229_VPU_CODEC_VP8D,
+		.num_planes = 1,
+	},
+};
+
+static struct rockchip_vpu_fmt *find_format(u32 fourcc, bool bitstream,
+					    struct rockchip_vpu_dev *dev)
+{
+	unsigned int i;
+
+	vpu_debug_enter();
+
+	for (i = 0; i < ARRAY_SIZE(formats); i++) {
+		if (formats[i].fourcc != fourcc)
+			continue;
+		if (bitstream && formats[i].codec_mode != RK_VPU_CODEC_NONE
+		    && formats[i].vpu_type == dev->variant->vpu_type)
+			return &formats[i];
+		if (!bitstream && formats[i].codec_mode == RK_VPU_CODEC_NONE)
+			return &formats[i];
+	}
+
+	return NULL;
+}
+
+/* Indices of controls that need to be accessed directly. */
+enum {
+	ROCKCHIP_VPU_DEC_CTRL_VP8_FRAME_HDR,
+};
+
+static struct rockchip_vpu_control controls[0];
+
+static inline const void *get_ctrl_ptr(struct rockchip_vpu_ctx *ctx,
+				       unsigned id)
+{
+	struct v4l2_ctrl *ctrl = ctx->ctrls[id];
+
+	return ctrl->p_cur.p;
+}
+
+/* Query capabilities of the device */
+static int vidioc_querycap(struct file *file, void *priv,
+			   struct v4l2_capability *cap)
+{
+	struct rockchip_vpu_dev *dev = video_drvdata(file);
+
+	vpu_debug_enter();
+
+	strlcpy(cap->driver, ROCKCHIP_VPU_DEC_NAME, sizeof(cap->driver));
+	strlcpy(cap->card, dev->pdev->name, sizeof(cap->card));
+	strlcpy(cap->bus_info, "platform:" ROCKCHIP_VPU_NAME,
+		sizeof(cap->bus_info));
+
+	/*
+	 * This is only a mem-to-mem video device. The capture and output
+	 * device capability flags are left only for backward compatibility
+	 * and are scheduled for removal.
+	 */
+	cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING |
+	    V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+	vpu_debug_leave();
+
+	return 0;
+}
+
+static int vidioc_enum_framesizes(struct file *file, void *prov,
+				  struct v4l2_frmsizeenum *fsize)
+{
+	struct rockchip_vpu_dev *dev = video_drvdata(file);
+	struct v4l2_frmsize_stepwise *s = &fsize->stepwise;
+	struct rockchip_vpu_fmt *fmt;
+
+	if (fsize->index != 0) {
+		vpu_debug(0, "invalid frame size index (expected 0, got %d)\n",
+				fsize->index);
+		return -EINVAL;
+	}
+
+	fmt = find_format(fsize->pixel_format, true, dev);
+	if (!fmt) {
+		vpu_debug(0, "unsupported bitstream format (%08x)\n",
+				fsize->pixel_format);
+		return -EINVAL;
+	}
+
+	fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+
+	s->min_width = ROCKCHIP_DEC_MIN_WIDTH;
+	s->max_width = ROCKCHIP_DEC_MAX_WIDTH;
+	s->step_width = MB_DIM;
+	s->min_height = ROCKCHIP_DEC_MIN_HEIGHT;
+	s->max_height = ROCKCHIP_DEC_MAX_HEIGHT;
+	s->step_height = MB_DIM;
+
+	return 0;
+}
+
+static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool out,
+			   struct rockchip_vpu_dev *dev)
+{
+	struct rockchip_vpu_fmt *fmt;
+	int i, j = 0;
+
+	vpu_debug_enter();
+
+	for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+		if (out && formats[i].codec_mode == RK_VPU_CODEC_NONE)
+			continue;
+		else if (!out && (formats[i].codec_mode != RK_VPU_CODEC_NONE))
+			continue;
+		else if (formats[i].vpu_type != dev->variant->vpu_type &&
+			 formats[i].vpu_type != RK_VPU_NONE)
+			continue;
+
+		if (j == f->index) {
+			fmt = &formats[i];
+			strlcpy(f->description, fmt->name,
+				sizeof(f->description));
+			f->pixelformat = fmt->fourcc;
+
+			f->flags = 0;
+			if (formats[i].codec_mode != RK_VPU_CODEC_NONE)
+				f->flags |= V4L2_FMT_FLAG_COMPRESSED;
+
+			vpu_debug_leave();
+
+			return 0;
+		}
+
+		++j;
+	}
+
+	vpu_debug_leave();
+
+	return -EINVAL;
+}
+
+static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *priv,
+					  struct v4l2_fmtdesc *f)
+{
+	struct rockchip_vpu_dev *dev = video_drvdata(file);
+
+	return vidioc_enum_fmt(f, false, dev);
+}
+
+static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *priv,
+					  struct v4l2_fmtdesc *f)
+{
+	struct rockchip_vpu_dev *dev = video_drvdata(file);
+
+	return vidioc_enum_fmt(f, true, dev);
+}
+
+static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+	struct rockchip_vpu_ctx *ctx = fh_to_ctx(priv);
+
+	vpu_debug_enter();
+
+	vpu_debug(4, "f->type = %d\n", f->type);
+
+	switch (f->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		f->fmt.pix_mp = ctx->dst_fmt;
+		break;
+
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		f->fmt.pix_mp = ctx->src_fmt;
+		break;
+
+	default:
+		vpu_err("invalid buf type\n");
+		return -EINVAL;
+	}
+
+	vpu_debug_leave();
+
+	return 0;
+}
+
+static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+	struct rockchip_vpu_dev *dev = video_drvdata(file);
+	struct rockchip_vpu_fmt *fmt;
+	struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
+	char str[5];
+
+	vpu_debug_enter();
+
+	switch (f->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		vpu_debug(4, "%s\n", fmt2str(f->fmt.pix_mp.pixelformat, str));
+
+		fmt = find_format(pix_fmt_mp->pixelformat, true, dev);
+		if (!fmt) {
+			vpu_err("failed to try output format\n");
+			return -EINVAL;
+		}
+
+		if (pix_fmt_mp->plane_fmt[0].sizeimage == 0) {
+			vpu_err("sizeimage of output format must be given\n");
+			return -EINVAL;
+		}
+
+		pix_fmt_mp->plane_fmt[0].bytesperline = 0;
+		break;
+
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		vpu_debug(4, "%s\n", fmt2str(f->fmt.pix_mp.pixelformat, str));
+
+		fmt = find_format(pix_fmt_mp->pixelformat, false, dev);
+		if (!fmt) {
+			vpu_err("failed to try capture format\n");
+			return -EINVAL;
+		}
+
+		if (fmt->num_planes != pix_fmt_mp->num_planes) {
+			vpu_err("plane number mismatches on capture format\n");
+			return -EINVAL;
+		}
+
+		/* Limit to hardware min/max. */
+		pix_fmt_mp->width = clamp(pix_fmt_mp->width,
+					  ROCKCHIP_DEC_MIN_WIDTH,
+					  ROCKCHIP_DEC_MAX_WIDTH);
+		pix_fmt_mp->height = clamp(pix_fmt_mp->height,
+					   ROCKCHIP_DEC_MIN_HEIGHT,
+					   ROCKCHIP_DEC_MAX_HEIGHT);
+
+		/* Round up to macroblocks. */
+		pix_fmt_mp->width = round_up(pix_fmt_mp->width, MB_DIM);
+		pix_fmt_mp->height = round_up(pix_fmt_mp->height, MB_DIM);
+		break;
+
+	default:
+		vpu_err("invalid buf type\n");
+		return -EINVAL;
+	}
+
+	vpu_debug_leave();
+
+	return 0;
+}
+
+static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+	struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
+	struct rockchip_vpu_ctx *ctx = fh_to_ctx(priv);
+	unsigned int mb_width, mb_height;
+	struct rockchip_vpu_dev *dev = ctx->dev;
+	struct rockchip_vpu_fmt *fmt;
+	int ret = 0;
+	int i;
+
+	vpu_debug_enter();
+
+	switch (f->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		/* Change not allowed if any queue is streaming. */
+		if (vb2_is_streaming(&ctx->vq_src)
+		    || vb2_is_streaming(&ctx->vq_dst)) {
+			ret = -EBUSY;
+			goto out;
+		}
+		/*
+		 * Pixel format change is not allowed when the other queue has
+		 * buffers allocated.
+		 */
+		if (vb2_is_busy(&ctx->vq_dst)
+		    && pix_fmt_mp->pixelformat != ctx->src_fmt.pixelformat) {
+			ret = -EBUSY;
+			goto out;
+		}
+
+		ret = vidioc_try_fmt(file, priv, f);
+		if (ret)
+			goto out;
+
+		ctx->vpu_src_fmt = find_format(pix_fmt_mp->pixelformat,
+					       true, dev);
+		ctx->src_fmt = *pix_fmt_mp;
+		break;
+
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		/*
+		 * Change not allowed if this queue is streaming.
+		 *
+		 * NOTE: We allow changes with source queue streaming
+		 * to support resolution change in decoded stream.
+		 */
+		if (vb2_is_streaming(&ctx->vq_dst)) {
+			ret = -EBUSY;
+			goto out;
+		}
+		/*
+		 * Pixel format change is not allowed when the other queue has
+		 * buffers allocated.
+		 */
+		if (vb2_is_busy(&ctx->vq_src)
+		    && pix_fmt_mp->pixelformat != ctx->dst_fmt.pixelformat) {
+			ret = -EBUSY;
+			goto out;
+		}
+
+		ret = vidioc_try_fmt(file, priv, f);
+		if (ret)
+			goto out;
+
+		fmt = find_format(pix_fmt_mp->pixelformat, false, dev);
+		ctx->vpu_dst_fmt = fmt;
+
+		mb_width = MB_WIDTH(pix_fmt_mp->width);
+		mb_height = MB_HEIGHT(pix_fmt_mp->height);
+
+		vpu_debug(0, "CAPTURE codec mode: %d\n", fmt->codec_mode);
+		vpu_debug(0, "fmt - w: %d, h: %d, mb - w: %d, h: %d\n",
+			  pix_fmt_mp->width, pix_fmt_mp->height,
+			  mb_width, mb_height);
+
+		for (i = 0; i < fmt->num_planes; ++i) {
+			pix_fmt_mp->plane_fmt[i].bytesperline =
+				mb_width * MB_DIM * fmt->depth[i] / 8;
+			pix_fmt_mp->plane_fmt[i].sizeimage =
+				pix_fmt_mp->plane_fmt[i].bytesperline
+				* mb_height * MB_DIM;
+			/*
+			 * All of multiplanar formats we support have chroma
+			 * planes subsampled by 2.
+			 */
+			if (i != 0)
+				pix_fmt_mp->plane_fmt[i].sizeimage /= 2;
+		}
+
+		ctx->dst_fmt = *pix_fmt_mp;
+		break;
+
+	default:
+		vpu_err("invalid buf type\n");
+		return -EINVAL;
+	}
+
+out:
+	vpu_debug_leave();
+
+	return ret;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+			  struct v4l2_requestbuffers *reqbufs)
+{
+	struct rockchip_vpu_ctx *ctx = fh_to_ctx(priv);
+	int ret;
+
+	vpu_debug_enter();
+
+	switch (reqbufs->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
+		if (ret != 0) {
+			vpu_err("error in vb2_reqbufs() for E(S)\n");
+			goto out;
+		}
+		break;
+
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+		if (ret != 0) {
+			vpu_err("error in vb2_reqbufs() for E(D)\n");
+			goto out;
+		}
+		break;
+
+	default:
+		vpu_err("invalid buf type\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+out:
+	vpu_debug_leave();
+
+	return ret;
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+			   struct v4l2_buffer *buf)
+{
+	struct rockchip_vpu_ctx *ctx = fh_to_ctx(priv);
+	int ret;
+
+	vpu_debug_enter();
+
+	switch (buf->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		ret = vb2_querybuf(&ctx->vq_dst, buf);
+		if (ret != 0) {
+			vpu_err("error in vb2_querybuf() for E(D)\n");
+			goto out;
+		}
+
+		buf->m.planes[0].m.mem_offset += DST_QUEUE_OFF_BASE;
+		break;
+
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		ret = vb2_querybuf(&ctx->vq_src, buf);
+		if (ret != 0) {
+			vpu_err("error in vb2_querybuf() for E(S)\n");
+			goto out;
+		}
+		break;
+
+	default:
+		vpu_err("invalid buf type\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+out:
+	vpu_debug_leave();
+
+	return ret;
+}
+
+/* Queue a buffer */
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+	struct rockchip_vpu_ctx *ctx = fh_to_ctx(priv);
+	int ret;
+	int i;
+
+	vpu_debug_enter();
+
+	for (i = 0; i < buf->length; i++)
+		vpu_debug(4, "plane[%d]->length %d bytesused %d\n",
+				i, buf->m.planes[i].length,
+				buf->m.planes[i].bytesused);
+
+	switch (buf->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		ret = vb2_qbuf(&ctx->vq_src, buf);
+		vpu_debug(4, "OUTPUT_MPLANE : vb2_qbuf return %d\n", ret);
+		break;
+
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		ret = vb2_qbuf(&ctx->vq_dst, buf);
+		vpu_debug(4, "CAPTURE_MPLANE: vb2_qbuf return %d\n", ret);
+		break;
+
+	default:
+		ret = -EINVAL;
+	}
+
+	vpu_debug_leave();
+
+	return ret;
+}
+
+/* Dequeue a buffer */
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+	struct rockchip_vpu_ctx *ctx = fh_to_ctx(priv);
+	int ret;
+
+	vpu_debug_enter();
+
+	switch (buf->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		ret = vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK);
+		break;
+
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		ret = vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK);
+		break;
+
+	default:
+		ret = -EINVAL;
+	}
+
+	vpu_debug_leave();
+
+	return ret;
+}
+
+/* Export DMA buffer */
+static int vidioc_expbuf(struct file *file, void *priv,
+			 struct v4l2_exportbuffer *eb)
+{
+	struct rockchip_vpu_ctx *ctx = fh_to_ctx(priv);
+	int ret;
+
+	vpu_debug_enter();
+
+	switch (eb->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		ret = vb2_expbuf(&ctx->vq_src, eb);
+		break;
+
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		ret = vb2_expbuf(&ctx->vq_dst, eb);
+		break;
+
+	default:
+		ret = -EINVAL;
+	}
+
+	vpu_debug_leave();
+
+	return ret;
+}
+
+/* Stream on */
+static int vidioc_streamon(struct file *file, void *priv,
+			   enum v4l2_buf_type type)
+{
+	struct rockchip_vpu_ctx *ctx = fh_to_ctx(priv);
+	int ret;
+
+	vpu_debug_enter();
+
+	switch (type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		ret = vb2_streamon(&ctx->vq_src, type);
+		break;
+
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		ret = vb2_streamon(&ctx->vq_dst, type);
+		break;
+
+	default:
+		ret = -EINVAL;
+	}
+
+	vpu_debug_leave();
+
+	return ret;
+}
+
+/* Stream off, which equals to a pause */
+static int vidioc_streamoff(struct file *file, void *priv,
+			    enum v4l2_buf_type type)
+{
+	struct rockchip_vpu_ctx *ctx = fh_to_ctx(priv);
+	int ret;
+
+	vpu_debug_enter();
+
+	switch (type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		ret = vb2_streamoff(&ctx->vq_src, type);
+		break;
+
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		ret = vb2_streamoff(&ctx->vq_dst, type);
+		break;
+
+	default:
+		ret = -EINVAL;
+	}
+
+	vpu_debug_leave();
+
+	return ret;
+}
+
+static int rockchip_vpu_dec_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct rockchip_vpu_ctx *ctx = ctrl_to_ctx(ctrl);
+	struct rockchip_vpu_dev *dev = ctx->dev;
+	int ret = 0;
+
+	vpu_debug_enter();
+
+	vpu_debug(4, "ctrl id %d\n", ctrl->id);
+
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_VIDEO_VP8_FRAME_HDR:
+		/* These controls are used directly. */
+		break;
+
+	default:
+		v4l2_err(&dev->v4l2_dev, "Invalid control, id=%d, val=%d\n",
+			 ctrl->id, ctrl->val);
+		ret = -EINVAL;
+	}
+
+	vpu_debug_leave();
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops rockchip_vpu_dec_ctrl_ops = {
+	.s_ctrl = rockchip_vpu_dec_s_ctrl,
+};
+
+static const struct v4l2_ioctl_ops rockchip_vpu_dec_ioctl_ops = {
+	.vidioc_querycap = vidioc_querycap,
+	.vidioc_enum_framesizes = vidioc_enum_framesizes,
+	.vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane,
+	.vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane,
+	.vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt,
+	.vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt,
+	.vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt,
+	.vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt,
+	.vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt,
+	.vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt,
+	.vidioc_reqbufs = vidioc_reqbufs,
+	.vidioc_querybuf = vidioc_querybuf,
+	.vidioc_qbuf = vidioc_qbuf,
+	.vidioc_dqbuf = vidioc_dqbuf,
+	.vidioc_expbuf = vidioc_expbuf,
+	.vidioc_streamon = vidioc_streamon,
+	.vidioc_streamoff = vidioc_streamoff,
+};
+
+static int rockchip_vpu_queue_setup(struct vb2_queue *vq,
+				  const void *parg,
+				  unsigned int *buf_count,
+				  unsigned int *plane_count,
+				  unsigned int psize[], void *allocators[])
+{
+	struct rockchip_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv);
+	int ret = 0;
+
+	vpu_debug_enter();
+
+	switch (vq->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		*plane_count = ctx->vpu_src_fmt->num_planes;
+
+		if (*buf_count < 1)
+			*buf_count = 1;
+
+		if (*buf_count > VIDEO_MAX_FRAME)
+			*buf_count = VIDEO_MAX_FRAME;
+
+		psize[0] = ctx->src_fmt.plane_fmt[0].sizeimage;
+		allocators[0] = ctx->dev->alloc_ctx;
+		vpu_debug(0, "output psize[%d]: %d\n", 0, psize[0]);
+		break;
+
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		*plane_count = ctx->vpu_dst_fmt->num_planes;
+
+		if (*buf_count < 1)
+			*buf_count = 1;
+
+		if (*buf_count > VIDEO_MAX_FRAME)
+			*buf_count = VIDEO_MAX_FRAME;
+
+		psize[0] = round_up(ctx->dst_fmt.plane_fmt[0].sizeimage, 8);
+		allocators[0] = ctx->dev->alloc_ctx;
+
+		vpu_debug(0, "capture psize[%d]: %d\n", 0, psize[0]);
+		break;
+
+	default:
+		vpu_err("invalid queue type: %d\n", vq->type);
+		ret = -EINVAL;
+	}
+
+	vpu_debug_leave();
+
+	return ret;
+}
+
+static int rockchip_vpu_buf_init(struct vb2_buffer *vb)
+{
+	struct vb2_queue *vq = vb->vb2_queue;
+	struct rockchip_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv);
+
+	vpu_debug_enter();
+
+	if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		ctx->dst_bufs[vb->index] = vb;
+
+	vpu_debug_leave();
+
+	return 0;
+}
+
+static void rockchip_vpu_buf_cleanup(struct vb2_buffer *vb)
+{
+	struct vb2_queue *vq = vb->vb2_queue;
+	struct rockchip_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv);
+
+	vpu_debug_enter();
+
+	if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		ctx->dst_bufs[vb->index] = NULL;
+
+	vpu_debug_leave();
+}
+
+static int rockchip_vpu_buf_prepare(struct vb2_buffer *vb)
+{
+	struct vb2_queue *vq = vb->vb2_queue;
+	struct rockchip_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv);
+	int ret = 0;
+	int i;
+
+	vpu_debug_enter();
+
+	switch (vq->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		vpu_debug(4, "plane size: %ld, dst size: %d\n",
+				vb2_plane_size(vb, 0),
+				ctx->src_fmt.plane_fmt[0].sizeimage);
+
+		if (vb2_plane_size(vb, 0)
+		    < ctx->src_fmt.plane_fmt[0].sizeimage) {
+			vpu_err("plane size is too small for output\n");
+			ret = -EINVAL;
+		}
+		break;
+
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		for (i = 0; i < ctx->vpu_dst_fmt->num_planes; ++i) {
+			vpu_debug(4, "plane %d size: %ld, sizeimage: %u\n", i,
+					vb2_plane_size(vb, i),
+					ctx->dst_fmt.plane_fmt[i].sizeimage);
+
+			if (vb2_plane_size(vb, i)
+			    < ctx->dst_fmt.plane_fmt[i].sizeimage) {
+				vpu_err("size of plane %d is too small for capture\n",
+					i);
+				break;
+			}
+		}
+
+		if (i != ctx->vpu_dst_fmt->num_planes)
+			ret = -EINVAL;
+		break;
+
+	default:
+		vpu_err("invalid queue type: %d\n", vq->type);
+		ret = -EINVAL;
+	}
+
+	vpu_debug_leave();
+
+	return ret;
+}
+
+static int rockchip_vpu_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+	int ret = 0;
+	struct rockchip_vpu_ctx *ctx = fh_to_ctx(q->drv_priv);
+	struct rockchip_vpu_dev *dev = ctx->dev;
+	bool ready = false;
+
+	vpu_debug_enter();
+
+	if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		ret = rockchip_vpu_init(ctx);
+		if (ret < 0) {
+			vpu_err("rockchip_vpu_init failed\n");
+			return ret;
+		}
+
+		ready = vb2_is_streaming(&ctx->vq_src);
+	} else if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		ready = vb2_is_streaming(&ctx->vq_dst);
+	}
+
+	if (ready)
+		rockchip_vpu_try_context(dev, ctx);
+
+	vpu_debug_leave();
+
+	return 0;
+}
+
+static void rockchip_vpu_stop_streaming(struct vb2_queue *q)
+{
+	unsigned long flags;
+	struct rockchip_vpu_ctx *ctx = fh_to_ctx(q->drv_priv);
+	struct rockchip_vpu_dev *dev = ctx->dev;
+	struct rockchip_vpu_buf *b;
+	LIST_HEAD(queue);
+	int i;
+
+	vpu_debug_enter();
+
+	spin_lock_irqsave(&dev->irqlock, flags);
+
+	list_del_init(&ctx->list);
+
+	switch (q->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		list_splice_init(&ctx->dst_queue, &queue);
+		break;
+
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		list_splice_init(&ctx->src_queue, &queue);
+		break;
+
+	default:
+		break;
+	}
+
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+
+	wait_event(dev->run_wq, dev->current_ctx != ctx);
+
+	while (!list_empty(&queue)) {
+		b = list_first_entry(&queue, struct rockchip_vpu_buf, list);
+		for (i = 0; i < b->b.vb2_buf.num_planes; i++)
+			vb2_set_plane_payload(&b->b.vb2_buf, i, 0);
+		vb2_buffer_done(&b->b.vb2_buf, VB2_BUF_STATE_ERROR);
+		list_del(&b->list);
+	}
+
+	if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		rockchip_vpu_deinit(ctx);
+
+	vpu_debug_leave();
+}
+
+static void rockchip_vpu_buf_queue(struct vb2_buffer *vb)
+{
+	struct vb2_queue *vq = vb->vb2_queue;
+	struct rockchip_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv);
+	struct rockchip_vpu_dev *dev = ctx->dev;
+	struct rockchip_vpu_buf *vpu_buf;
+	unsigned long flags;
+
+	vpu_debug_enter();
+
+	switch (vq->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		vpu_buf = vb_to_buf(vb);
+
+		/* Mark destination as available for use by VPU */
+		spin_lock_irqsave(&dev->irqlock, flags);
+
+		list_add_tail(&vpu_buf->list, &ctx->dst_queue);
+
+		spin_unlock_irqrestore(&dev->irqlock, flags);
+		break;
+
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		vpu_buf = vb_to_buf(vb);
+
+		spin_lock_irqsave(&dev->irqlock, flags);
+
+		list_add_tail(&vpu_buf->list, &ctx->src_queue);
+
+		spin_unlock_irqrestore(&dev->irqlock, flags);
+		break;
+
+	default:
+		vpu_err("unsupported buffer type (%d)\n", vq->type);
+	}
+
+	if (vb2_is_streaming(&ctx->vq_src) && vb2_is_streaming(&ctx->vq_dst))
+		rockchip_vpu_try_context(dev, ctx);
+
+	vpu_debug_leave();
+}
+
+static struct vb2_ops rockchip_vpu_dec_qops = {
+	.queue_setup = rockchip_vpu_queue_setup,
+	.wait_prepare = vb2_ops_wait_prepare,
+	.wait_finish = vb2_ops_wait_finish,
+	.buf_init = rockchip_vpu_buf_init,
+	.buf_prepare = rockchip_vpu_buf_prepare,
+	.buf_cleanup = rockchip_vpu_buf_cleanup,
+	.start_streaming = rockchip_vpu_start_streaming,
+	.stop_streaming = rockchip_vpu_stop_streaming,
+	.buf_queue = rockchip_vpu_buf_queue,
+};
+
+struct vb2_ops *get_dec_queue_ops(void)
+{
+	return &rockchip_vpu_dec_qops;
+}
+
+const struct v4l2_ioctl_ops *get_dec_v4l2_ioctl_ops(void)
+{
+	return &rockchip_vpu_dec_ioctl_ops;
+}
+
+static void rockchip_vpu_dec_prepare_run(struct rockchip_vpu_ctx *ctx)
+{
+	struct vb2_v4l2_buffer *src =
+		to_vb2_v4l2_buffer(&ctx->run.src->b.vb2_buf);
+
+	v4l2_ctrl_apply_request(&ctx->ctrl_handler, src->request);
+
+}
+
+static void rockchip_vpu_dec_run_done(struct rockchip_vpu_ctx *ctx,
+				    enum vb2_buffer_state result)
+{
+	struct v4l2_plane_pix_format *plane_fmts = ctx->dst_fmt.plane_fmt;
+	struct vb2_buffer *dst = &ctx->run.dst->b.vb2_buf;
+	int i;
+
+	if (result != VB2_BUF_STATE_DONE) {
+		/* Assume no payload after failed run. */
+		for (i = 0; i < dst->num_planes; ++i)
+			vb2_set_plane_payload(dst, i, 0);
+		return;
+	}
+
+	for (i = 0; i < dst->num_planes; ++i)
+		vb2_set_plane_payload(dst, i, plane_fmts[i].sizeimage);
+}
+
+static const struct rockchip_vpu_run_ops rockchip_vpu_dec_run_ops = {
+	.prepare_run = rockchip_vpu_dec_prepare_run,
+	.run_done = rockchip_vpu_dec_run_done,
+};
+
+int rockchip_vpu_dec_init(struct rockchip_vpu_ctx *ctx)
+{
+
+	ctx->run_ops = &rockchip_vpu_dec_run_ops;
+
+	return rockchip_vpu_ctrls_setup(ctx, &rockchip_vpu_dec_ctrl_ops,
+					controls, ARRAY_SIZE(controls), NULL);
+}
+
+void rockchip_vpu_dec_exit(struct rockchip_vpu_ctx *ctx)
+{
+	rockchip_vpu_ctrls_delete(ctx);
+}
diff --git a/drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.h b/drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.h
new file mode 100644
index 0000000..267a089
--- /dev/null
+++ b/drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.h
@@ -0,0 +1,33 @@
+/*
+ * Rockchip VPU codec driver
+ *
+ * Copyright (C) 2014 Rockchip Electronics Co., Ltd.
+ *	Hertz Wong <hertz.wong at rock-chips.com>
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *	Tomasz Figa <tfiga at chromium.org>
+ *
+ * Based on s5p-mfc driver by Samsung Electronics Co., Ltd.
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef ROCKCHIP_VPU_DEC_H_
+#define ROCKCHIP_VPU_DEC_H_
+
+struct vb2_ops *get_dec_queue_ops(void);
+const struct v4l2_ioctl_ops *get_dec_v4l2_ioctl_ops(void);
+struct rockchip_vpu_fmt *get_dec_def_fmt(bool src);
+int rockchip_vpu_dec_init(struct rockchip_vpu_ctx *ctx);
+void rockchip_vpu_dec_exit(struct rockchip_vpu_ctx *ctx);
+
+#endif /* ROCKCHIP_VPU_DEC_H_ */
diff --git a/drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.c b/drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.c
new file mode 100644
index 0000000..dc7abc7
--- /dev/null
+++ b/drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.c
@@ -0,0 +1,278 @@
+/*
+ * Rockchip VPU codec driver
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *	Tomasz Figa <tfiga at chromium.org>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "rockchip_vpu_common.h"
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+
+#include <asm/dma-iommu.h>
+
+/**
+ * struct rockchip_vpu_codec_ops - codec mode specific operations
+ *
+ * @init:	Prepare for streaming. Called from VB2 .start_streaming()
+ *		when streaming from both queues is being enabled.
+ * @exit:	Clean-up after streaming. Called from VB2 .stop_streaming()
+ *		when streaming from first of both enabled queues is being
+ *		disabled.
+ * @run:	Start single {en,de)coding run. Called from non-atomic context
+ *		to indicate that a pair of buffers is ready and the hardware
+ *		should be programmed and started.
+ * @done:	Read back processing results and additional data from hardware.
+ * @reset:	Reset the hardware in case of a timeout.
+ */
+struct rockchip_vpu_codec_ops {
+	int (*init)(struct rockchip_vpu_ctx *);
+	void (*exit)(struct rockchip_vpu_ctx *);
+
+	int (*irq)(int, struct rockchip_vpu_dev *);
+	void (*run)(struct rockchip_vpu_ctx *);
+	void (*done)(struct rockchip_vpu_ctx *, enum vb2_buffer_state);
+	void (*reset)(struct rockchip_vpu_ctx *);
+};
+
+/*
+ * Hardware control routines.
+ */
+
+void rockchip_vpu_power_on(struct rockchip_vpu_dev *vpu)
+{
+	vpu_debug_enter();
+
+	/* TODO: Clock gating. */
+
+	pm_runtime_get_sync(vpu->dev);
+
+	vpu_debug_leave();
+}
+
+static void rockchip_vpu_power_off(struct rockchip_vpu_dev *vpu)
+{
+	vpu_debug_enter();
+
+	pm_runtime_mark_last_busy(vpu->dev);
+	pm_runtime_put_autosuspend(vpu->dev);
+
+	/* TODO: Clock gating. */
+
+	vpu_debug_leave();
+}
+
+/*
+ * Interrupt handlers.
+ */
+
+static irqreturn_t vdpu_irq(int irq, void *dev_id)
+{
+	struct rockchip_vpu_dev *vpu = dev_id;
+	struct rockchip_vpu_ctx *ctx = vpu->current_ctx;
+
+	if (!ctx->hw.codec_ops->irq(irq, vpu)) {
+		rockchip_vpu_power_off(vpu);
+		cancel_delayed_work(&vpu->watchdog_work);
+
+		ctx->hw.codec_ops->done(ctx, VB2_BUF_STATE_DONE);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void rockchip_vpu_watchdog(struct work_struct *work)
+{
+	struct rockchip_vpu_dev *vpu = container_of(to_delayed_work(work),
+					struct rockchip_vpu_dev, watchdog_work);
+	struct rockchip_vpu_ctx *ctx = vpu->current_ctx;
+	unsigned long flags;
+
+	spin_lock_irqsave(&vpu->irqlock, flags);
+
+	ctx->hw.codec_ops->reset(ctx);
+
+	spin_unlock_irqrestore(&vpu->irqlock, flags);
+
+	vpu_err("frame processing timed out!\n");
+
+	rockchip_vpu_power_off(vpu);
+	ctx->hw.codec_ops->done(ctx, VB2_BUF_STATE_ERROR);
+}
+
+/*
+ * Initialization/clean-up.
+ */
+
+#if defined(CONFIG_ROCKCHIP_IOMMU)
+static int rockchip_vpu_iommu_init(struct rockchip_vpu_dev *vpu)
+{
+	int ret;
+
+	vpu->mapping = arm_iommu_create_mapping(&platform_bus_type,
+						0x10000000, SZ_2G);
+	if (IS_ERR(vpu->mapping)) {
+		ret = PTR_ERR(vpu->mapping);
+		return ret;
+	}
+
+	vpu->dev->dma_parms = devm_kzalloc(vpu->dev,
+				sizeof(*vpu->dev->dma_parms), GFP_KERNEL);
+	if (!vpu->dev->dma_parms)
+		goto err_release_mapping;
+
+	dma_set_max_seg_size(vpu->dev, 0xffffffffu);
+
+	ret = arm_iommu_attach_device(vpu->dev, vpu->mapping);
+	if (ret)
+		goto err_release_mapping;
+
+	return 0;
+
+err_release_mapping:
+	arm_iommu_release_mapping(vpu->mapping);
+
+	return ret;
+}
+
+static void rockchip_vpu_iommu_cleanup(struct rockchip_vpu_dev *vpu)
+{
+	arm_iommu_detach_device(vpu->dev);
+	arm_iommu_release_mapping(vpu->mapping);
+}
+#else
+static inline int rockchip_vpu_iommu_init(struct rockchip_vpu_dev *vpu)
+{
+	return 0;
+}
+
+static inline void rockchip_vpu_iommu_cleanup(struct rockchip_vpu_dev *vpu) { }
+#endif
+
+int rockchip_vpu_hw_probe(struct rockchip_vpu_dev *vpu)
+{
+	struct resource *res;
+	int irq_dec;
+	int ret;
+
+	pr_info("probe device %s\n", dev_name(vpu->dev));
+
+	INIT_DELAYED_WORK(&vpu->watchdog_work, rockchip_vpu_watchdog);
+
+	vpu->aclk = devm_clk_get(vpu->dev, "aclk");
+	if (IS_ERR(vpu->aclk)) {
+		dev_err(vpu->dev, "failed to get aclk\n");
+		return PTR_ERR(vpu->aclk);
+	}
+
+	vpu->hclk = devm_clk_get(vpu->dev, "hclk");
+	if (IS_ERR(vpu->hclk)) {
+		dev_err(vpu->dev, "failed to get hclk\n");
+		return PTR_ERR(vpu->hclk);
+	}
+
+	/*
+	 * Bump ACLK to max. possible freq. (400 MHz) to improve performance.
+	 */
+	clk_set_rate(vpu->aclk, 400*1000*1000);
+
+	res = platform_get_resource(vpu->pdev, IORESOURCE_MEM, 0);
+	vpu->base = devm_ioremap_resource(vpu->dev, res);
+	if (IS_ERR(vpu->base))
+		return PTR_ERR(vpu->base);
+
+	clk_prepare_enable(vpu->aclk);
+	clk_prepare_enable(vpu->hclk);
+
+	vpu->dec_base = vpu->base + vpu->variant->dec_offset;
+
+	ret = dma_set_coherent_mask(vpu->dev, DMA_BIT_MASK(32));
+	if (ret) {
+		dev_err(vpu->dev, "could not set DMA coherent mask\n");
+		goto err_power;
+	}
+
+	ret = rockchip_vpu_iommu_init(vpu);
+	if (ret)
+		goto err_power;
+
+	irq_dec = platform_get_irq_byname(vpu->pdev, "vdpu");
+	if (irq_dec <= 0) {
+		dev_err(vpu->dev, "could not get vdpu IRQ\n");
+		ret = -ENXIO;
+		goto err_iommu;
+	}
+
+	ret = devm_request_threaded_irq(vpu->dev, irq_dec, NULL, vdpu_irq,
+					IRQF_ONESHOT, dev_name(vpu->dev), vpu);
+	if (ret) {
+		dev_err(vpu->dev, "could not request vdpu IRQ\n");
+		goto err_iommu;
+	}
+
+	pm_runtime_set_autosuspend_delay(vpu->dev, 100);
+	pm_runtime_use_autosuspend(vpu->dev);
+	pm_runtime_enable(vpu->dev);
+
+	return 0;
+
+err_iommu:
+	rockchip_vpu_iommu_cleanup(vpu);
+err_power:
+	clk_disable_unprepare(vpu->hclk);
+	clk_disable_unprepare(vpu->aclk);
+
+	return ret;
+}
+
+void rockchip_vpu_hw_remove(struct rockchip_vpu_dev *vpu)
+{
+	rockchip_vpu_iommu_cleanup(vpu);
+
+	pm_runtime_disable(vpu->dev);
+
+	clk_disable_unprepare(vpu->hclk);
+	clk_disable_unprepare(vpu->aclk);
+}
+
+static const struct rockchip_vpu_codec_ops mode_ops[0];
+
+void rockchip_vpu_run(struct rockchip_vpu_ctx *ctx)
+{
+	ctx->hw.codec_ops->run(ctx);
+}
+
+int rockchip_vpu_init(struct rockchip_vpu_ctx *ctx)
+{
+	enum rockchip_vpu_codec_mode codec_mode;
+
+	codec_mode = ctx->vpu_src_fmt->codec_mode; /* Decoder */
+
+	ctx->hw.codec_ops = &mode_ops[codec_mode];
+
+	return ctx->hw.codec_ops->init(ctx);
+}
+
+void rockchip_vpu_deinit(struct rockchip_vpu_ctx *ctx)
+{
+	ctx->hw.codec_ops->exit(ctx);
+}
diff --git a/drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.h b/drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.h
new file mode 100644
index 0000000..975357da
--- /dev/null
+++ b/drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.h
@@ -0,0 +1,78 @@
+/*
+ * Rockchip VPU codec driver
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *	Tomasz Figa <tfiga at chromium.org>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef ROCKCHIP_VPU_HW_H_
+#define ROCKCHIP_VPU_HW_H_
+
+#include <media/videobuf2-core.h>
+
+#define ROCKCHIP_HEADER_SIZE		1280
+#define ROCKCHIP_HW_PARAMS_SIZE		5487
+#define ROCKCHIP_RET_PARAMS_SIZE	488
+
+struct rockchip_vpu_dev;
+struct rockchip_vpu_ctx;
+struct rockchip_vpu_buf;
+
+/**
+ * enum rockchip_vpu_type - vpu type.
+ * @RK_VPU_NONE:	No vpu type. Used for RAW video formats.
+ * @RK3288_VPU:		Vpu on rk3288 soc.
+ * @RK3229_VPU:		Vpu on rk3229 soc.
+ * @RKVDEC:		Rkvdec.
+ */
+enum rockchip_vpu_type {
+	RK_VPU_NONE	= -1,
+	RK3288_VPU,
+	RK3229_VPU,
+};
+
+#define ROCKCHIP_VPU_MATCHES(type1, type2) \
+	(type1 == RK_VPU_NONE || type2 == RK_VPU_NONE || type1 == type2)
+
+/**
+ * struct rockchip_vpu_aux_buf - auxiliary DMA buffer for hardware data
+ * @cpu:	CPU pointer to the buffer.
+ * @dma:	DMA address of the buffer.
+ * @size:	Size of the buffer.
+ */
+struct rockchip_vpu_aux_buf {
+	void *cpu;
+	dma_addr_t dma;
+	size_t size;
+};
+
+/**
+ * struct rockchip_vpu_hw_ctx - Context private data of hardware code.
+ * @codec_ops:		Set of operations associated with current codec mode.
+ */
+struct rockchip_vpu_hw_ctx {
+	const struct rockchip_vpu_codec_ops *codec_ops;
+
+	/* Specific for particular codec modes. */
+};
+
+int rockchip_vpu_hw_probe(struct rockchip_vpu_dev *vpu);
+void rockchip_vpu_hw_remove(struct rockchip_vpu_dev *vpu);
+
+int rockchip_vpu_init(struct rockchip_vpu_ctx *ctx);
+void rockchip_vpu_deinit(struct rockchip_vpu_ctx *ctx);
+
+void rockchip_vpu_run(struct rockchip_vpu_ctx *ctx);
+
+void rockchip_vpu_power_on(struct rockchip_vpu_dev *vpu);
+
+#endif /* RK3288_VPU_HW_H_ */
-- 
1.9.1



From jung.zhao at rock-chips.com  Tue Jan 26 01:04:06 2016
From: jung.zhao at rock-chips.com (Jung Zhao)
Date: Tue, 26 Jan 2016 17:04:06 +0800
Subject: [PATCH v1 3/3] media: vcodec: rockchip: Add Rockchip VP8 decoder
 driver
In-Reply-To: <1453799046-307-1-git-send-email-jung.zhao@rock-chips.com>
References: <1453799046-307-1-git-send-email-jung.zhao@rock-chips.com>
Message-ID: <1453799046-307-4-git-send-email-jung.zhao@rock-chips.com>

From: zhaojun <jung.zhao at rock-chips.com>

add vp8 decoder for rk3229 & rk3288

Signed-off-by: zhaojun <jung.zhao at rock-chips.com>
---

 drivers/media/platform/rockchip-vpu/Makefile       |    3 +-
 .../media/platform/rockchip-vpu/rkvpu_hw_vp8d.c    |  798 ++++++++++
 .../platform/rockchip-vpu/rockchip_vp8d_regs.h     | 1594 ++++++++++++++++++++
 .../media/platform/rockchip-vpu/rockchip_vpu_dec.c |   15 +-
 .../media/platform/rockchip-vpu/rockchip_vpu_hw.c  |   19 +-
 .../media/platform/rockchip-vpu/rockchip_vpu_hw.h  |   22 +
 6 files changed, 2448 insertions(+), 3 deletions(-)
 create mode 100644 drivers/media/platform/rockchip-vpu/rkvpu_hw_vp8d.c
 create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vp8d_regs.h

diff --git a/drivers/media/platform/rockchip-vpu/Makefile b/drivers/media/platform/rockchip-vpu/Makefile
index 5aab094..2163eb9 100644
--- a/drivers/media/platform/rockchip-vpu/Makefile
+++ b/drivers/media/platform/rockchip-vpu/Makefile
@@ -3,4 +3,5 @@ obj-$(CONFIG_VIDEO_ROCKCHIP_VPU) += rockchip-vpu.o
 
 rockchip-vpu-y += rockchip_vpu.o \
 		rockchip_vpu_dec.o \
-		rockchip_vpu_hw.o
+		rockchip_vpu_hw.o \
+		rkvpu_hw_vp8d.o
diff --git a/drivers/media/platform/rockchip-vpu/rkvpu_hw_vp8d.c b/drivers/media/platform/rockchip-vpu/rkvpu_hw_vp8d.c
new file mode 100644
index 0000000..d584eae
--- /dev/null
+++ b/drivers/media/platform/rockchip-vpu/rkvpu_hw_vp8d.c
@@ -0,0 +1,798 @@
+/*
+ * Rockchip VPU codec vp8 decode driver
+ *
+ * Copyright (C) 2014 Rockchip Electronics Co., Ltd.
+ *	ZhiChao Yu <zhichao.yu at rock-chips.com>
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *      Tomasz Figa <tfiga at chromium.org>
+ *
+ * Copyright (C) 2015 Rockchip Electronics Co., Ltd.
+ *      Alpha Lin <alpha.lin at rock-chips.com>
+ *
+ * Copyright (C) 2015 Rockchip Electronics Co., Ltd.
+ *      Jung Zhao <jung.zhao at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "rockchip_vpu_hw.h"
+#include "rockchip_vp8d_regs.h"
+#include "rockchip_vpu_common.h"
+
+#define RK_MAX_REGS_NUMS	256
+#define DEC_8190_ALIGN_MASK	0x07U
+
+static u32 *rockchip_regs_map;
+static u32 (*rockchip_regs_table)[3];
+static u32 rk_regs_value[RK_MAX_REGS_NUMS];
+
+#define RK_GET_REG_BASE(x) \
+	(rockchip_regs_table[rockchip_regs_map[(x)]][0])
+
+#define RK_GET_REG_BITS_MASK(x) \
+	(rockchip_regs_table[rockchip_regs_map[(x)]][1])
+
+#define RK_GET_REG_BITS_OFFSET(x) \
+	(rockchip_regs_table[rockchip_regs_map[(x)]][2])
+
+/*
+ * probs table with packed
+ */
+struct vp8_prob_tbl_packed {
+	u8 prob_mb_skip_false;
+	u8 prob_intra;
+	u8 prob_ref_last;
+	u8 prob_ref_golden;
+	u8 prob_segment[3];
+	u8 packed0;
+
+	u8 prob_luma_16x16_pred_mode[4];
+	u8 prob_chroma_pred_mode[3];
+	u8 packed1;
+
+	/* mv prob */
+	u8 prob_mv_context[2][19];
+	u8 packed2[2];
+
+	/* coeff probs */
+	u8 prob_coeffs[4][8][3][11];
+	u8 packed3[96];
+};
+
+/*
+ * filter taps taken to 7-bit precision,
+ * reference RFC6386#Page-16, filters[8][6]
+ */
+static const u32 vp8d_mc_filter[8][6] = {
+	{ 0, 0, 128, 0, 0, 0 },
+	{ 0, -6, 123, 12, -1, 0 },
+	{ 2, -11, 108, 36, -8, 1 },
+	{ 0, -9, 93, 50, -6, 0 },
+	{ 3, -16, 77, 77, -16, 3 },
+	{ 0, -6, 50, 93, -9, 0 },
+	{ 1, -8, 36, 108, -11, 2 },
+	{ 0, -1, 12, 123, -6, 0 }
+};
+
+/* dump hw params for debug */
+#ifdef DEBUG
+static void rockchip_vp8d_dump_hdr(struct rockchip_vpu_ctx *ctx)
+{
+	const struct v4l2_ctrl_vp8_frame_hdr *hdr = ctx->run.vp8d.frame_hdr;
+	int dct_total_len = 0;
+	int i;
+
+	vpu_debug(4, "Frame tag: key_frame=0x%02x, version=0x%02x\n",
+		  !hdr->key_frame, hdr->version);
+
+	vpu_debug(4, "Picture size: w=%d, h=%d\n", hdr->width, hdr->height);
+
+	/* stream addresses */
+	vpu_debug(4, "Addresses: segmap=0x%x, probs=0x%x\n",
+		  ctx->hw.vp8d.segment_map.dma,
+		  ctx->hw.vp8d.prob_tbl.dma);
+
+	/* reference frame info */
+	vpu_debug(4, "Ref frame: last=%d, golden=%d, alt=%d\n",
+		  hdr->last_frame, hdr->golden_frame, hdr->alt_frame);
+
+	/* bool decoder info */
+	vpu_debug(4, "Bool decoder: range=0x%x, value=0x%x, count=0x%x\n",
+		  hdr->bool_dec_range, hdr->bool_dec_value,
+		  hdr->bool_dec_count);
+
+	/* control partition info */
+	vpu_debug(4, "Control Part: offset=0x%x, size=0x%x\n",
+		  hdr->first_part_offset, hdr->first_part_size);
+	vpu_debug(2, "Macroblock Data: bits_offset=0x%x\n",
+		  hdr->macroblock_bit_offset);
+
+	/* dct partition info */
+	for (i = 0; i < hdr->num_dct_parts; i++) {
+		dct_total_len += hdr->dct_part_sizes[i];
+		vpu_debug(4, "Dct Part%d Size: 0x%x\n",
+			  i, hdr->dct_part_sizes[i]);
+	}
+
+	dct_total_len += (hdr->num_dct_parts - 1) * 3;
+	vpu_debug(4, "Dct Part Total Length: 0x%x\n", dct_total_len);
+}
+#else
+static inline void rockchip_vp8d_dump_hdr(struct rockchip_vpu_ctx *ctx) { }
+#endif
+
+static void vp8d_write_regs_value(u32 index, u32 value, char *name)
+{
+	vpu_debug(6, "rk_regs_value[ %s:%03d ]=%08x\n", name, index, value);
+	rk_regs_value[index] = value;
+}
+static void rockchip_vp8d_prob_update(struct rockchip_vpu_ctx *ctx)
+{
+	const struct v4l2_ctrl_vp8_frame_hdr *hdr = ctx->run.vp8d.frame_hdr;
+	const struct v4l2_vp8_entropy_hdr *entropy_hdr = &hdr->entropy_hdr;
+	u32 i, j, k;
+	u8 *dst;
+
+	/* first probs */
+	dst = ctx->hw.vp8d.prob_tbl.cpu;
+
+	dst[0] = hdr->prob_skip_false;
+	dst[1] = hdr->prob_intra;
+	dst[2] = hdr->prob_last;
+	dst[3] = hdr->prob_gf;
+	dst[4] = hdr->sgmnt_hdr.segment_probs[0];
+	dst[5] = hdr->sgmnt_hdr.segment_probs[1];
+	dst[6] = hdr->sgmnt_hdr.segment_probs[2];
+	dst[7] = 0;
+
+	dst += 8;
+	dst[0] = entropy_hdr->y_mode_probs[0];
+	dst[1] = entropy_hdr->y_mode_probs[1];
+	dst[2] = entropy_hdr->y_mode_probs[2];
+	dst[3] = entropy_hdr->y_mode_probs[3];
+	dst[4] = entropy_hdr->uv_mode_probs[0];
+	dst[5] = entropy_hdr->uv_mode_probs[1];
+	dst[6] = entropy_hdr->uv_mode_probs[2];
+	dst[7] = 0; /*unused */
+
+	/* mv probs */
+	dst += 8;
+	dst[0] = entropy_hdr->mv_probs[0][0]; /* is short */
+	dst[1] = entropy_hdr->mv_probs[1][0];
+	dst[2] = entropy_hdr->mv_probs[0][1]; /* sign */
+	dst[3] = entropy_hdr->mv_probs[1][1];
+	dst[4] = entropy_hdr->mv_probs[0][8 + 9];
+	dst[5] = entropy_hdr->mv_probs[0][9 + 9];
+	dst[6] = entropy_hdr->mv_probs[1][8 + 9];
+	dst[7] = entropy_hdr->mv_probs[1][9 + 9];
+	dst += 8;
+	for (i = 0; i < 2; ++i) {
+		for (j = 0; j < 8; j += 4) {
+			dst[0] = entropy_hdr->mv_probs[i][j + 9 + 0];
+			dst[1] = entropy_hdr->mv_probs[i][j + 9 + 1];
+			dst[2] = entropy_hdr->mv_probs[i][j + 9 + 2];
+			dst[3] = entropy_hdr->mv_probs[i][j + 9 + 3];
+			dst += 4;
+		}
+	}
+	for (i = 0; i < 2; ++i) {
+		dst[0] = entropy_hdr->mv_probs[i][0 + 2];
+		dst[1] = entropy_hdr->mv_probs[i][1 + 2];
+		dst[2] = entropy_hdr->mv_probs[i][2 + 2];
+		dst[3] = entropy_hdr->mv_probs[i][3 + 2];
+		dst[4] = entropy_hdr->mv_probs[i][4 + 2];
+		dst[5] = entropy_hdr->mv_probs[i][5 + 2];
+		dst[6] = entropy_hdr->mv_probs[i][6 + 2];
+		dst[7] = 0; /*unused */
+		dst += 8;
+	}
+
+	/* coeff probs (header part) */
+	dst = ctx->hw.vp8d.prob_tbl.cpu;
+	dst += (8 * 7);
+	for (i = 0; i < 4; ++i) {
+		for (j = 0; j < 8; ++j) {
+			for (k = 0; k < 3; ++k) {
+				dst[0] = entropy_hdr->coeff_probs[i][j][k][0];
+				dst[1] = entropy_hdr->coeff_probs[i][j][k][1];
+				dst[2] = entropy_hdr->coeff_probs[i][j][k][2];
+				dst[3] = entropy_hdr->coeff_probs[i][j][k][3];
+				dst += 4;
+			}
+		}
+	}
+
+	/* coeff probs (footer part) */
+	dst = ctx->hw.vp8d.prob_tbl.cpu;
+	dst += (8 * 55);
+	for (i = 0; i < 4; ++i) {
+		for (j = 0; j < 8; ++j) {
+			for (k = 0; k < 3; ++k) {
+				dst[0] = entropy_hdr->coeff_probs[i][j][k][4];
+				dst[1] = entropy_hdr->coeff_probs[i][j][k][5];
+				dst[2] = entropy_hdr->coeff_probs[i][j][k][6];
+				dst[3] = entropy_hdr->coeff_probs[i][j][k][7];
+				dst[4] = entropy_hdr->coeff_probs[i][j][k][8];
+				dst[5] = entropy_hdr->coeff_probs[i][j][k][9];
+				dst[6] = entropy_hdr->coeff_probs[i][j][k][10];
+				dst[7] = 0; /*unused */
+				dst += 8;
+			}
+		}
+	}
+}
+
+/*
+ * set loop filters
+ */
+static void rockchip_vp8d_cfg_lf(struct rockchip_vpu_ctx *ctx)
+{
+	const struct v4l2_ctrl_vp8_frame_hdr *hdr = ctx->run.vp8d.frame_hdr;
+	__s8 reg_value;
+	int i;
+
+	if (!(hdr->sgmnt_hdr.flags & V4L2_VP8_SEGMNT_HDR_FLAG_ENABLED)) {
+		vp8d_write_regs_value(VDPU_REG_REF_PIC_LF_LEVEL_0,
+				      hdr->lf_hdr.level,
+				      "VDPU_REG_REF_PIC_LF_LEVEL_0");
+	} else if (hdr->sgmnt_hdr.segment_feature_mode) {
+		/* absolute mode */
+		for (i = 0; i < 4; i++) {
+			vp8d_write_regs_value(VDPU_REG_REF_PIC_LF_LEVEL_0 + i,
+					      hdr->sgmnt_hdr.lf_update[i],
+					      "VDPU_REG_REF_PIC_LF_LEVEL_ARRAY"
+					      );
+		}
+	} else {
+		/* delta mode */
+		for (i = 0; i < 4; i++) {
+			vp8d_write_regs_value(VDPU_REG_REF_PIC_LF_LEVEL_0 + i,
+					      clamp(hdr->lf_hdr.level +
+						    hdr->sgmnt_hdr.lf_update[i],
+						    0, 63),
+					      "VDPU_REG_REF_PIC_LF_LEVEL_ARRAY"
+					      );
+		}
+	}
+
+	vp8d_write_regs_value(VDPU_REG_REF_PIC_FILT_SHARPNESS,
+			      (hdr->lf_hdr.sharpness_level),
+			      "VDPU_REG_REF_PIC_FILT_SHARPNESS");
+	if (hdr->lf_hdr.type)
+		vp8d_write_regs_value(VDPU_REG_REF_PIC_FILT_TYPE_E, 1,
+				      "VDPU_REG_REF_PIC_FILT_TYPE_E");
+
+	if (hdr->lf_hdr.flags & V4L2_VP8_LF_HDR_ADJ_ENABLE) {
+		for (i = 0; i < 4; i++) {
+			reg_value = hdr->lf_hdr.mb_mode_delta_magnitude[i];
+			vp8d_write_regs_value(VDPU_REG_FILT_MB_ADJ_0 + i,
+					      reg_value,
+					      "VDPU_REG_FILT_MB_ADJ_ARRAY");
+			reg_value = hdr->lf_hdr.ref_frm_delta_magnitude[i];
+			vp8d_write_regs_value(VDPU_REG_REF_PIC_ADJ_0 + i,
+					      reg_value,
+					      "VDPU_REG_REF_PIC_ADJ_ARRAY");
+		}
+	}
+}
+
+/*
+ * set quantization parameters
+ */
+static void rockchip_vp8d_cfg_qp(struct rockchip_vpu_ctx *ctx)
+{
+	const struct v4l2_ctrl_vp8_frame_hdr *hdr = ctx->run.vp8d.frame_hdr;
+	__s8 reg_value;
+	int i;
+
+	if (!(hdr->sgmnt_hdr.flags & V4L2_VP8_SEGMNT_HDR_FLAG_ENABLED)) {
+		vp8d_write_regs_value(VDPU_REG_REF_PIC_QUANT_0,
+				      hdr->quant_hdr.y_ac_qi,
+				      "VDPU_REG_REF_PIC_QUANT_0");
+	} else if (hdr->sgmnt_hdr.segment_feature_mode) {
+		/* absolute mode */
+		for (i = 0; i < 4; i++) {
+			vp8d_write_regs_value(VDPU_REG_REF_PIC_QUANT_0 + i,
+					      hdr->sgmnt_hdr.quant_update[i],
+					      "VDPU_REG_REF_PIC_QUANT_ARRAY");
+		}
+	} else {
+		/* delta mode */
+		for (i = 0; i < 4; i++) {
+			reg_value = hdr->sgmnt_hdr.quant_update[i];
+			vp8d_write_regs_value(VDPU_REG_REF_PIC_QUANT_0 + i,
+					      clamp(hdr->quant_hdr.y_ac_qi +
+						    reg_value,
+						    0, 127),
+					      "VDPU_REG_REF_PIC_QUANT_ARRAY");
+		}
+	}
+
+	vp8d_write_regs_value(VDPU_REG_REF_PIC_QUANT_DELTA_0,
+			      hdr->quant_hdr.y_dc_delta,
+			      "VDPU_REG_REF_PIC_QUANT_DELTA_0");
+	vp8d_write_regs_value(VDPU_REG_REF_PIC_QUANT_DELTA_1,
+			      hdr->quant_hdr.y2_dc_delta,
+			      "VDPU_REG_REF_PIC_QUANT_DELTA_1");
+	vp8d_write_regs_value(VDPU_REG_REF_PIC_QUANT_DELTA_2,
+			      hdr->quant_hdr.y2_ac_delta,
+			      "VDPU_REG_REF_PIC_QUANT_DELTA_2");
+	vp8d_write_regs_value(VDPU_REG_REF_PIC_QUANT_DELTA_3,
+			      hdr->quant_hdr.uv_dc_delta,
+			      "VDPU_REG_REF_PIC_QUANT_DELTA_3");
+	vp8d_write_regs_value(VDPU_REG_REF_PIC_QUANT_DELTA_4,
+			      hdr->quant_hdr.uv_ac_delta,
+			      "VDPU_REG_REF_PIC_QUANT_DELTA_4");
+}
+
+/*
+ * set control partition and dct partition regs
+ *
+ * VP8 frame stream data layout:
+ *
+ *	                     first_part_size          parttion_sizes[0]
+ *                              ^                     ^
+ * src_dma                      |                     |
+ * ^                   +--------+------+        +-----+-----+
+ * |                   | control part  |        |           |
+ * +--------+----------------+------------------+-----------+-----+-----------+
+ * | tag 3B | extra 7B | hdr | mb_data | dct sz | dct part0 | ... | dct partn |
+ * +--------+-----------------------------------+-----------+-----+-----------+
+ *                     |     |         |        |                             |
+ *                     |     v         +----+---+                             v
+ *                     |     mb_start       |                       src_dma_end
+ *                     v                    v
+ *             first_part_offset         dct size part
+ *                                      (num_dct-1)*3B
+ * Note:
+ *   1. only key frame has extra 7 bytes
+ *   2. all offsets are base on src_dma
+ *   3. number of dct parts is 1, 2, 4 or 8
+ *   4. the addresses set to vpu must be 64bits alignment
+ */
+static void rockchip_vp8d_cfg_parts(struct rockchip_vpu_ctx *ctx)
+{
+	const struct v4l2_ctrl_vp8_frame_hdr *hdr = ctx->run.vp8d.frame_hdr;
+	u32 dct_part_total_len = 0;
+	u32 dct_size_part_size = 0;
+	u32 dct_part_offset = 0;
+	u32 mb_offset_bytes = 0;
+	u32 mb_offset_bits = 0;
+	u32 mb_start_bits = 0;
+	dma_addr_t src_dma;
+	u32 mb_size = 0;
+	u32 count = 0;
+	u32 i;
+
+	src_dma = vb2_dma_contig_plane_dma_addr(&ctx->run.src->b.vb2_buf, 0);
+
+	/*
+	 * Calculate control partition mb data info
+	 * @macroblock_bit_offset:	bits offset of mb data from first
+	 *				part start pos
+	 * @mb_offset_bits:		bits offset of mb data from src_dma
+	 *				base addr
+	 * @mb_offset_byte:		bytes offset of mb data from src_dma
+	 *				base addr
+	 * @mb_start_bits:		bits offset of mb data from mb data
+	 *				64bits alignment addr
+	 */
+	mb_offset_bits = hdr->first_part_offset * 8
+			 + hdr->macroblock_bit_offset + 8;
+	mb_offset_bytes = mb_offset_bits / 8;
+	mb_start_bits = mb_offset_bits
+			- (mb_offset_bytes & (~DEC_8190_ALIGN_MASK)) * 8;
+	mb_size = hdr->first_part_size
+		  - (mb_offset_bytes - hdr->first_part_offset)
+		  + (mb_offset_bytes & DEC_8190_ALIGN_MASK);
+
+	/* mb data aligned base addr */
+	vp8d_write_regs_value(VDPU_REG_VP8_ADDR_CTRL_PART,
+			      (mb_offset_bytes & (~DEC_8190_ALIGN_MASK))
+			      + src_dma,
+			      "VDPU_REG_VP8_ADDR_CTRL_PART");
+
+	/* mb data start bits */
+	vp8d_write_regs_value(VDPU_REG_DEC_CTRL2_STRM1_START_BIT,
+			      mb_start_bits,
+			      "VDPU_REG_DEC_CTRL2_STRM1_START_BIT");
+
+	/* mb aligned data length */
+	vp8d_write_regs_value(VDPU_REG_DEC_CTRL6_STREAM1_LEN,
+			      mb_size,
+			      "VDPU_REG_DEC_CTRL6_STREAM1_LEN");
+
+	/*
+	 * Calculate dct partition info
+	 * @dct_size_part_size: Containing sizes of dct part, every dct part
+	 *			has 3 bytes to store its size, except the last
+	 *			dct part
+	 * @dct_part_offset:	bytes offset of dct parts from src_dma base addr
+	 * @dct_part_total_len: total size of all dct parts
+	 */
+	dct_size_part_size = (hdr->num_dct_parts - 1) * 3;
+	dct_part_offset = hdr->first_part_offset + hdr->first_part_size;
+	for (i = 0; i < hdr->num_dct_parts; i++)
+		dct_part_total_len += hdr->dct_part_sizes[i];
+	dct_part_total_len += dct_size_part_size;
+	dct_part_total_len += (dct_part_offset & DEC_8190_ALIGN_MASK);
+
+	/* number of dct partitions */
+	vp8d_write_regs_value(VDPU_REG_DEC_CTRL6_COEFFS_PART_AM,
+			      (hdr->num_dct_parts - 1),
+			      "VDPU_REG_DEC_CTRL6_COEFFS_PART_AM");
+
+	/* dct partition length */
+	vp8d_write_regs_value(VDPU_REG_DEC_CTRL3_STREAM_LEN,
+			      dct_part_total_len,
+			      "VDPU_REG_DEC_CTRL3_STREAM_LEN");
+	/* dct partitions base address */
+	for (i = 0; i < hdr->num_dct_parts; i++) {
+		u32 byte_offset = dct_part_offset + dct_size_part_size + count;
+		u32 base_addr = byte_offset + src_dma;
+
+		vp8d_write_regs_value(VDPU_REG_ADDR_STR + i,
+				      base_addr & (~DEC_8190_ALIGN_MASK),
+				      "VDPU_REG_ADDR_STR_ARRAY");
+
+		vp8d_write_regs_value(VDPU_REG_DEC_CTRL2_STRM_START_BIT + i,
+				      ((byte_offset & DEC_8190_ALIGN_MASK) * 8),
+				      "VDPU_REG_DEC_CTRL2_STRM_START_BIT_ARRAY"
+				     );
+
+		count += hdr->dct_part_sizes[i];
+	}
+}
+
+/*
+ * prediction filter taps
+ * normal 6-tap filters
+ */
+static void rockchip_vp8d_cfg_tap(struct rockchip_vpu_ctx *ctx)
+{
+	const struct v4l2_ctrl_vp8_frame_hdr *hdr = ctx->run.vp8d.frame_hdr;
+	int i, j, index;
+
+	if ((hdr->version & 0x03) != 0)
+		return; /* Tap filter not used. */
+
+	for (i = 0; i < 8; i++) {
+		for (j = 0; j < 6; j++) {
+			index = VDPU_REG_PRED_FLT_NONE_0 + i * 6 + j;
+			if (RK_GET_REG_BASE(index) != 0) {
+				vp8d_write_regs_value(index,
+						      vp8d_mc_filter[i][j],
+						      "VDPU_REG_PRED_FLT_ARRAY"
+						      );
+			}
+		}
+	}
+}
+
+/* set reference frame */
+static void rockchip_vp8d_cfg_ref(struct rockchip_vpu_ctx *ctx)
+{
+	struct vb2_buffer *buf;
+	const struct v4l2_ctrl_vp8_frame_hdr *hdr = ctx->run.vp8d.frame_hdr;
+	dma_addr_t dma_address;
+
+	/* set last frame address */
+	if (hdr->last_frame >= ctx->vq_dst.num_buffers)
+		buf = &ctx->run.dst->b.vb2_buf;
+	else
+		buf = ctx->dst_bufs[hdr->last_frame];
+
+	if (!hdr->key_frame) {
+		dma_address =
+			vb2_dma_contig_plane_dma_addr(&ctx->run.dst->b.vb2_buf,
+						      0);
+		vp8d_write_regs_value(VDPU_REG_VP8_ADDR_REF0,
+				      dma_address,
+				      "VDPU_REG_VP8_ADDR_REF0");
+	} else {
+		vp8d_write_regs_value(VDPU_REG_VP8_ADDR_REF0,
+				      vb2_dma_contig_plane_dma_addr(buf, 0),
+				      "VDPU_REG_VP8_ADDR_REF0");
+	}
+
+	/* set golden reference frame buffer address */
+	if (hdr->golden_frame >= ctx->vq_dst.num_buffers)
+		buf = &ctx->run.dst->b.vb2_buf;
+	else
+		buf = ctx->dst_bufs[hdr->golden_frame];
+
+	vp8d_write_regs_value(VDPU_REG_VP8_ADDR_REF2_5_0,
+			      vb2_dma_contig_plane_dma_addr(buf, 0),
+			      "VDPU_REG_VP8_ADDR_REF2_5_0");
+
+	if (hdr->sign_bias_golden)
+		vp8d_write_regs_value(VDPU_REG_VP8_GREF_SIGN_BIAS_0, 1,
+				      "VDPU_REG_VP8_GREF_SIGN_BIAS_0");
+
+	/* set alternate reference frame buffer address */
+	if (hdr->alt_frame >= ctx->vq_dst.num_buffers)
+		buf = &ctx->run.dst->b.vb2_buf;
+	else
+		buf = ctx->dst_bufs[hdr->alt_frame];
+
+	vp8d_write_regs_value(VDPU_REG_VP8_ADDR_REF2_5_1,
+			      vb2_dma_contig_plane_dma_addr(buf, 0),
+			      "VDPU_REG_VP8_ADDR_REF2_5_1");
+	if (hdr->sign_bias_alternate)
+		vp8d_write_regs_value(VDPU_REG_VP8_AREF_SIGN_BIAS_1, 1,
+				      "VDPU_REG_VP8_AREF_SIGN_BIAS_1");
+}
+
+static void rockchip_vp8d_cfg_buffers(struct rockchip_vpu_ctx *ctx)
+{
+	const struct v4l2_ctrl_vp8_frame_hdr *hdr = ctx->run.vp8d.frame_hdr;
+	dma_addr_t dma_address;
+
+	/* set probability table buffer address */
+	vp8d_write_regs_value(VDPU_REG_ADDR_QTABLE,
+			      ctx->hw.vp8d.prob_tbl.dma,
+			      "VDPU_REG_ADDR_QTABLE");
+
+	/* set segment map address */
+	vp8d_write_regs_value(VDPU_REG_FWD_PIC1_SEGMENT_BASE,
+			      ctx->hw.vp8d.segment_map.dma,
+			      "VDPU_REG_FWD_PIC1_SEGMENT_BASE");
+
+	if (hdr->sgmnt_hdr.flags & V4L2_VP8_SEGMNT_HDR_FLAG_ENABLED) {
+		vp8d_write_regs_value(VDPU_REG_FWD_PIC1_SEGMENT_E, 1,
+				      "VDPU_REG_FWD_PIC1_SEGMENT_E");
+		if (hdr->sgmnt_hdr.flags & V4L2_VP8_SEGMNT_HDR_FLAG_UPDATE_MAP)
+			vp8d_write_regs_value(VDPU_REG_FWD_PIC1_SEGMENT_UPD_E,
+					      1,
+					      "VDPU_REG_FWD_PIC1_SEGMENT_UPD_E");
+	}
+
+	dma_address = vb2_dma_contig_plane_dma_addr(&ctx->run.dst->b.vb2_buf,
+						    0);
+	/* set output frame buffer address */
+	vp8d_write_regs_value(VDPU_REG_ADDR_DST, dma_address,
+			      "VDPU_REG_ADDR_DST");
+}
+
+int rockchip_vpu_vp8d_init(struct rockchip_vpu_ctx *ctx)
+{
+	struct rockchip_vpu_dev *vpu = ctx->dev;
+	unsigned int mb_width, mb_height;
+	size_t segment_map_size;
+	int ret;
+
+	vpu_debug_enter();
+	if (vpu->variant->vpu_type == RK3229_VPU) {
+		rockchip_regs_table = rk3229_vp8d_regs_table;
+		rockchip_regs_map = rk3229_regs_map;
+	} else if (vpu->variant->vpu_type == RK3288_VPU) {
+		rockchip_regs_table = rk3288_vp8d_regs_table;
+		rockchip_regs_map = rk3288_regs_map;
+	} else {
+		vpu_err("unknown platform\n");
+		return -EPERM;
+	}
+	/* segment map table size calculation */
+	mb_width = MB_WIDTH(ctx->dst_fmt.width);
+	mb_height = MB_HEIGHT(ctx->dst_fmt.height);
+	segment_map_size = round_up(DIV_ROUND_UP(mb_width * mb_height, 4), 64);
+
+	/*
+	 * In context init the dma buffer for segment map must be allocated.
+	 * And the data in segment map buffer must be set to all zero.
+	 */
+	ret = rockchip_vpu_aux_buf_alloc(vpu, &ctx->hw.vp8d.segment_map,
+					 segment_map_size);
+	if (ret) {
+		vpu_err("allocate segment map mem failed\n");
+		return ret;
+	}
+	memset(ctx->hw.vp8d.segment_map.cpu, 0, ctx->hw.vp8d.segment_map.size);
+
+	/*
+	 * Allocate probability table buffer,
+	 * total 1208 bytes, 4K page is far enough.
+	 */
+	ret = rockchip_vpu_aux_buf_alloc(vpu, &ctx->hw.vp8d.prob_tbl,
+					 sizeof(struct vp8_prob_tbl_packed));
+	if (ret) {
+		vpu_err("allocate prob table mem failed\n");
+		goto prob_table_failed;
+	}
+
+	vpu_debug_leave();
+	return 0;
+
+prob_table_failed:
+	rockchip_vpu_aux_buf_free(vpu, &ctx->hw.vp8d.segment_map);
+
+	vpu_debug_leave();
+	return ret;
+}
+
+void rockchip_vpu_vp8d_exit(struct rockchip_vpu_ctx *ctx)
+{
+	struct rockchip_vpu_dev *vpu = ctx->dev;
+
+	vpu_debug_enter();
+
+	rockchip_vpu_aux_buf_free(vpu, &ctx->hw.vp8d.segment_map);
+	rockchip_vpu_aux_buf_free(vpu, &ctx->hw.vp8d.prob_tbl);
+
+	vpu_debug_leave();
+}
+
+void rockchip_vpu_vp8d_run(struct rockchip_vpu_ctx *ctx)
+{
+	const struct v4l2_ctrl_vp8_frame_hdr *hdr = ctx->run.vp8d.frame_hdr;
+	struct rockchip_vpu_dev *vpu = ctx->dev;
+	size_t height = ctx->dst_fmt.height;
+	size_t width = ctx->dst_fmt.width;
+	u32 mb_width, mb_height;
+	u32 reg;
+	u32 cur_reg;
+	u32 reg_base;
+	int i;
+
+	vpu_debug_enter();
+
+	memset(rk_regs_value, 0, sizeof(rk_regs_value));
+
+	rockchip_vp8d_dump_hdr(ctx);
+
+	/* reset segment_map buffer in keyframe */
+	if (!hdr->key_frame && ctx->hw.vp8d.segment_map.cpu)
+		memset(ctx->hw.vp8d.segment_map.cpu, 0,
+		       ctx->hw.vp8d.segment_map.size);
+
+	rockchip_vp8d_prob_update(ctx);
+
+	rockchip_vpu_power_on(vpu);
+
+	for (i = 0; i < vpu->variant->dec_reg_num; i++)
+		vdpu_write_relaxed(vpu, 0, i * 4);
+
+	vp8d_write_regs_value(VDPU_REG_CONFIG_DEC_TIMEOUT_E, 1,
+			      "VDPU_REG_CONFIG_DEC_TIMEOUT_E");
+	vp8d_write_regs_value(VDPU_REG_CONFIG_DEC_CLK_GATE_E, 1,
+			      "VDPU_REG_CONFIG_DEC_CLK_GATE_E");
+
+	if (hdr->key_frame)
+		vp8d_write_regs_value(VDPU_REG_DEC_CTRL0_PIC_INTER_E, 1,
+				      "VDPU_REG_DEC_CTRL0_PIC_INTER_E");
+
+	vp8d_write_regs_value(VDPU_REG_CONFIG_DEC_STRENDIAN_E, 1,
+			      "VDPU_REG_CONFIG_DEC_STRENDIAN_E");
+	vp8d_write_regs_value(VDPU_REG_CONFIG_DEC_INSWAP32_E, 1,
+			      "VDPU_REG_CONFIG_DEC_INSWAP32_E");
+	vp8d_write_regs_value(VDPU_REG_CONFIG_DEC_STRSWAP32_E, 1,
+			      "VDPU_REG_CONFIG_DEC_STRSWAP32_E");
+	vp8d_write_regs_value(VDPU_REG_CONFIG_DEC_OUTSWAP32_E, 1,
+			      "VDPU_REG_CONFIG_DEC_OUTSWAP32_E");
+	vp8d_write_regs_value(VDPU_REG_CONFIG_DEC_IN_ENDIAN, 1,
+			      "VDPU_REG_CONFIG_DEC_IN_ENDIAN");
+	vp8d_write_regs_value(VDPU_REG_CONFIG_DEC_OUT_ENDIAN, 1,
+			      "VDPU_REG_CONFIG_DEC_OUT_ENDIAN");
+
+	vp8d_write_regs_value(VDPU_REG_CONFIG_DEC_MAX_BURST, 16,
+			      "VDPU_REG_CONFIG_DEC_MAX_BURST");
+
+	vp8d_write_regs_value(VDPU_REG_DEC_CTRL0_DEC_MODE, 10,
+			      "VDPU_REG_DEC_CTRL0_DEC_MODE");
+
+	if (!(hdr->flags & V4L2_VP8_FRAME_HDR_FLAG_MB_NO_SKIP_COEFF))
+		vp8d_write_regs_value(VDPU_REG_DEC_CTRL0_SKIP_MODE, 1,
+				      "VDPU_REG_DEC_CTRL0_SKIP_MODE");
+	if (hdr->lf_hdr.level == 0)
+		vp8d_write_regs_value(VDPU_REG_DEC_CTRL0_FILTERING_DIS, 1,
+				      "VDPU_REG_DEC_CTRL0_FILTERING_DIS");
+
+	/* frame dimensions */
+	mb_width = MB_WIDTH(width);
+	mb_height = MB_HEIGHT(height);
+	vp8d_write_regs_value(VDPU_REG_DEC_PIC_MB_WIDTH, mb_width,
+			      "VDPU_REG_DEC_PIC_MB_WIDTH");
+	vp8d_write_regs_value(VDPU_REG_DEC_PIC_MB_HEIGHT_P, mb_height,
+			      "VDPU_REG_DEC_PIC_MB_HEIGHT_P");
+	vp8d_write_regs_value(VDPU_REG_DEC_CTRL1_PIC_MB_W_EXT, (mb_width >> 9),
+			      "VDPU_REG_DEC_CTRL1_PIC_MB_W_EXT");
+	vp8d_write_regs_value(VDPU_REG_DEC_CTRL1_PIC_MB_H_EXT, (mb_height >> 8),
+			      "VDPU_REG_DEC_CTRL1_PIC_MB_H_EXT");
+
+	/* bool decode info */
+	vp8d_write_regs_value(VDPU_REG_DEC_CTRL2_BOOLEAN_RANGE,
+			      hdr->bool_dec_range,
+			      "VDPU_REG_DEC_CTRL2_BOOLEAN_RANGE");
+	vp8d_write_regs_value(VDPU_REG_DEC_CTRL2_BOOLEAN_VALUE,
+			      hdr->bool_dec_value,
+			      "VDPU_REG_DEC_CTRL2_BOOLEAN_VALUE");
+
+	if (hdr->version != 3)
+		vp8d_write_regs_value(VDPU_REG_DEC_CTRL4_VC1_HEIGHT_EXT, 1,
+				      "VDPU_REG_DEC_CTRL4_VC1_HEIGHT_EXT");
+	if (hdr->version & 0x3)
+		vp8d_write_regs_value(VDPU_REG_DEC_CTRL4_BILIN_MC_E, 1,
+				      "VDPU_REG_DEC_CTRL4_BILIN_MC_E");
+
+	rockchip_vp8d_cfg_lf(ctx);
+	rockchip_vp8d_cfg_qp(ctx);
+	rockchip_vp8d_cfg_parts(ctx);
+	rockchip_vp8d_cfg_tap(ctx);
+	rockchip_vp8d_cfg_ref(ctx);
+	rockchip_vp8d_cfg_buffers(ctx);
+
+	reg = (rk_regs_value[0]
+	       & RK_GET_REG_BITS_MASK(0)) << RK_GET_REG_BITS_OFFSET(0);
+	reg_base = RK_GET_REG_BASE(0);
+
+	for (i = 1; i <= VDPU_REG_BEFORE_ENABLE; i++) {
+		cur_reg = (rk_regs_value[i]
+			   & RK_GET_REG_BITS_MASK(i))
+				<< RK_GET_REG_BITS_OFFSET(i);
+
+		if ((reg_base != 0)
+		    && (reg_base != RK_GET_REG_BASE(i)
+			|| i == VDPU_REG_BEFORE_ENABLE)) {
+			reg |= vdpu_read(vpu, reg_base);
+			vdpu_write_relaxed(vpu, reg, reg_base);
+			reg = cur_reg;
+		} else
+			reg |= cur_reg;
+
+		reg_base = RK_GET_REG_BASE(i);
+	}
+	schedule_delayed_work(&vpu->watchdog_work, msecs_to_jiffies(2000));
+
+	reg = vdpu_read(vpu, RK_GET_REG_BASE(VDPU_REG_INTERRUPT_DEC_E));
+	reg &= ~(RK_GET_REG_BITS_MASK(VDPU_REG_INTERRUPT_DEC_E)
+		 << RK_GET_REG_BITS_OFFSET(VDPU_REG_INTERRUPT_DEC_E));
+	reg |= (((1) & RK_GET_REG_BITS_MASK(VDPU_REG_INTERRUPT_DEC_E))
+		<< RK_GET_REG_BITS_OFFSET(VDPU_REG_INTERRUPT_DEC_E));
+	vdpu_write_relaxed(vpu, reg, RK_GET_REG_BASE(VDPU_REG_INTERRUPT_DEC_E));
+
+	vpu_debug_leave();
+}
+
+int rockchip_vdpu_irq(int irq, struct rockchip_vpu_dev *vpu)
+{
+	u32 mask;
+	u32 status = vdpu_read(vpu,
+			       RK_GET_REG_BASE(VDPU_REG_INTERRUPT_DEC_IRQ));
+
+	vdpu_write(vpu, 0, RK_GET_REG_BASE(VDPU_REG_INTERRUPT_DEC_IRQ));
+
+	vpu_debug(3, "vdpu_irq status: %08x\n", status);
+
+	mask = RK_GET_REG_BITS_MASK(VDPU_REG_INTERRUPT_DEC_IRQ);
+	mask = mask << RK_GET_REG_BITS_OFFSET(VDPU_REG_INTERRUPT_DEC_IRQ);
+	if (status & mask) {
+		vdpu_write(vpu, 0,
+			   RK_GET_REG_BASE(VDPU_REG_CONFIG_DEC_MAX_BURST));
+		return 0;
+	}
+
+	return -1;
+}
+
+/*
+ * Initialization/clean-up.
+ */
+
+void rockchip_vpu_dec_reset(struct rockchip_vpu_ctx *ctx)
+{
+	struct rockchip_vpu_dev *vpu = ctx->dev;
+	u32 mask;
+
+	mask = RK_GET_REG_BITS_MASK(VDPU_REG_INTERRUPT_DEC_IRQ_DIS);
+	mask = mask << RK_GET_REG_BITS_OFFSET(VDPU_REG_INTERRUPT_DEC_IRQ_DIS);
+	vdpu_write(vpu, mask, RK_GET_REG_BASE(VDPU_REG_INTERRUPT_DEC_IRQ_DIS));
+	vdpu_write(vpu, 0, RK_GET_REG_BASE(VDPU_REG_CONFIG_DEC_TIMEOUT_E));
+}
diff --git a/drivers/media/platform/rockchip-vpu/rockchip_vp8d_regs.h b/drivers/media/platform/rockchip-vpu/rockchip_vp8d_regs.h
new file mode 100644
index 0000000..dc01554
--- /dev/null
+++ b/drivers/media/platform/rockchip-vpu/rockchip_vp8d_regs.h
@@ -0,0 +1,1594 @@
+/*
+ * Rockchip VPU codec driver
+ *
+ * Copyright (C) 2015 Rockchip Electronics Co., Ltd.
+ *      Alpha Lin <alpha.lin at rock-chips.com>
+ *
+ * * Copyright (C) 2016 Rockchip Electronics Co., Ltd.
+ *      Jung Zhao <jung.zhao at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef ROCKCHIP_VP8D_REGS_H
+#define ROCKCHIP_VP8D_REGS_H
+
+enum VP8_REGS {
+	VDPU_REG_CONFIG_DEC_TIMEOUT_E,
+	VDPU_REG_CONFIG_DEC_CLK_GATE_E,
+	VDPU_REG_DEC_CTRL0_PIC_INTER_E,
+	VDPU_REG_CONFIG_DEC_STRENDIAN_E,
+	VDPU_REG_CONFIG_DEC_INSWAP32_E,
+	VDPU_REG_CONFIG_DEC_STRSWAP32_E,
+	VDPU_REG_CONFIG_DEC_OUTSWAP32_E,
+	VDPU_REG_CONFIG_DEC_IN_ENDIAN,
+	VDPU_REG_CONFIG_DEC_OUT_ENDIAN,
+	VDPU_REG_CONFIG_DEC_MAX_BURST,
+	VDPU_REG_DEC_CTRL0_DEC_MODE,
+	VDPU_REG_DEC_CTRL0_SKIP_MODE,
+	VDPU_REG_DEC_CTRL0_FILTERING_DIS,
+	VDPU_REG_DEC_PIC_MB_WIDTH,
+	VDPU_REG_DEC_PIC_MB_HEIGHT_P,
+	VDPU_REG_DEC_CTRL1_PIC_MB_W_EXT,
+	VDPU_REG_DEC_CTRL1_PIC_MB_H_EXT,
+	VDPU_REG_DEC_CTRL2_BOOLEAN_RANGE,
+	VDPU_REG_DEC_CTRL2_BOOLEAN_VALUE,
+	VDPU_REG_DEC_CTRL4_VC1_HEIGHT_EXT,
+	VDPU_REG_DEC_CTRL4_BILIN_MC_E,
+	VDPU_REG_REF_PIC_LF_LEVEL_0,
+	VDPU_REG_REF_PIC_LF_LEVEL_1,
+	VDPU_REG_REF_PIC_LF_LEVEL_2,
+	VDPU_REG_REF_PIC_LF_LEVEL_3,
+	VDPU_REG_REF_PIC_FILT_SHARPNESS,
+	VDPU_REG_REF_PIC_FILT_TYPE_E,
+	VDPU_REG_FILT_MB_ADJ_0,
+	VDPU_REG_FILT_MB_ADJ_1,
+	VDPU_REG_FILT_MB_ADJ_2,
+	VDPU_REG_FILT_MB_ADJ_3,
+	VDPU_REG_REF_PIC_ADJ_0,
+	VDPU_REG_REF_PIC_ADJ_1,
+	VDPU_REG_REF_PIC_ADJ_2,
+	VDPU_REG_REF_PIC_ADJ_3,
+	VDPU_REG_REF_PIC_QUANT_0,
+	VDPU_REG_REF_PIC_QUANT_1,
+	VDPU_REG_REF_PIC_QUANT_2,
+	VDPU_REG_REF_PIC_QUANT_3,
+	VDPU_REG_REF_PIC_QUANT_DELTA_0,
+	VDPU_REG_REF_PIC_QUANT_DELTA_1,
+	VDPU_REG_REF_PIC_QUANT_DELTA_2,
+	VDPU_REG_REF_PIC_QUANT_DELTA_3,
+	VDPU_REG_REF_PIC_QUANT_DELTA_4,
+	VDPU_REG_VP8_ADDR_CTRL_PART,
+	VDPU_REG_DEC_CTRL2_STRM1_START_BIT,
+	VDPU_REG_DEC_CTRL6_STREAM1_LEN,
+	VDPU_REG_DEC_CTRL6_COEFFS_PART_AM,
+	VDPU_REG_DEC_CTRL3_STREAM_LEN,
+	VDPU_REG_ADDR_STR,
+	VDPU_REG_VP8_DCT_BASE_0,
+	VDPU_REG_VP8_DCT_BASE_1,
+	VDPU_REG_VP8_DCT_BASE_2,
+	VDPU_REG_VP8_DCT_BASE_3,
+	VDPU_REG_VP8_DCT_BASE_4,
+	VDPU_REG_VP8_DCT_BASE_5,
+	VDPU_REG_VP8_DCT_BASE_6,
+	VDPU_REG_DEC_CTRL2_STRM_START_BIT,
+	VDPU_REG_DEC_CTRL4_DCT1_START_BIT,
+	VDPU_REG_DEC_CTRL4_DCT2_START_BIT,
+	VDPU_REG_DEC_CTRL7_DCT3_START_BIT,
+	VDPU_REG_DEC_CTRL7_DCT4_START_BIT,
+	VDPU_REG_DEC_CTRL7_DCT5_START_BIT,
+	VDPU_REG_DEC_CTRL7_DCT6_START_BIT,
+	VDPU_REG_DEC_CTRL7_DCT7_START_BIT,
+	VDPU_REG_PRED_FLT_NONE_0,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_0_0,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_0_1,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_0_2,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_0_3,
+	VDPU_REG_PRED_FLT_NONE_1,
+	VDPU_REG_PRED_FLT_NONE_2,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_1_0,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_1_1,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_1_2,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_1_3,
+	VDPU_REG_PRED_FLT_NONE_3,
+	VDPU_REG_BD_REF_PIC_PRED_TAP_2_M1,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_2_0,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_2_1,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_2_2,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_2_3,
+	VDPU_REG_BD_REF_PIC_PRED_TAP_2_4,
+	VDPU_REG_PRED_FLT_NONE_4,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_3_0,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_3_1,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_3_2,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_3_3,
+	VDPU_REG_PRED_FLT_NONE_5,
+	VDPU_REG_BD_REF_PIC_PRED_TAP_4_M1,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_4_0,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_4_1,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_4_2,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_4_3,
+	VDPU_REG_BD_REF_PIC_PRED_TAP_4_4,
+	VDPU_REG_PRED_FLT_NONE_6,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_5_0,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_5_1,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_5_2,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_5_3,
+	VDPU_REG_PRED_FLT_NONE_7,
+	VDPU_REG_BD_REF_PIC_PRED_TAP_6_M1,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_6_0,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_6_1,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_6_2,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_6_3,
+	VDPU_REG_BD_REF_PIC_PRED_TAP_6_4,
+	VDPU_REG_PRED_FLT_NONE_8,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_7_0,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_7_1,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_7_2,
+	VDPU_REG_PRED_FLT_PRED_BC_TAP_7_3,
+	VDPU_REG_PRED_FLT_NONE_9,
+	VDPU_REG_VP8_ADDR_REF0,
+	VDPU_REG_VP8_ADDR_REF2_5_0,
+	VDPU_REG_VP8_GREF_SIGN_BIAS_0,
+	VDPU_REG_VP8_ADDR_REF2_5_1,
+	VDPU_REG_VP8_AREF_SIGN_BIAS_1,
+	VDPU_REG_ADDR_QTABLE,
+	VDPU_REG_FWD_PIC1_SEGMENT_BASE,
+	VDPU_REG_FWD_PIC1_SEGMENT_E,
+	VDPU_REG_FWD_PIC1_SEGMENT_UPD_E,
+	VDPU_REG_ADDR_DST,
+	VDPU_REG_INTERRUPT_DEC_IRQ,
+	VDPU_REG_INTERRUPT_DEC_IRQ_DIS,
+	VDPU_REG_BEFORE_ENABLE,
+	VDPU_REG_INTERRUPT_DEC_E,
+	VDPU_REG_LAST,
+};
+
+/* {register_base, mask, bit_offset } */
+u32 rk3229_vp8d_regs_table[][3] = {
+	{ 0, 0, 0 },
+	{
+		/* VDPU_REG_DEC_CTRL0 */ 0x0c8,
+		/* VDPU_REG_REF_BUF_CTRL2_REFBU2_PICID */ 0x1f,
+		25
+	},
+	{
+		/* VDPU_REG_DEC_CTRL0 */ 0x0c8,
+		/* VDPU_REG_REF_BUF_CTRL2_REFBU2_THR */ 0xfff,
+		13
+	},
+	{
+		/* VDPU_REG_DEC_CTRL0 */ 0x0c8,
+		/* VDPU_REG_CONFIG_TILED_MODE_LSB */ 0x01,
+		12
+	},
+	{
+		/* VDPU_REG_DEC_CTRL0 */ 0x0c8,
+		/* VDPU_REG_CONFIG_DEC_ADV_PRE_DIS */ 0x01,
+		11
+	},
+	{
+		/* VDPU_REG_DEC_CTRL0 */ 0x0c8,
+		/* VDPU_REG_CONFIG_DEC_SCMD_DIS */ 0x01,
+		10
+	},
+	{
+		/* VDPU_REG_DEC_CTRL0 */ 0x0c8,
+		/* VDPU_REG_DEC_CTRL0_SKIP_MODE */ 0x01,
+		9
+	},
+	{
+		/* VDPU_REG_DEC_CTRL0 */ 0x0c8,
+		/* VDPU_REG_DEC_CTRL0_FILTERING_DIS */ 0x01,
+		8
+	},
+	{
+		/* VDPU_REG_DEC_CTRL0 */ 0x0c8,
+		/* VDPU_REG_DEC_CTRL0_PIC_FIXED_QUANT */ 0x01,
+		7
+	},
+	{
+		/* VDPU_REG_STREAM_LEN */ 0x0cc,
+		/* VDPU_REG_DEC_CTRL3_INIT_QP */ 0x3f,
+		25
+	},
+	{
+		/* VDPU_REG_STREAM_LEN */ 0x0cc,
+		/* VDPU_REG_DEC_STREAM_LEN_HI */ 0x01,
+		24
+	},
+	{
+		/* VDPU_REG_STREAM_LEN */ 0x0cc,
+		/* VDPU_REG_DEC_CTRL3_STREAM_LEN */ 0xffffff,
+		0
+	},
+	{
+		/* VDPU_REG_DEC_FORMAT */ 0x0d4,
+		/* VDPU_REG_DEC_CTRL0_DEC_MODE */ 0x0f,
+		0
+	},
+	{
+		/* VDPU_REG_DATA_ENDIAN */ 0x0d8,
+		/* VDPU_REG_CONFIG_DEC_STRENDIAN_E */ 0x01,
+		5
+	},
+	{
+		/* VDPU_REG_DATA_ENDIAN */ 0x0d8,
+		/* VDPU_REG_CONFIG_DEC_STRSWAP32_E */ 0x01,
+		4
+	},
+	{
+		/* VDPU_REG_DATA_ENDIAN */ 0x0d8,
+		/* VDPU_REG_CONFIG_DEC_OUTSWAP32_E */ 0x01,
+		3
+	},
+	{
+		/* VDPU_REG_DATA_ENDIAN */ 0x0d8,
+		/* VDPU_REG_CONFIG_DEC_INSWAP32_E */ 0x01,
+		2
+	},
+	{
+		/* VDPU_REG_DATA_ENDIAN */ 0x0d8,
+		/* VDPU_REG_CONFIG_DEC_OUT_ENDIAN */ 0x01,
+		1
+	},
+	{
+		/* VDPU_REG_DATA_ENDIAN */ 0x0d8,
+		/* VDPU_REG_CONFIG_DEC_IN_ENDIAN */ 0x01,
+		0
+	},
+	{
+		/* VDPU_REG_INTERRUPT */ 0x0dc,
+		/* VDPU_REG_INTERRUPT_DEC_TIMEOUT */ 0x01,
+		13
+	},
+	{
+		/* VDPU_REG_INTERRUPT */ 0x0dc,
+		/* VDPU_REG_INTERRUPT_DEC_ERROR_INT */ 0x01,
+		12
+	},
+	{
+		/* VDPU_REG_INTERRUPT */ 0x0dc,
+		/* VDPU_REG_INTERRUPT_DEC_PIC_INF */ 0x01,
+		10
+	},
+	{
+		/* VDPU_REG_INTERRUPT */ 0x0dc,
+		/* VDPU_REG_INTERRUPT_DEC_SLICE_INT */ 0x01,
+		9
+	},
+	{
+		/* VDPU_REG_INTERRUPT */ 0x0dc,
+		/* VDPU_REG_INTERRUPT_DEC_ASO_INT */ 0x01,
+		8
+	},
+	{
+		/* VDPU_REG_INTERRUPT */ 0x0dc,
+		/* VDPU_REG_INTERRUPT_DEC_BUFFER_INT */ 0x01,
+		6
+	},
+	{
+		/* VDPU_REG_INTERRUPT */ 0x0dc,
+		/* VDPU_REG_INTERRUPT_DEC_BUS_INT */ 0x01,
+		5
+	},
+	{
+		/* VDPU_REG_INTERRUPT */ 0x0dc,
+		/* VDPU_REG_INTERRUPT_DEC_RDY_INT */ 0x01,
+		4
+	},
+	{
+		/* VDPU_REG_INTERRUPT */ 0x0dc,
+		/* VDPU_REG_INTERRUPT_DEC_IRQ_DIS */ 0x01,
+		1
+	},
+	{
+		/* VDPU_REG_INTERRUPT */ 0x0dc,
+		/* VDPU_REG_INTERRUPT_DEC_IRQ */ 0x01,
+		0
+	},
+	{
+		/* VDPU_REG_AXI_CTRL */ 0x0e0,
+		/* VDPU_REG_AXI_DEC_SEL */ 0x01,
+		23
+	},
+	{
+		/* VDPU_REG_AXI_CTRL */ 0x0e0,
+		/* VDPU_REG_CONFIG_DEC_DATA_DISC_E */ 0x01,
+		22
+	},
+	{
+		/* VDPU_REG_AXI_CTRL */ 0x0e0,
+		/* VDPU_REG_PARAL_BUS_E */ 0x01,
+		21
+	},
+	{
+		/* VDPU_REG_AXI_CTRL */ 0x0e0,
+		/* VDPU_REG_CONFIG_DEC_MAX_BURST */ 0x1f,
+		16
+	},
+	{
+		/* VDPU_REG_AXI_CTRL */ 0x0e0,
+		/* VDPU_REG_DEC_CTRL0_DEC_AXI_WR_ID */ 0xff,
+		8
+	},
+	{
+		/* VDPU_REG_AXI_CTRL */ 0x0e0,
+		/* VDPU_REG_CONFIG_DEC_AXI_RD_ID */ 0xff,
+		0
+	},
+	{
+		/* VDPU_REG_EN_FLAGS */ 0x0e4,
+		/* VDPU_REG_AHB_HLOCK_E */ 0x01,
+		31
+	},
+	{
+		/* VDPU_REG_EN_FLAGS */ 0x0e4,
+		/* VDPU_REG_CACHE_E */ 0x01,
+		29
+	},
+	{
+		/* VDPU_REG_EN_FLAGS */ 0x0e4,
+		/* VDPU_REG_PREFETCH_SINGLE_CHANNEL_E */ 0x01,
+		28
+	},
+	{
+		/* VDPU_REG_EN_FLAGS */ 0x0e4,
+		/* VDPU_REG_INTRA_3_CYCLE_ENHANCE */ 0x01,
+		27
+	},
+	{
+		/* VDPU_REG_EN_FLAGS */ 0x0e4,
+		/* VDPU_REG_INTRA_DOUBLE_SPEED */ 0x01,
+		26
+	},
+	{
+		/* VDPU_REG_EN_FLAGS */ 0x0e4,
+		/* VDPU_REG_INTER_DOUBLE_SPEED */ 0x01,
+		25
+	},
+	{
+		/* VDPU_REG_EN_FLAGS */ 0x0e4,
+		/* VDPU_REG_DEC_CTRL3_START_CODE_E */ 0x01,
+		22
+	},
+	{
+		/* VDPU_REG_EN_FLAGS */ 0x0e4,
+		/* VDPU_REG_DEC_CTRL3_CH_8PIX_ILEAV_E */ 0x01,
+		21
+	},
+	{
+		/* VDPU_REG_EN_FLAGS */ 0x0e4,
+		/* VDPU_REG_DEC_CTRL0_RLC_MODE_E */ 0x01,
+		20
+	},
+	{
+		/* VDPU_REG_EN_FLAGS */ 0x0e4,
+		/* VDPU_REG_DEC_CTRL0_DIVX3_E */ 0x01,
+		19
+	},
+	{
+		/* VDPU_REG_EN_FLAGS */ 0x0e4,
+		/* VDPU_REG_DEC_CTRL0_PJPEG_E */ 0x01,
+		18
+	},
+	{
+		/* VDPU_REG_EN_FLAGS */ 0x0e4,
+		/* VDPU_REG_DEC_CTRL0_PIC_INTERLACE_E */ 0x01,
+		17
+	},
+	{
+		/* VDPU_REG_EN_FLAGS */ 0x0e4,
+		/* VDPU_REG_DEC_CTRL0_PIC_FIELDMODE_E */ 0x01,
+		16
+	},
+	{
+		/* VDPU_REG_EN_FLAGS */ 0x0e4,
+		/* VDPU_REG_DEC_CTRL0_PIC_B_E */ 0x01,
+		15
+	},
+	{
+		/* VDPU_REG_EN_FLAGS */ 0x0e4,
+		/* VDPU_REG_DEC_CTRL0_PIC_INTER_E */ 0x01,
+		14
+	},
+	{
+		/* VDPU_REG_EN_FLAGS */ 0x0e4,
+		/* VDPU_REG_DEC_CTRL0_PIC_TOPFIELD_E */ 0x01,
+		13
+	},
+	{
+		/* VDPU_REG_EN_FLAGS */ 0x0e4,
+		/* VDPU_REG_DEC_CTRL0_FWD_INTERLACE_E */ 0x01,
+		12
+	},
+	{
+		/* VDPU_REG_EN_FLAGS */ 0x0e4,
+		/* VDPU_REG_DEC_CTRL0_SORENSON_E */ 0x01,
+		11
+	},
+	{
+		/* VDPU_REG_EN_FLAGS */ 0x0e4,
+		/* VDPU_REG_DEC_CTRL0_WRITE_MVS_E */ 0x01,
+		10
+	},
+	{
+		/* VDPU_REG_EN_FLAGS */ 0x0e4,
+		/* VDPU_REG_DEC_CTRL0_REF_TOPFIELD_E */ 0x01,
+		9
+	},
+	{
+		/* VDPU_REG_EN_FLAGS */ 0x0e4,
+		/* VDPU_REG_DEC_CTRL0_REFTOPFIRST_E */ 0x01,
+		8
+	},
+	{
+		/* VDPU_REG_EN_FLAGS */ 0x0e4,
+		/* VDPU_REG_DEC_CTRL0_SEQ_MBAFF_E */ 0x01,
+		7
+	},
+	{
+		/* VDPU_REG_EN_FLAGS */ 0x0e4,
+		/* VDPU_REG_DEC_CTRL0_PICORD_COUNT_E */ 0x01,
+		6
+	},
+	{
+		/* VDPU_REG_EN_FLAGS */ 0x0e4,
+		/* VDPU_REG_CONFIG_DEC_TIMEOUT_E */ 0x01,
+		5
+	},
+	{
+		/* VDPU_REG_EN_FLAGS */ 0x0e4,
+		/* VDPU_REG_CONFIG_DEC_CLK_GATE_E */ 0x01,
+		4
+	},
+	{
+		/* VDPU_REG_EN_FLAGS */ 0x0e4,
+		/* VDPU_REG_DEC_CTRL0_DEC_OUT_DIS */ 0x01,
+		2
+	},
+	{
+		/* VDPU_REG_EN_FLAGS */ 0x0e4,
+		/* VDPU_REG_REF_BUF_CTRL2_REFBU2_BUF_E */ 0x01,
+		1
+	},
+	{
+		/* VDPU_REG_EN_FLAGS */ 0x0e4,
+		/* VDPU_REG_INTERRUPT_DEC_E */ 0x01,
+		0
+	},
+	{
+		/* VDPU_REG_PRED_FLT */ 0x0ec,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_0_0 */ 0x3ff,
+		22
+	},
+	{
+		/* VDPU_REG_PRED_FLT */ 0x0ec,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_0_1 */ 0x3ff,
+		12
+	},
+	{
+		/* VDPU_REG_PRED_FLT */ 0x0ec,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_0_2 */ 0x3ff,
+		2
+	},
+	{
+		/* VDPU_REG_ADDR_QTABLE */ 0x0f4,
+		0xffffffff, 0
+	},
+	{
+		/* VDPU_REG_ADDR_DST */ 0x0fc,
+		0xffffffff, 0
+	},
+	{
+		/* VDPU_REG_ADDR_STR */ 0x100,
+		0xffffffff, 0
+	},
+	{
+		/* VDPU_REG_VP8_PIC_MB_SIZE */ 0x1e0,
+		/* VDPU_REG_DEC_PIC_MB_WIDTH */ 0x1ff,
+		23
+	},
+	{
+		/* VDPU_REG_VP8_PIC_MB_SIZE */ 0x1e0,
+		/* VDPU_REG_DEC_MB_WIDTH_OFF */ 0x0f,
+		19
+	},
+	{
+		/* VDPU_REG_VP8_PIC_MB_SIZE */ 0x1e0,
+		/* VDPU_REG_DEC_PIC_MB_HEIGHT_P */ 0xff,
+		11
+	},
+	{
+		/* VDPU_REG_VP8_PIC_MB_SIZE */ 0x1e0,
+		/* VDPU_REG_DEC_MB_HEIGHT_OFF */ 0x0f,
+		7
+	},
+	{
+		/* VDPU_REG_VP8_PIC_MB_SIZE */ 0x1e0,
+		/* VDPU_REG_DEC_CTRL1_PIC_MB_W_EXT */ 0x07,
+		3
+	},
+	{
+		/* VDPU_REG_VP8_PIC_MB_SIZE */ 0x1e0,
+		/* VDPU_REG_DEC_CTRL1_PIC_MB_H_EXT */ 0x07,
+		0
+	},
+	{
+		/* VDPU_REG_VP8_DCT_START_BIT */ 0x1e4,
+		/* VDPU_REG_DEC_CTRL4_DCT1_START_BIT */ 0x3f,
+		26
+	},
+	{
+		/* VDPU_REG_VP8_DCT_START_BIT */ 0x1e4,
+		/* VDPU_REG_DEC_CTRL4_DCT2_START_BIT */ 0x3f,
+		20
+	},
+	{
+		/* VDPU_REG_VP8_DCT_START_BIT */ 0x1e4,
+		/* VDPU_REG_DEC_CTRL4_VC1_HEIGHT_EXT */ 0x01,
+		13
+	},
+	{
+		/* VDPU_REG_VP8_DCT_START_BIT */ 0x1e4,
+		/* VDPU_REG_DEC_CTRL4_BILIN_MC_E */ 0x01,
+		12
+	},
+	{
+		/* VDPU_REG_VP8_CTRL0 */ 0x1e8,
+		/* VDPU_REG_DEC_CTRL2_STRM_START_BIT */ 0x3f,
+		26
+	},
+	{
+		/* VDPU_REG_VP8_CTRL0 */ 0x1e8,
+		/* VDPU_REG_DEC_CTRL2_STRM1_START_BIT */ 0x3f,
+		18
+	},
+	{
+		/* VDPU_REG_VP8_CTRL0 */ 0x1e8,
+		/* VDPU_REG_DEC_CTRL2_BOOLEAN_VALUE */ 0xff,
+		8
+	},
+	{
+		/* VDPU_REG_VP8_CTRL0 */ 0x1e8,
+		/* VDPU_REG_DEC_CTRL2_BOOLEAN_RANGE */ 0xff,
+		0
+	},
+	{
+		/* VDPU_REG_VP8_DATA_VAL */ 0x1f0,
+		/* VDPU_REG_DEC_CTRL6_COEFFS_PART_AM */ 0x0f,
+		24
+	},
+	{
+		/* VDPU_REG_VP8_DATA_VAL */ 0x1f0,
+		/* VDPU_REG_DEC_CTRL6_STREAM1_LEN */ 0xffffff,
+		0
+	},
+	{
+		/* VDPU_REG_PRED_FLT7 */ 0x1f4,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_5_1 */ 0x3ff,
+		22
+	},
+	{
+		/* VDPU_REG_PRED_FLT7 */ 0x1f4,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_5_2 */ 0x3ff,
+		12
+	},
+	{
+		/* VDPU_REG_PRED_FLT7 */ 0x1f4,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_5_3 */ 0x3ff,
+		2
+	},
+	{
+		/* VDPU_REG_PRED_FLT8 */ 0x1f8,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_6_0 */ 0x3ff,
+		22
+	},
+	{
+		/* VDPU_REG_PRED_FLT8 */ 0x1f8,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_6_1 */ 0x3ff,
+		12
+	},
+	{
+		/* VDPU_REG_PRED_FLT8 */ 0x1f8,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_6_2 */ 0x3ff,
+		2
+	},
+	{
+		/* VDPU_REG_PRED_FLT9 */ 0x1fc,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_6_3 */ 0x3ff,
+		22
+	},
+	{
+		/* VDPU_REG_PRED_FLT9 */ 0x1fc,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_7_0 */ 0x3ff,
+		12
+	},
+	{
+		/* VDPU_REG_PRED_FLT9 */ 0x1fc,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_7_1 */ 0x3ff,
+		2
+	},
+	{
+		/* VDPU_REG_PRED_FLT10 */ 0x200,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_7_2 */ 0x3ff,
+		22
+	},
+	{
+		/* VDPU_REG_PRED_FLT10 */ 0x200,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_7_3 */ 0x3ff,
+		12
+	},
+	{
+		/* VDPU_REG_PRED_FLT10 */ 0x200,
+		/* VDPU_REG_BD_REF_PIC_PRED_TAP_2_M1 */ 0x03,
+		10
+	},
+	{
+		/* VDPU_REG_PRED_FLT10 */ 0x200,
+		/* VDPU_REG_BD_REF_PIC_PRED_TAP_2_4 */ 0x03,
+		8
+	},
+	{
+		/* VDPU_REG_PRED_FLT10 */ 0x200,
+		/* VDPU_REG_BD_REF_PIC_PRED_TAP_4_M1 */ 0x03,
+		6
+	},
+	{
+		/* VDPU_REG_PRED_FLT10 */ 0x200,
+		/* VDPU_REG_BD_REF_PIC_PRED_TAP_4_4 */ 0x03,
+		4
+	},
+	{
+		/* VDPU_REG_PRED_FLT10 */ 0x200,
+		/* VDPU_REG_BD_REF_PIC_PRED_TAP_6_M1 */ 0x03,
+		2
+	},
+	{
+		/* VDPU_REG_PRED_FLT10 */ 0x200,
+		/* VDPU_REG_BD_REF_PIC_PRED_TAP_6_4 */ 0x03,
+		0
+	},
+	{
+		/* VDPU_REG_FILTER_LEVEL */ 0x204,
+		/* VDPU_REG_REF_PIC_LF_LEVEL_0 */ 0x3f,
+		18
+	},
+	{
+		/* VDPU_REG_FILTER_LEVEL */ 0x204,
+		/* VDPU_REG_REF_PIC_LF_LEVEL_1 */ 0x3f,
+		12
+	},
+	{
+		/* VDPU_REG_FILTER_LEVEL */ 0x204,
+		/* VDPU_REG_REF_PIC_LF_LEVEL_2 */ 0x3f,
+		6
+	},
+	{
+		/* VDPU_REG_FILTER_LEVEL */ 0x204,
+		/* VDPU_REG_REF_PIC_LF_LEVEL_3 */ 0x3f,
+		0
+	},
+	{
+		/* VDPU_REG_VP8_QUANTER0 */ 0x208,
+		/* VDPU_REG_REF_PIC_QUANT_DELTA_0 */ 0x1f,
+		27
+	},
+	{
+		/* VDPU_REG_VP8_QUANTER0 */ 0x208,
+		/* VDPU_REG_REF_PIC_QUANT_DELTA_1 */ 0x1f,
+		22
+	},
+	{
+		/* VDPU_REG_VP8_QUANTER0 */ 0x208,
+		/* VDPU_REG_REF_PIC_QUANT_0 */ 0x7ff,
+		11
+	},
+	{
+		/* VDPU_REG_VP8_QUANTER0 */ 0x208,
+		/* VDPU_REG_REF_PIC_QUANT_1 */ 0x7ff,
+		0
+	},
+	{
+		/* VDPU_REG_VP8_ADDR_REF0 */ 0x20c,
+		0xffffffff, 0
+	},
+	{
+		/* VDPU_REG_FILTER_MB_ADJ */ 0x210,
+		/* VDPU_REG_REF_PIC_FILT_TYPE_E */ 0x01,
+		31
+	},
+	{
+		/* VDPU_REG_FILTER_MB_ADJ */ 0x210,
+		/* VDPU_REG_REF_PIC_FILT_SHARPNESS */ 0x07,
+		28
+	},
+	{
+		/* VDPU_REG_FILTER_MB_ADJ */ 0x210,
+		/* VDPU_REG_FILT_MB_ADJ_0 */ 0x7f,
+		21
+	},
+	{
+		/* VDPU_REG_FILTER_MB_ADJ */ 0x210,
+		/* VDPU_REG_FILT_MB_ADJ_1 */ 0x7f,
+		14
+	},
+	{
+		/* VDPU_REG_FILTER_MB_ADJ */ 0x210,
+		/* VDPU_REG_FILT_MB_ADJ_2 */ 0x7f,
+		7
+	},
+	{
+		/* VDPU_REG_FILTER_MB_ADJ */ 0x210,
+		/* VDPU_REG_FILT_MB_ADJ_3 */ 0x7f,
+		0
+	},
+	{
+		/* VDPU_REG_FILTER_REF_ADJ */ 0x214,
+		/* VDPU_REG_REF_PIC_ADJ_0 */ 0x7f,
+		21
+	},
+	{
+		/* VDPU_REG_FILTER_REF_ADJ */ 0x214,
+		/* VDPU_REG_REF_PIC_ADJ_1 */ 0x7f,
+		14
+	},
+	{
+		/* VDPU_REG_FILTER_REF_ADJ */ 0x214,
+		/* VDPU_REG_REF_PIC_ADJ_2 */ 0x7f,
+		7
+	},
+	{
+		/* VDPU_REG_FILTER_REF_ADJ */ 0x214,
+		/* VDPU_REG_REF_PIC_ADJ_3 */ 0x7f,
+		0
+	},
+	{
+		/* VDPU_REG_VP8_ADDR_REF2_5(2) */ 0x220,
+		/* VDPU_REG_VP8_ADDR_REF2_5_2 */ 0xffffffff,
+		0
+	},
+	{
+		/* VDPU_REG_VP8_ADDR_REF2_5(2) */ 0x220,
+		/* VDPU_REG_VP8_GREF_SIGN_BIAS_2 */ 0x01, 0},
+	{
+		/* VDPU_REG_VP8_ADDR_REF2_5(2) */ 0x220,
+		/* VDPU_REG_VP8_AREF_SIGN_BIAS_2 */ 0x01, 0},
+	{
+		/* VDPU_REG_VP8_ADDR_REF2_5(3) */ 0x224,
+		/* VDPU_REG_VP8_ADDR_REF2_5_3 */ 0xffffffff,
+		0
+	},
+	{
+		/* VDPU_REG_VP8_ADDR_REF2_5(3) */ 0x224,
+		/* VDPU_REG_VP8_GREF_SIGN_BIAS_3 */ 0x01, 0},
+	{
+		/* VDPU_REG_VP8_ADDR_REF2_5(3) */ 0x224,
+		/* VDPU_REG_VP8_AREF_SIGN_BIAS_3 */ 0x01, 0},
+	{
+		/* VDPU_REG_VP8_DCT_BASE0 */ 0x230,
+		0xffffffff, 0
+	},
+	{
+		/* VDPU_REG_VP8_DCT_BASE1 */ 0x234,
+		0xffffffff, 0
+	},
+	{
+		/* VDPU_REG_VP8_DCT_BASE2 */ 0x238,
+		0xffffffff, 0
+	},
+	{
+		/* VDPU_REG_VP8_DCT_BASE3 */ 0x23C,
+		0xffffffff, 0
+	},
+	{
+		/* VDPU_REG_VP8_DCT_BASE4 */ 0x240,
+		0xffffffff, 0
+	},
+	{
+		/* VDPU_REG_VP8_ADDR_CTRL_PART */ 0x244,
+		0xffffffff, 0
+	},
+	{
+		/* VDPU_REG_VP8_DCT_BASE6 */ 0x248,
+		0xffffffff, 0
+	},
+	{
+		/* VDPU_REG_VP8_ADDR_REF1 */ 0x250,
+		0xffffffff, 0
+	},
+	{
+		/* VDPU_REG_VP8_SEGMENT_VAL */ 0x254,
+		/* VDPU_REG_FWD_PIC1_SEGMENT_BASE */ 0xffffffff,
+		0
+	},
+	{
+		/* VDPU_REG_VP8_SEGMENT_VAL */ 0x254,
+		/* VDPU_REG_FWD_PIC1_SEGMENT_UPD_E */ 0x01,
+		1
+	},
+	{
+		/* VDPU_REG_VP8_SEGMENT_VAL */ 0x254,
+		/* VDPU_REG_FWD_PIC1_SEGMENT_E */ 0x01,
+		0
+	},
+	{
+		/* VDPU_REG_VP8_DCT_START_BIT2 */ 0x258,
+		/* VDPU_REG_DEC_CTRL7_DCT3_START_BIT */ 0x3f,
+		24
+	},
+	{
+		/* VDPU_REG_VP8_DCT_START_BIT2 */ 0x258,
+		/* VDPU_REG_DEC_CTRL7_DCT4_START_BIT */ 0x3f,
+		18
+	},
+	{
+		/* VDPU_REG_VP8_DCT_START_BIT2 */ 0x258,
+		/* VDPU_REG_DEC_CTRL7_DCT5_START_BIT */ 0x3f,
+		12
+	},
+	{
+		/* VDPU_REG_VP8_DCT_START_BIT2 */ 0x258,
+		/* VDPU_REG_DEC_CTRL7_DCT6_START_BIT */ 0x3f,
+		6
+	},
+	{
+		/* VDPU_REG_VP8_DCT_START_BIT2 */ 0x258,
+		/* VDPU_REG_DEC_CTRL7_DCT7_START_BIT */ 0x3f,
+		0
+	},
+	{
+		/* VDPU_REG_VP8_QUANTER1 */ 0x25c,
+		/* VDPU_REG_REF_PIC_QUANT_DELTA_2 */ 0x1f,
+		27
+	},
+	{
+		/* VDPU_REG_VP8_QUANTER1 */ 0x25c,
+		/* VDPU_REG_REF_PIC_QUANT_DELTA_3 */ 0x1f,
+		22
+	},
+	{
+		/* VDPU_REG_VP8_QUANTER1 */ 0x25c,
+		/* VDPU_REG_REF_PIC_QUANT_2 */ 0x7ff,
+		11
+	},
+	{
+		/* VDPU_REG_VP8_QUANTER1 */ 0x25c,
+		/* VDPU_REG_REF_PIC_QUANT_3 */ 0x7ff,
+		0
+	},
+	{
+		/* VDPU_REG_VP8_QUANTER2 */ 0x260,
+		/* VDPU_REG_REF_PIC_QUANT_DELTA_4 */ 0x1f,
+		27
+	},
+	{
+		/* VDPU_REG_VP8_QUANTER2 */ 0x260,
+		/* VDPU_REG_REF_PIC_QUANT_4 */ 0x7ff,
+		11
+	},
+	{
+		/* VDPU_REG_VP8_QUANTER2 */ 0x260,
+		/* VDPU_REG_REF_PIC_QUANT_5 */ 0x7ff,
+		0
+	},
+	{
+		/* VDPU_REG_PRED_FLT1 */ 0x264,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_0_3 */ 0x3ff,
+		22
+	},
+	{
+		/* VDPU_REG_PRED_FLT1 */ 0x264,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_1_0 */ 0x3ff,
+		12
+	},
+	{
+		/* VDPU_REG_PRED_FLT1 */ 0x264,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_1_1 */ 0x3ff,
+		2
+	},
+	{
+		/* VDPU_REG_PRED_FLT2 */ 0x268,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_1_2 */ 0x3ff,
+		22
+	},
+	{
+		/* VDPU_REG_PRED_FLT2 */ 0x268,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_1_3 */ 0x3ff,
+		12
+	},
+	{
+		/* VDPU_REG_PRED_FLT2 */ 0x268,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_2_0 */ 0x3ff,
+		2
+	},
+	{
+		/* VDPU_REG_PRED_FLT3 */ 0x26c,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_2_1 */ 0x3ff,
+		22
+	},
+	{
+		/* VDPU_REG_PRED_FLT3 */ 0x26c,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_2_2 */ 0x3ff,
+		12
+	},
+	{
+		/* VDPU_REG_PRED_FLT3 */ 0x26c,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_2_3 */ 0x3ff,
+		2
+	},
+	{
+		/* VDPU_REG_PRED_FLT4 */ 0x270,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_3_0 */ 0x3ff,
+		22
+	},
+	{
+		/* VDPU_REG_PRED_FLT4 */ 0x270,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_3_1 */ 0x3ff,
+		12
+	},
+	{
+		/* VDPU_REG_PRED_FLT4 */ 0x270,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_3_2 */ 0x3ff,
+		2
+	},
+	{
+		/* VDPU_REG_PRED_FLT5 */ 0x274,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_3_3 */ 0x3ff,
+		22
+	},
+	{
+		/* VDPU_REG_PRED_FLT5 */ 0x274,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_4_0 */ 0x3ff,
+		12
+	},
+	{
+		/* VDPU_REG_PRED_FLT5 */ 0x274,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_4_1 */ 0x3ff,
+		2
+	},
+	{
+		/* VDPU_REG_PRED_FLT6 */ 0x278,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_4_2 */ 0x3ff,
+		22
+	},
+	{
+		/* VDPU_REG_PRED_FLT6 */ 0x278,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_4_3 */ 0x3ff,
+		12
+	},
+	{
+		/* VDPU_REG_PRED_FLT6 */ 0x278,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_5_0 */ 0x3ff,
+		2
+	},
+	{ 0, 0, 0 },
+};
+
+u32 rk3288_vp8d_regs_table[][3] = {
+	{ 0, 0, 0 },
+	{
+		/* VDPU_REG_INTERRUPT	*/ 0x004,
+		/* VDPU_REG_INTERRUPT_DEC_IRQ */ 0x01,
+		8
+	},
+	{
+		/* VDPU_REG_INTERRUPT	*/ 0x004,
+		/* VDPU_REG_INTERRUPT_DEC_IRQ_DIS */ 0x01,
+		4
+	},
+	{
+		/* VDPU_REG_INTERRUPT	*/ 0x004,
+		/* VDPU_REG_INTERRUPT_DEC_E */ 0x01,
+		0
+	},
+	{
+		/* VDPU_REG_CONFIG	*/ 0x008,
+		/* VDPU_REG_CONFIG_DEC_TIMEOUT_E */ 0x01,
+		23
+	},
+	{
+		/* VDPU_REG_CONFIG	*/ 0x008,
+		/* VDPU_REG_CONFIG_DEC_STRSWAP32_E */ 0x01,
+		22
+	},
+	{
+		/* VDPU_REG_CONFIG	*/ 0x008,
+		/* VDPU_REG_CONFIG_DEC_STRENDIAN_E */ 0x01,
+		21
+	},
+	{
+		/* VDPU_REG_CONFIG	*/ 0x008,
+		/* VDPU_REG_CONFIG_DEC_INSWAP32_E */ 0x01,
+		20
+	},
+	{
+		/* VDPU_REG_CONFIG	*/ 0x008,
+		/* VDPU_REG_CONFIG_DEC_OUTSWAP32_E */ 0x01,
+		19
+	},
+	{
+		/* VDPU_REG_CONFIG	*/ 0x008,
+		/* VDPU_REG_CONFIG_DEC_CLK_GATE_E */ 0x01,
+		10
+	},
+	{
+		/* VDPU_REG_CONFIG	*/ 0x008,
+		/* VDPU_REG_CONFIG_DEC_IN_ENDIAN */ 0x01,
+		9
+	},
+	{
+		/* VDPU_REG_CONFIG	*/ 0x008,
+		/* VDPU_REG_CONFIG_DEC_OUT_ENDIAN */ 0x01,
+		8
+	},
+	{
+		/* VDPU_REG_CONFIG	*/ 0x008,
+		/* VDPU_REG_CONFIG_DEC_MAX_BURST */ 0x1f,
+		0
+	},
+	{
+		/* VDPU_REG_DEC_CTRL0	*/ 0x00c,
+		/* VDPU_REG_DEC_CTRL0_DEC_MODE */ 0x0f,
+		28
+	},
+	{
+		/* VDPU_REG_DEC_CTRL0	*/ 0x00c,
+		/* VDPU_REG_DEC_CTRL0_SKIP_MODE */ 0x01,
+		26
+	},
+	{
+		/* VDPU_REG_DEC_CTRL0	*/ 0x00c,
+		/* VDPU_REG_DEC_CTRL0_PIC_INTER_E */ 0x01,
+		20
+	},
+	{
+		/* VDPU_REG_DEC_CTRL0	*/ 0x00c,
+		/* VDPU_REG_DEC_CTRL0_FILTERING_DIS */ 0x01,
+		14
+	},
+	{
+		/* VDPU_REG_DEC_CTRL1	*/ 0x010,
+		/* VDPU_REG_DEC_PIC_MB_WIDTH */ 0x1ff,
+		23
+	},
+	{
+		/* VDPU_REG_DEC_CTRL1	*/ 0x010,
+		/* VDPU_REG_DEC_PIC_MB_HEIGHT_P */ 0xff,
+		11
+	},
+	{
+		/* VDPU_REG_DEC_CTRL1	*/ 0x010,
+		/* VDPU_REG_DEC_CTRL1_PIC_MB_W_EXT */ 0x07,
+		3
+	},
+	{
+		/* VDPU_REG_DEC_CTRL1	*/ 0x010,
+		/* VDPU_REG_DEC_CTRL1_PIC_MB_H_EXT */ 0x07,
+		0
+	},
+	{
+		/* VDPU_REG_DEC_CTRL2	*/ 0x014,
+		/* VDPU_REG_DEC_CTRL2_STRM_START_BIT */ 0x3f,
+		26
+	},
+	{
+		/* VDPU_REG_DEC_CTRL2	*/ 0x014,
+		/* VDPU_REG_DEC_CTRL2_STRM1_START_BIT */ 0x3f,
+		18
+	},
+	{
+		/* VDPU_REG_DEC_CTRL2	*/ 0x014,
+		/* VDPU_REG_DEC_CTRL2_BOOLEAN_VALUE */ 0xff,
+		8
+	},
+	{
+		/* VDPU_REG_DEC_CTRL2	*/ 0x014,
+		/* VDPU_REG_DEC_CTRL2_BOOLEAN_RANGE */ 0xff,
+		0
+	},
+	{
+		/* VDPU_REG_DEC_CTRL3	*/ 0x018,
+		/* VDPU_REG_DEC_CTRL3_STREAM_LEN */ 0xffffff,
+		0
+	},
+	{
+		/* VDPU_REG_DEC_CTRL4	*/ 0x01c,
+		/* VDPU_REG_DEC_CTRL4_DCT1_START_BIT */ 0x3f,
+		26
+	},
+	{
+		/* VDPU_REG_DEC_CTRL4	*/ 0x01c,
+		/* VDPU_REG_DEC_CTRL4_DCT2_START_BIT */ 0x3f,
+		20
+	},
+	{
+		/* VDPU_REG_DEC_CTRL4	*/ 0x01c,
+		/* VDPU_REG_DEC_CTRL4_VC1_HEIGHT_EXT */ 0x01,
+		13
+	},
+	{
+		/* VDPU_REG_DEC_CTRL4	*/ 0x01c,
+		/* VDPU_REG_DEC_CTRL4_BILIN_MC_E */ 0x01,
+		12
+	},
+	{
+		/* VDPU_REG_DEC_CTRL7	*/ 0x02c,
+		/* VDPU_REG_DEC_CTRL7_DCT3_START_BIT */ 0x3f,
+		24
+	},
+	{
+		/* VDPU_REG_DEC_CTRL7	*/ 0x02c,
+		/* VDPU_REG_DEC_CTRL7_DCT4_START_BIT */ 0x3f,
+		18
+	},
+	{
+		/* VDPU_REG_DEC_CTRL7	*/ 0x02c,
+		/* VDPU_REG_DEC_CTRL7_DCT5_START_BIT */ 0x3f,
+		12
+	},
+	{
+		/* VDPU_REG_DEC_CTRL7	*/ 0x02c,
+		/* VDPU_REG_DEC_CTRL7_DCT6_START_BIT */ 0x3f,
+		6
+	},
+	{
+		/* VDPU_REG_DEC_CTRL7	*/ 0x02c,
+		/* VDPU_REG_DEC_CTRL7_DCT7_START_BIT */ 0x3f,
+		0
+	},
+	{
+		/* VDPU_REG_ADDR_STR	*/ 0x030,
+		0xffffffff, 0
+	},
+	{
+		/* VDPU_REG_ADDR_DST	*/ 0x034,
+		0xffffffff, 0
+	},
+	{
+		/* VDPU_REG_ADDR_REF(0) */ 0x038,
+		0xffffffff, 0
+	},
+	{
+		/* VDPU_REG_ADDR_REF(4) */ 0x048,
+		/* VDPU_REG_VP8_ADDR_REF2_5_2 */ 0xffffffff,
+		0
+	},
+	{
+		/* VDPU_REG_ADDR_REF(4) */ 0x048,
+		/* VDPU_REG_VP8_GREF_SIGN_BIAS_2 */ 0x01, 1},
+	{
+		/* VDPU_REG_ADDR_REF(4) */ 0x048,
+		/* VDPU_REG_VP8_AREF_SIGN_BIAS_2 */ 0x01, 0},
+	{
+		/* VDPU_REG_ADDR_REF(5) */ 0x04c,
+		/* VDPU_REG_VP8_ADDR_REF2_5_3 */ 0xffffffff,
+		0
+	},
+	{
+		/* VDPU_REG_ADDR_REF(5) */ 0x04c,
+		/* VDPU_REG_VP8_GREF_SIGN_BIAS_3 */ 0x01, 1},
+	{
+		/* VDPU_REG_ADDR_REF(5) */ 0x04c,
+		/* VDPU_REG_VP8_AREF_SIGN_BIAS_3 */ 0x01, 0},
+	{
+		/* VDPU_REG_VP8_DCT_BASE0 */ 0x058,
+		0xffffffff, 0
+	},
+	{
+		/* VDPU_REG_VP8_DCT_BASE1 */ 0x05c,
+		0xffffffff, 0
+	},
+	{
+		/* VDPU_REG_VP8_DCT_BASE2 */ 0x060,
+		0xffffffff, 0
+	},
+	{
+		/* VDPU_REG_VP8_DCT_BASE3 */ 0x064,
+		0xffffffff, 0
+	},
+	{
+		/* VDPU_REG_VP8_DCT_BASE4 */ 0x068,
+		0xffffffff, 0
+	},
+	{
+		/* VDPU_REG_VP8_ADDR_CTRL_PART */ 0x06c,
+		0xffffffff, 0
+	},
+	{
+		/* VDPU_REG_VP8_DCT_BASE6 */ 0x070,
+		0xffffffff, 0
+	},
+	{
+		/* VDPU_REG_REF_PIC(2) */ 0x080,
+		/* VDPU_REG_REF_PIC_LF_LEVEL_0 */ 0x3f,
+		18
+	},
+	{
+		/* VDPU_REG_REF_PIC(2) */ 0x080,
+		/* VDPU_REG_REF_PIC_LF_LEVEL_1 */ 0x3f,
+		12
+	},
+	{
+		/* VDPU_REG_REF_PIC(2) */ 0x080,
+		/* VDPU_REG_REF_PIC_LF_LEVEL_2 */ 0x3f,
+		6
+	},
+	{
+		/* VDPU_REG_REF_PIC(2) */ 0x080,
+		/* VDPU_REG_REF_PIC_LF_LEVEL_3 */ 0x3f,
+		0
+	},
+	{
+		/* VDPU_REG_REF_PIC(0) */ 0x078,
+		/* VDPU_REG_REF_PIC_FILT_TYPE_E */ 0x01,
+		31
+	},
+	{
+		/* VDPU_REG_REF_PIC(0) */ 0x078,
+		/* VDPU_REG_REF_PIC_FILT_SHARPNESS */ 0x07,
+		28
+	},
+	{
+		/* VDPU_REG_REF_PIC(0) */ 0x078,
+		/* VDPU_REG_FILT_MB_ADJ_0 */ 0x7f,
+		21
+	},
+	{
+		/* VDPU_REG_REF_PIC(0) */ 0x078,
+		/* VDPU_REG_FILT_MB_ADJ_1 */ 0x7f,
+		14
+	},
+	{
+		/* VDPU_REG_REF_PIC(0) */ 0x078,
+		/* VDPU_REG_FILT_MB_ADJ_2 */ 0x7f,
+		7
+	},
+	{
+		/* VDPU_REG_REF_PIC(0) */ 0x078,
+		/* VDPU_REG_FILT_MB_ADJ_3 */ 0x7f,
+		0
+	},
+	{
+		/* VDPU_REG_REF_PIC(1) */ 0x07c,
+		/* VDPU_REG_REF_PIC_ADJ_0 */ 0x7f,
+		21
+	},
+	{
+		/* VDPU_REG_REF_PIC(1) */ 0x07c,
+		/* VDPU_REG_REF_PIC_ADJ_1 */ 0x7f,
+		14
+	},
+	{
+		/* VDPU_REG_REF_PIC(1) */ 0x07c,
+		/* VDPU_REG_REF_PIC_ADJ_2 */ 0x7f,
+		7
+	},
+	{
+		/* VDPU_REG_REF_PIC(1) */ 0x07c,
+		/* VDPU_REG_REF_PIC_ADJ_3 */ 0x7f,
+		0
+	},
+	{
+		/* VDPU_REG_REF_PIC(3) */ 0x084,
+		/* VDPU_REG_REF_PIC_QUANT_DELTA_0 */ 0x1f,
+		27
+	},
+	{
+		/* VDPU_REG_REF_PIC(3) */ 0x084,
+		/* VDPU_REG_REF_PIC_QUANT_DELTA_1 */ 0x1f,
+		22
+	},
+	{
+		/* VDPU_REG_REF_PIC(3) */ 0x084,
+		/* VDPU_REG_REF_PIC_QUANT_0 */ 0x7ff,
+		11
+	},
+	{
+		/* VDPU_REG_REF_PIC(3) */ 0x084,
+		/* VDPU_REG_REF_PIC_QUANT_1 */ 0x7ff,
+		0
+	},
+	{
+		/* VDPU_REG_BD_REF_PIC(4) */ 0x0b8,
+		/* VDPU_REG_REF_PIC_QUANT_DELTA_2 */ 0x1f,
+		27
+	},
+	{
+		/* VDPU_REG_BD_REF_PIC(4) */ 0x0b8,
+		/* VDPU_REG_REF_PIC_QUANT_DELTA_3 */ 0x1f,
+		22
+	},
+	{
+		/* VDPU_REG_BD_REF_PIC(4) */ 0x0b8,
+		/* VDPU_REG_REF_PIC_QUANT_2 */ 0x7ff,
+		11
+	},
+	{
+		/* VDPU_REG_BD_REF_PIC(4) */ 0x0b8,
+		/* VDPU_REG_REF_PIC_QUANT_3 */ 0x7ff,
+		0
+	},
+	{
+		/* VDPU_REG_VP8_QUANTER2 */ 0x0bc,
+		/* VDPU_REG_REF_PIC_QUANT_DELTA_4 */ 0x1f,
+		27
+	},
+	{
+		/* VDPU_REG_VP8_QUANTER2 */ 0x0bc,
+		/* VDPU_REG_REF_PIC_QUANT_4 */ 0x7ff,
+		11
+	},
+	{
+		/* VDPU_REG_VP8_QUANTER2 */ 0x0bc,
+		/* VDPU_REG_REF_PIC_QUANT_5 */ 0x7ff,
+		0
+	},
+	{
+		/* VDPU_REG_FWD_PIC(0) */ 0x028,
+		/* VDPU_REG_FWD_PIC1_SEGMENT_BASE */ 0xffffffff,
+		0
+	},
+	{
+		/* VDPU_REG_FWD_PIC(0) */ 0x028,
+		/* VDPU_REG_FWD_PIC1_SEGMENT_UPD_E */ 0x01,
+		1
+	},
+	{
+		/* VDPU_REG_FWD_PIC(0) */ 0x028,
+		/* VDPU_REG_FWD_PIC1_SEGMENT_E */ 0x01,
+		0
+	},
+	{
+		/* VDPU_REG_PRED_FLT */ 0x0c4,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_0_0 */ 0x3ff,
+		22
+	},
+	{
+		/* VDPU_REG_PRED_FLT */ 0x0c4,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_0_1 */ 0x3ff,
+		12
+	},
+	{
+		/* VDPU_REG_PRED_FLT */ 0x0c4,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_0_2 */ 0x3ff,
+		2
+	},
+	{
+		/* VDPU_REG_REF_PIC(4) */ 0x088,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_0_3 */ 0x3ff,
+		22
+	},
+	{
+		/* VDPU_REG_REF_PIC(4) */ 0x088,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_1_0 */ 0x3ff,
+		12
+	},
+	{
+		/* VDPU_REG_REF_PIC(4) */ 0x088,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_1_1 */ 0x3ff,
+		2
+	},
+	{
+		/* VDPU_REG_REF_PIC(5) */ 0x08c,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_1_2 */ 0x3ff,
+		22
+	},
+	{
+		/* VDPU_REG_REF_PIC(5) */ 0x08c,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_1_3 */ 0x3ff,
+		12
+	},
+	{
+		/* VDPU_REG_REF_PIC(5) */ 0x08c,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_2_0 */ 0x3ff,
+		2
+	},
+	{
+		/* VDPU_REG_REF_PIC(6) */ 0x090,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_2_1 */ 0x3ff,
+		22
+	},
+	{
+		/* VDPU_REG_REF_PIC(6) */ 0x090,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_2_2 */ 0x3ff,
+		12
+	},
+	{
+		/* VDPU_REG_REF_PIC(6) */ 0x090,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_2_3 */ 0x3ff,
+		2
+	},
+	{
+		/* VDPU_REG_REF_PIC(7) */ 0x094,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_3_0 */ 0x3ff,
+		22
+	},
+	{
+		/* VDPU_REG_REF_PIC(7) */ 0x094,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_3_1 */ 0x3ff,
+		12
+	},
+	{
+		/* VDPU_REG_REF_PIC(7) */ 0x094,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_3_2 */ 0x3ff,
+		2
+	},
+	{
+		/* VDPU_REG_REF_PIC(8) */ 0x098,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_3_3 */ 0x3ff,
+		22
+	},
+	{
+		/* VDPU_REG_REF_PIC(8) */ 0x098,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_4_0 */ 0x3ff,
+		12
+	},
+	{
+		/* VDPU_REG_REF_PIC(8) */ 0x098,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_4_1 */ 0x3ff,
+		2
+	},
+	{
+		/* VDPU_REG_REF_PIC(9) */ 0x09c,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_4_2 */ 0x3ff,
+		22
+	},
+	{
+		/* VDPU_REG_REF_PIC(9) */ 0x09c,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_4_3 */ 0x3ff,
+		12
+	},
+	{
+		/* VDPU_REG_REF_PIC(9) */ 0x09c,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_5_0 */ 0x3ff,
+		2
+	},
+	{
+		/* VDPU_REG_BD_REF_PIC(0) */ 0x0a8,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_5_1 */ 0x3ff,
+		22
+	},
+	{
+		/* VDPU_REG_BD_REF_PIC(0) */ 0x0a8,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_5_2 */ 0x3ff,
+		12
+	},
+	{
+		/* VDPU_REG_BD_REF_PIC(0) */ 0x0a8,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_5_3 */ 0x3ff,
+		2
+	},
+	{
+		/* VDPU_REG_BD_REF_PIC(1) */ 0x0ac,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_6_0 */ 0x3ff,
+		22
+	},
+	{
+		/* VDPU_REG_BD_REF_PIC(1) */ 0x0ac,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_6_1 */ 0x3ff,
+		12
+	},
+	{
+		/* VDPU_REG_BD_REF_PIC(1) */ 0x0ac,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_6_2 */ 0x3ff,
+		2
+	},
+	{
+		/* VDPU_REG_BD_REF_PIC(2) */ 0x0b0,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_6_3 */ 0x3ff,
+		22
+	},
+	{
+		/* VDPU_REG_BD_REF_PIC(2) */ 0x0b0,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_7_0 */ 0x3ff,
+		12
+	},
+	{
+		/* VDPU_REG_BD_REF_PIC(2) */ 0x0b0,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_7_1 */ 0x3ff,
+		2
+	},
+	{
+		/* VDPU_REG_BD_REF_PIC(3) */ 0x0b4,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_7_2 */ 0x3ff,
+		22
+	},
+	{
+		/* VDPU_REG_BD_REF_PIC(3) */ 0x0b4,
+		/* VDPU_REG_PRED_FLT_PRED_BC_TAP_7_3 */ 0x3ff,
+		12
+	},
+	{
+		/* VDPU_REG_BD_REF_PIC(3) */ 0x0b4,
+		/* VDPU_REG_BD_REF_PIC_PRED_TAP_2_M1 */ 0x03,
+		10
+	},
+	{
+		/* VDPU_REG_BD_REF_PIC(3) */ 0x0b4,
+		/* VDPU_REG_BD_REF_PIC_PRED_TAP_2_4 */ 0x03,
+		8
+	},
+	{
+		/* VDPU_REG_BD_REF_PIC(3) */ 0x0b4,
+		/* VDPU_REG_BD_REF_PIC_PRED_TAP_4_M1 */ 0x03,
+		6
+	},
+	{
+		/* VDPU_REG_BD_REF_PIC(3) */ 0x0b4,
+		/* VDPU_REG_BD_REF_PIC_PRED_TAP_4_4 */ 0x03,
+		4
+	},
+	{
+		/* VDPU_REG_BD_REF_PIC(3) */ 0x0b4,
+		/* VDPU_REG_BD_REF_PIC_PRED_TAP_6_M1 */ 0x03,
+		2
+	},
+	{
+		/* VDPU_REG_BD_REF_PIC(3) */ 0x0b4,
+		/* VDPU_REG_BD_REF_PIC_PRED_TAP_6_4 */ 0x03,
+		0
+	},
+	{
+		/* VDPU_REG_DEC_CTRL6 */ 0x024,
+		/* VDPU_REG_DEC_CTRL6_STREAM1_LEN */ 0xffffff,
+		0
+	},
+	{
+		/* VDPU_REG_DEC_CTRL6 */ 0x024,
+		/* VDPU_REG_DEC_CTRL6_COEFFS_PART_AM */ 0x0f,
+		24
+	},
+	{
+		/* VDPU_REG_ADDR_QTABLE */ 0x0a0,
+		0xffffffff, 0
+	},
+	{ 0, 0, 0 },
+};
+
+u32 rk3229_regs_map[VDPU_REG_LAST + 1] = {
+	58, 59, 49, 13, 16, 14, 15, 18,
+	17, 32, 12, 6, 7, 69, 71, 73,
+	74, 82, 81, 77, 78, 102, 103, 104,
+	105, 112, 111, 113, 114, 115, 116, 117,
+	118, 119, 120, 108, 109, 145, 146, 106,
+	107, 143, 144, 147, 132, 80, 84, 83,
+	11, 68, 127, 128, 129, 130, 131, 132,
+	133, 79, 75, 76, 138, 139, 140, 141,
+	142, 0, 63, 64, 65, 150, 0, 0,
+	151, 152, 153, 154, 0, 96, 155, 156,
+	157, 158, 97, 0, 159, 160, 161, 162,
+	0, 98, 163, 164, 165, 166, 99, 0,
+	167, 85, 86, 87, 0, 100, 88, 89,
+	90, 91, 101, 0, 92, 93, 94, 95,
+	0, 110, 121, 122, 124, 126, 66, 135,
+	137, 136, 67, 28, 27, 0, 62, 0,
+};
+
+u32 rk3288_regs_map[VDPU_REG_LAST + 1] = {
+	4, 9, 15, 6, 7, 5, 8, 10,
+	11, 12, 13, 14, 16, 17, 18, 19,
+	20, 24, 23, 28, 29, 51, 52, 53,
+	54, 56, 55, 57, 58, 59, 60, 61,
+	62, 63, 64, 67, 68, 71, 72, 65,
+	66, 69, 70, 73, 49, 22, 117, 118,
+	25, 35, 44, 45, 46, 47, 48, 49,
+	50, 21, 26, 27, 30, 31, 32, 33,
+	34, 0, 79, 80, 81, 82, 0, 0,
+	83, 84, 85, 86, 0, 111, 87, 88,
+	89, 90, 112, 0, 91, 92, 93, 94,
+	0, 113, 95, 96, 97, 98, 114, 0,
+	99, 100, 101, 102, 0, 115, 103, 104,
+	105, 106, 116, 0, 107, 108, 109, 110,
+	0, 37, 38, 39, 41, 43, 119, 76,
+	78, 77, 36, 1, 2, 0, 3, 0,
+};
+
+#endif
diff --git a/drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.c b/drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.c
index 33e9a89..916f549 100644
--- a/drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.c
+++ b/drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.c
@@ -93,7 +93,16 @@ enum {
 	ROCKCHIP_VPU_DEC_CTRL_VP8_FRAME_HDR,
 };
 
-static struct rockchip_vpu_control controls[0];
+static struct rockchip_vpu_control controls[] = {
+	[ROCKCHIP_VPU_DEC_CTRL_VP8_FRAME_HDR] = {
+		.id = V4L2_CID_MPEG_VIDEO_VP8_FRAME_HDR,
+		.type = V4L2_CTRL_TYPE_PRIVATE,
+		.name = "VP8 Frame Header Parameters",
+		.max_reqs = VIDEO_MAX_FRAME,
+		.elem_size = sizeof(struct v4l2_ctrl_vp8_frame_hdr),
+		.can_store = true,
+	},
+};
 
 static inline const void *get_ctrl_ptr(struct rockchip_vpu_ctx *ctx,
 				       unsigned id)
@@ -954,6 +963,10 @@ static void rockchip_vpu_dec_prepare_run(struct rockchip_vpu_ctx *ctx)
 
 	v4l2_ctrl_apply_request(&ctx->ctrl_handler, src->request);
 
+	if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_VP8_FRAME) {
+		ctx->run.vp8d.frame_hdr = get_ctrl_ptr(ctx,
+				ROCKCHIP_VPU_DEC_CTRL_VP8_FRAME_HDR);
+	}
 }
 
 static void rockchip_vpu_dec_run_done(struct rockchip_vpu_ctx *ctx,
diff --git a/drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.c b/drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.c
index dc7abc7..f4aa866 100644
--- a/drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.c
+++ b/drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.c
@@ -254,7 +254,24 @@ void rockchip_vpu_hw_remove(struct rockchip_vpu_dev *vpu)
 	clk_disable_unprepare(vpu->aclk);
 }
 
-static const struct rockchip_vpu_codec_ops mode_ops[0];
+static const struct rockchip_vpu_codec_ops mode_ops[] = {
+	[RK3288_VPU_CODEC_VP8D] = {
+		.init = rockchip_vpu_vp8d_init,
+		.exit = rockchip_vpu_vp8d_exit,
+		.irq = rockchip_vdpu_irq,
+		.run = rockchip_vpu_vp8d_run,
+		.done = rockchip_vpu_run_done,
+		.reset = rockchip_vpu_dec_reset,
+	},
+	[RK3229_VPU_CODEC_VP8D] = {
+		.init = rockchip_vpu_vp8d_init,
+		.exit = rockchip_vpu_vp8d_exit,
+		.irq = rockchip_vdpu_irq,
+		.run = rockchip_vpu_vp8d_run,
+		.done = rockchip_vpu_run_done,
+		.reset = rockchip_vpu_dec_reset,
+	},
+};
 
 void rockchip_vpu_run(struct rockchip_vpu_ctx *ctx)
 {
diff --git a/drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.h b/drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.h
index 975357da..675a0eb 100644
--- a/drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.h
+++ b/drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.h
@@ -56,6 +56,16 @@ struct rockchip_vpu_aux_buf {
 };
 
 /**
+ * struct rockchip_vpu_vp8d_hw_ctx - Context private data of VP8 decoder.
+ * @segment_map:	Segment map buffer.
+ * @prob_tbl:		Probability table buffer.
+ */
+struct rockchip_vpu_vp8d_hw_ctx {
+	struct rockchip_vpu_aux_buf segment_map;
+	struct rockchip_vpu_aux_buf prob_tbl;
+};
+
+/**
  * struct rockchip_vpu_hw_ctx - Context private data of hardware code.
  * @codec_ops:		Set of operations associated with current codec mode.
  */
@@ -63,6 +73,10 @@ struct rockchip_vpu_hw_ctx {
 	const struct rockchip_vpu_codec_ops *codec_ops;
 
 	/* Specific for particular codec modes. */
+	union {
+		struct rockchip_vpu_vp8d_hw_ctx vp8d;
+		/* Other modes will need different data. */
+	};
 };
 
 int rockchip_vpu_hw_probe(struct rockchip_vpu_dev *vpu);
@@ -75,4 +89,12 @@ void rockchip_vpu_run(struct rockchip_vpu_ctx *ctx);
 
 void rockchip_vpu_power_on(struct rockchip_vpu_dev *vpu);
 
+/* for vp8 decoder */
+int rockchip_vdpu_irq(int irq, struct rockchip_vpu_dev *vpu);
+void rockchip_vpu_dec_reset(struct rockchip_vpu_ctx *ctx);
+
+int rockchip_vpu_vp8d_init(struct rockchip_vpu_ctx *ctx);
+void rockchip_vpu_vp8d_exit(struct rockchip_vpu_ctx *ctx);
+void rockchip_vpu_vp8d_run(struct rockchip_vpu_ctx *ctx);
+
 #endif /* RK3288_VPU_HW_H_ */
-- 
1.9.1



From caesar.upstream at gmail.com  Tue Jan 26 01:09:44 2016
From: caesar.upstream at gmail.com (Caesar Wang)
Date: Tue, 26 Jan 2016 17:09:44 +0800
Subject: [PATCH v7 0/2] Add Rockchip Inno-HDMI driver
In-Reply-To: <1453777142-30119-1-git-send-email-ykk@rock-chips.com>
References: <1453777142-30119-1-git-send-email-ykk@rock-chips.com>
Message-ID: <56A737D8.7090508@gmail.com>

Hi,

? 2016?01?26? 10:59, Yakir Yang ??:
> Here are a brief introduction to Innosilicon HDMI IP:
>    - Support HDMI 1.4a, HDCP 1.2 and DVI 1.0 standard compliant transmitter
>    - Support HDMI1.4 a/b 3D function defined in HDMI 1.4 a/b spec
>    - Digital video interface supports a pixel size of 24, 30, 36, 48bits color depth in RGB
>    - S/PDIF output supports PCM, Dolby Digital, DTS digital audio transmission
>      (32-192kHz Fs) using IEC60958 and IEC 61937
>    - The EDID and CEC function are also supported by Innosilicon HDMI Transmitter Controlle
>
> Changes in v7:
> - Correct the module licnese statement (Paul)
>       - MODULE_LICENSE("GPL");
>       + MODULE_LICENSE("GPL v2");
> - Start indentation with tabs and fix the misspell in Kconfig (Paul)
> - Carry the lost device-binding document (Heiko)
>
> Changes in v6:
> - Rebase the Makefile/Kconfig files which add by Chris's rockchip-mipi driver (Caeser)
>
> Changes in v5:
> - Use hdmi_infoframe helper functions to packed the infoframe (Russell)
> - Remove the unused double wait_for_completion_timeout for ddc transfer (Russell)
> - Remove the unused local variable in "inno_hdmi_i2c_write()" function (Russell)
>
> Changes in v4:
> - Modify the commit title "drm/rockchip: hdmi: ..." (Mark)
> - Correct the "DKMS" to "DPMS" (Mark)
> - Fix over 80 characters problems (Mark)
> - Remove encoder .prepare/.commit helper functions, and move the vop mode
> configure function into encoder .enable helper functions. (Mark)
>
> Changes in v3:
> - Use encoder enable/disable function, and remove the encoder DPMS function
> - Keep HDMI PLL power on in standby mode
>
> Changes in v2:
> - Using DRM atomic helper functions for connector init (Mark)
> - Remove "hdmi->connector.encoder = encoder;" (Mark)
> - Add the Acked-by tags from Rob
> - Correct the misspell "rk3036-dw-hdmi" (Heiko)
>
> Yakir Yang (2):
>    drm/rockchip: hdmi: add Innosilicon HDMI support
>    dt-bindings: add document for Innosilicon HDMI on Rockchip platform
>
>   .../display/rockchip/inno_hdmi-rockchip.txt        |  50 ++
>   drivers/gpu/drm/rockchip/Kconfig                   |   8 +
>   drivers/gpu/drm/rockchip/Makefile                  |   1 +
>   drivers/gpu/drm/rockchip/inno_hdmi.c               | 941 +++++++++++++++++++++
>   drivers/gpu/drm/rockchip/inno_hdmi.h               | 362 ++++++++
>   5 files changed, 1362 insertions(+)
>   create mode 100644 Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt
>   create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.c
>   create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.h
The inno hdmi driver is verified by on my work branch.
(https://github.com/Caesar-github/rockchip/commits/kylin/next)

929605e arm: config: support the inno hdmi for rk3036 SoCs
aac426e FROMLIST: ARM: dts: rockchip: add hdmi/vop device node for rk3036
7d96943 FROMLIST: dt-bindings: add document for Innosilicon HDMI on 
Rockchip platform
f7751f2 FROMLIST: drm/rockchip: hdmi: add Innosilicon HDMI support
....


>


-- 
Thanks,
Caesar



From sjoerd.simons at collabora.co.uk  Tue Jan 26 01:12:52 2016
From: sjoerd.simons at collabora.co.uk (Sjoerd Simons)
Date: Tue, 26 Jan 2016 10:12:52 +0100
Subject: [PATCH] brcmfmac: sdio: Increase the default timeouts a bit
In-Reply-To: <56A68064.5020702@broadcom.com>
References: <1453718849-3508-1-git-send-email-sjoerd.simons@collabora.co.uk>
 <56A64114.3010400@gmail.com>
 <CAD=FV=X-ejMLnh30TofU7dKP5WwZaXcgLaQwFs__wo9wHsv53Q@mail.gmail.com>
 <56A68064.5020702@broadcom.com>
Message-ID: <1453799572.30130.51.camel@collabora.co.uk>

On Mon, 2016-01-25 at 21:07 +0100, Arend van Spriel wrote:
> On 25-1-2016 20:23, Doug Anderson wrote:
> > Kinda curious: do we get one or two really slow responses on every
> > bootup, or just some bootups???Do we ever succeed even with a slow
> > (like 1.8 or 1.9 seconds) response, or is it always either "fast"
> > or
> > "2.1" seconds?
> 
> Now these are interesting questions that I should have spilled out in
> the first place. Thanks.

I would need to get a more data to really conclusively say, but what i
saw in testing thusfar was:

* The issue occurs on _some_ boots
* The issue seems to come up more often on warm reboots
* The likelyhood seemed to change depending on the kernel
? configuration (iotw when the initilisation order/timing changed?)
* Forcing the brcmfmac module to load later in boot seemed to make the?
? likelyhood of the issue showing up lower.
* The samples I have with traces showed either quick responses on?
? success or slow "2.1" seconds responses on failure.

I'll try to make some time to get some statistics over a series of
boots wrt. the response time(s) for control messages so we can get a
better idea how often this happens and its behaviour.


I guess one other thing to note is that both Doug and myself were
seeing this on Rockchip RK3288 based devices. It would ofcourse be
interesting to know if this has been seen on other types of devices..

--?
Sjoerd Simons
Collabora Ltd.


From lkp at intel.com  Tue Jan 26 01:16:50 2016
From: lkp at intel.com (kbuild test robot)
Date: Tue, 26 Jan 2016 17:16:50 +0800
Subject: [PATCH v1 1/3] media: v4l: Add VP8 format support in V4L2
 framework
In-Reply-To: <1453799046-307-2-git-send-email-jung.zhao@rock-chips.com>
Message-ID: <201601261736.Z1YOYKHt%fengguang.wu@intel.com>

Hi zhaojun,

[auto build test ERROR on rockchip/for-next]
[also build test ERROR on v4.5-rc1 next-20160125]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]

url:    https://github.com/0day-ci/linux/commits/Jung-Zhao/Add-VP8-deocder-for-rk3229-rk3288/20160126-170940
base:   https://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git for-next
config: x86_64-randconfig-x012-01260845 (attached as .config)
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All errors (new ones prefixed by >>):

   drivers/media/v4l2-core/v4l2-ctrls.c: In function 'v4l2_ctrl_new':
>> drivers/media/v4l2-core/v4l2-ctrls.c:1994:28: error: 'V4L2_CTRL_FLAG_REQ_KEEP' undeclared (first use in this function)
     if ((is_array || (flags & V4L2_CTRL_FLAG_REQ_KEEP)) &&
                               ^
   drivers/media/v4l2-core/v4l2-ctrls.c:1994:28: note: each undeclared identifier is reported only once for each function it appears in

vim +/V4L2_CTRL_FLAG_REQ_KEEP +1994 drivers/media/v4l2-core/v4l2-ctrls.c

  1988			return NULL;
  1989		}
  1990		if (type == V4L2_CTRL_TYPE_BITMASK && ((def & ~max) || min || step)) {
  1991			handler_set_err(hdl, -ERANGE);
  1992			return NULL;
  1993		}
> 1994		if ((is_array || (flags & V4L2_CTRL_FLAG_REQ_KEEP)) &&
  1995		    (type == V4L2_CTRL_TYPE_BUTTON ||
  1996		     type == V4L2_CTRL_TYPE_CTRL_CLASS)) {
  1997			handler_set_err(hdl, -EINVAL);

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
-------------- next part --------------
A non-text attachment was scrubbed...
Name: .config.gz
Type: application/octet-stream
Size: 26697 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160126/2521820c/attachment-0001.obj>

From heiko at sntech.de  Tue Jan 26 01:29:37 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Tue, 26 Jan 2016 10:29:37 +0100
Subject: [PATCH v1 1/3] media: v4l: Add VP8 format support in V4L2
 framework
In-Reply-To: <1453799046-307-2-git-send-email-jung.zhao@rock-chips.com>
References: <1453799046-307-1-git-send-email-jung.zhao@rock-chips.com>
 <1453799046-307-2-git-send-email-jung.zhao@rock-chips.com>
Message-ID: <111853624.RJBieTVshh@diego>

Hi,

Am Dienstag, 26. Januar 2016, 17:04:04 schrieb Jung Zhao:
> From: zhaojun <jung.zhao at rock-chips.com>
> 
> Signed-off-by: zhaojun <jung.zhao at rock-chips.com>

please fix your git settings regarding your name :-)
	git config user.name "Jung Zhao"
should do the correct thing (maybe even add the --global option)


Thanks
Heiko

> ---
> 
>  drivers/media/v4l2-core/v4l2-ctrls.c           | 17 ++++-
>  drivers/media/v4l2-core/v4l2-ioctl.c           |  3 +
>  drivers/media/v4l2-core/videobuf2-dma-contig.c | 51 +++++++++-----
>  include/media/v4l2-ctrls.h                     |  2 +
>  include/media/videobuf2-dma-contig.h           | 11 ++-
>  include/uapi/linux/v4l2-controls.h             | 98
> ++++++++++++++++++++++++++ include/uapi/linux/videodev2.h                 |
>  5 ++
>  7 files changed, 166 insertions(+), 21 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c
> b/drivers/media/v4l2-core/v4l2-ctrls.c index 890520d..22821e94 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls.c
> @@ -761,7 +761,7 @@ const char *v4l2_ctrl_get_name(u32 id)
>  	case V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP:		return "VPX I-Frame QP Value";
>  	case V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP:		return "VPX P-Frame QP 
Value";
>  	case V4L2_CID_MPEG_VIDEO_VPX_PROFILE:			return "VPX Profile";
> -
> +	case V4L2_CID_MPEG_VIDEO_VP8_FRAME_HDR:			return "VP8 Frame Header";
>  	/* CAMERA controls */
>  	/* Keep the order of the 'case's the same as in v4l2-controls.h! */
>  	case V4L2_CID_CAMERA_CLASS:		return "Camera Controls";
> @@ -1126,6 +1126,9 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum
> v4l2_ctrl_type *type, case V4L2_CID_RDS_TX_ALT_FREQS:
>  		*type = V4L2_CTRL_TYPE_U32;
>  		break;
> +	case V4L2_CID_MPEG_VIDEO_VP8_FRAME_HDR:
> +		*type = V4L2_CTRL_TYPE_VP8_FRAME_HDR;
> +		break;
>  	default:
>  		*type = V4L2_CTRL_TYPE_INTEGER;
>  		break;
> @@ -1525,6 +1528,13 @@ static int std_validate(const struct v4l2_ctrl *ctrl,
> u32 idx, return -ERANGE;
>  		return 0;
> 
> +	/* FIXME:just return 0 for now */
> +	case V4L2_CTRL_TYPE_PRIVATE:
> +		return 0;
> +
> +	case V4L2_CTRL_TYPE_VP8_FRAME_HDR:
> +		return 0;
> +
>  	default:
>  		return -EINVAL;
>  	}
> @@ -2074,6 +2084,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct
> v4l2_ctrl_handler *hdl, case V4L2_CTRL_TYPE_U32:
>  		elem_size = sizeof(u32);
>  		break;
> +	case V4L2_CTRL_TYPE_VP8_FRAME_HDR:
> +		elem_size = sizeof(struct v4l2_ctrl_vp8_frame_hdr);
> +		break;
>  	default:
>  		if (type < V4L2_CTRL_COMPOUND_TYPES)
>  			elem_size = sizeof(s32);
> @@ -2098,7 +2111,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct
> v4l2_ctrl_handler *hdl, handler_set_err(hdl, -ERANGE);
>  		return NULL;
>  	}
> -	if (is_array &&
> +	if ((is_array || (flags & V4L2_CTRL_FLAG_REQ_KEEP)) &&
>  	    (type == V4L2_CTRL_TYPE_BUTTON ||
>  	     type == V4L2_CTRL_TYPE_CTRL_CLASS)) {
>  		handler_set_err(hdl, -EINVAL);
> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c
> b/drivers/media/v4l2-core/v4l2-ioctl.c index 7d028d1..8aa5812 100644
> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> @@ -1259,6 +1259,9 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
> case V4L2_PIX_FMT_VC1_ANNEX_G:	descr = "VC-1 (SMPTE 412M Annex G)"; break;
> case V4L2_PIX_FMT_VC1_ANNEX_L:	descr = "VC-1 (SMPTE 412M Annex L)"; break;
> case V4L2_PIX_FMT_VP8:		descr = "VP8"; break;
> +		case V4L2_PIX_FMT_VP8_FRAME:
> +			descr = "VP8 FRAME";
> +			break;
>  		case V4L2_PIX_FMT_CPIA1:	descr = "GSPCA CPiA YUV"; break;
>  		case V4L2_PIX_FMT_WNVA:		descr = "WNVA"; break;
>  		case V4L2_PIX_FMT_SN9C10X:	descr = "GSPCA SN9C10X"; break;
> diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c
> b/drivers/media/v4l2-core/videobuf2-dma-contig.c index c331272..aebcc7f
> 100644
> --- a/drivers/media/v4l2-core/videobuf2-dma-contig.c
> +++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c
> @@ -23,13 +23,16 @@
> 
>  struct vb2_dc_conf {
>  	struct device		*dev;
> +	struct dma_attrs	attrs;
>  };
> 
>  struct vb2_dc_buf {
>  	struct device			*dev;
>  	void				*vaddr;
>  	unsigned long			size;
> +	void				*cookie;
>  	dma_addr_t			dma_addr;
> +	struct dma_attrs		attrs;
>  	enum dma_data_direction		dma_dir;
>  	struct sg_table			*dma_sgt;
>  	struct frame_vector		*vec;
> @@ -131,7 +134,8 @@ static void vb2_dc_put(void *buf_priv)
>  		sg_free_table(buf->sgt_base);
>  		kfree(buf->sgt_base);
>  	}
> -	dma_free_coherent(buf->dev, buf->size, buf->vaddr, buf->dma_addr);
> +	dma_free_attrs(buf->dev, buf->size, buf->cookie, buf->dma_addr,
> +			&buf->attrs);
>  	put_device(buf->dev);
>  	kfree(buf);
>  }
> @@ -143,18 +147,22 @@ static void *vb2_dc_alloc(void *alloc_ctx, unsigned
> long size, struct device *dev = conf->dev;
>  	struct vb2_dc_buf *buf;
> 
> -	buf = kzalloc(sizeof *buf, GFP_KERNEL);
> +	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
>  	if (!buf)
>  		return ERR_PTR(-ENOMEM);
> 
> -	buf->vaddr = dma_alloc_coherent(dev, size, &buf->dma_addr,
> -						GFP_KERNEL | gfp_flags);
> -	if (!buf->vaddr) {
> +	buf->attrs = conf->attrs;
> +	buf->cookie = dma_alloc_attrs(dev, size, &buf->dma_addr,
> +					GFP_KERNEL | gfp_flags, &buf->attrs);
> +	if (!buf->cookie) {
>  		dev_err(dev, "dma_alloc_coherent of size %ld failed\n", size);
>  		kfree(buf);
>  		return ERR_PTR(-ENOMEM);
>  	}
> 
> +	if (!dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, &buf->attrs))
> +		buf->vaddr = buf->cookie;
> +
>  	/* Prevent the device from being released while the buffer is used */
>  	buf->dev = get_device(dev);
>  	buf->size = size;
> @@ -185,8 +193,8 @@ static int vb2_dc_mmap(void *buf_priv, struct
> vm_area_struct *vma) */
>  	vma->vm_pgoff = 0;
> 
> -	ret = dma_mmap_coherent(buf->dev, vma, buf->vaddr,
> -		buf->dma_addr, buf->size);
> +	ret = dma_mmap_attrs(buf->dev, vma, buf->cookie,
> +		buf->dma_addr, buf->size, &buf->attrs);
> 
>  	if (ret) {
>  		pr_err("Remapping memory failed, error: %d\n", ret);
> @@ -329,7 +337,7 @@ static void *vb2_dc_dmabuf_ops_kmap(struct dma_buf
> *dbuf, unsigned long pgnum) {
>  	struct vb2_dc_buf *buf = dbuf->priv;
> 
> -	return buf->vaddr + pgnum * PAGE_SIZE;
> +	return buf->vaddr ? buf->vaddr + pgnum * PAGE_SIZE : NULL;
>  }
> 
>  static void *vb2_dc_dmabuf_ops_vmap(struct dma_buf *dbuf)
> @@ -368,8 +376,8 @@ static struct sg_table *vb2_dc_get_base_sgt(struct
> vb2_dc_buf *buf) return NULL;
>  	}
> 
> -	ret = dma_get_sgtable(buf->dev, sgt, buf->vaddr, buf->dma_addr,
> -		buf->size);
> +	ret = dma_get_sgtable_attrs(buf->dev, sgt, buf->cookie, buf->dma_addr,
> +		buf->size, &buf->attrs);
>  	if (ret < 0) {
>  		dev_err(buf->dev, "failed to get scatterlist from DMA API\n");
>  		kfree(sgt);
> @@ -448,22 +456,26 @@ static void vb2_dc_put_userptr(void *buf_priv)
>   */
> 
>  #ifdef __arch_pfn_to_dma
> -static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned
> long pfn) +static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev,
> +					   unsigned long pfn)
>  {
>  	return (dma_addr_t)__arch_pfn_to_dma(dev, pfn);
>  }
>  #elif defined(__pfn_to_bus)
> -static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned
> long pfn) +static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev,
> +					   unsigned long pfn)
>  {
>  	return (dma_addr_t)__pfn_to_bus(pfn);
>  }
>  #elif defined(__pfn_to_phys)
> -static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned
> long pfn) +static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev,
> +					   unsigned long pfn)
>  {
>  	return (dma_addr_t)__pfn_to_phys(pfn);
>  }
>  #else
> -static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned
> long pfn) +static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev,
> +					   unsigned long pfn)
>  {
>  	/* really, we cannot do anything better at this point */
>  	return (dma_addr_t)(pfn) << PAGE_SHIFT;
> @@ -497,7 +509,7 @@ static void *vb2_dc_get_userptr(void *alloc_ctx,
> unsigned long vaddr, return ERR_PTR(-EINVAL);
>  	}
> 
> -	buf = kzalloc(sizeof *buf, GFP_KERNEL);
> +	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
>  	if (!buf)
>  		return ERR_PTR(-ENOMEM);
> 
> @@ -721,19 +733,22 @@ const struct vb2_mem_ops vb2_dma_contig_memops = {
>  };
>  EXPORT_SYMBOL_GPL(vb2_dma_contig_memops);
> 
> -void *vb2_dma_contig_init_ctx(struct device *dev)
> +void *vb2_dma_contig_init_ctx_attrs(struct device *dev,
> +				    struct dma_attrs *attrs)
>  {
>  	struct vb2_dc_conf *conf;
> 
> -	conf = kzalloc(sizeof *conf, GFP_KERNEL);
> +	conf = kzalloc(sizeof(*conf), GFP_KERNEL);
>  	if (!conf)
>  		return ERR_PTR(-ENOMEM);
> 
>  	conf->dev = dev;
> +	if (attrs)
> +		conf->attrs = *attrs;
> 
>  	return conf;
>  }
> -EXPORT_SYMBOL_GPL(vb2_dma_contig_init_ctx);
> +EXPORT_SYMBOL_GPL(vb2_dma_contig_init_ctx_attrs);
> 
>  void vb2_dma_contig_cleanup_ctx(void *alloc_ctx)
>  {
> diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
> index 5f9526f..0424cdc 100644
> --- a/include/media/v4l2-ctrls.h
> +++ b/include/media/v4l2-ctrls.h
> @@ -46,6 +46,7 @@ struct poll_table_struct;
>   * @p_u16:	Pointer to a 16-bit unsigned value.
>   * @p_u32:	Pointer to a 32-bit unsigned value.
>   * @p_char:	Pointer to a string.
> + * @p_vp8_frame_hdr:	Pointer to a struct v4l2_ctrl_vp8_frame_hdr.
>   * @p:		Pointer to a compound value.
>   */
>  union v4l2_ctrl_ptr {
> @@ -55,6 +56,7 @@ union v4l2_ctrl_ptr {
>  	u16 *p_u16;
>  	u32 *p_u32;
>  	char *p_char;
> +	struct v4l2_ctrl_vp8_frame_hdr *p_vp8_frame_hdr;
>  	void *p;
>  };
> 
> diff --git a/include/media/videobuf2-dma-contig.h
> b/include/media/videobuf2-dma-contig.h index c33dfa6..2087c9a 100644
> --- a/include/media/videobuf2-dma-contig.h
> +++ b/include/media/videobuf2-dma-contig.h
> @@ -16,6 +16,8 @@
>  #include <media/videobuf2-v4l2.h>
>  #include <linux/dma-mapping.h>
> 
> +struct dma_attrs;
> +
>  static inline dma_addr_t
>  vb2_dma_contig_plane_dma_addr(struct vb2_buffer *vb, unsigned int plane_no)
> {
> @@ -24,7 +26,14 @@ vb2_dma_contig_plane_dma_addr(struct vb2_buffer *vb,
> unsigned int plane_no) return *addr;
>  }
> 
> -void *vb2_dma_contig_init_ctx(struct device *dev);
> +void *vb2_dma_contig_init_ctx_attrs(struct device *dev,
> +				    struct dma_attrs *attrs);
> +
> +static inline void *vb2_dma_contig_init_ctx(struct device *dev)
> +{
> +	return vb2_dma_contig_init_ctx_attrs(dev, NULL);
> +}
> +
>  void vb2_dma_contig_cleanup_ctx(void *alloc_ctx);
> 
>  extern const struct vb2_mem_ops vb2_dma_contig_memops;
> diff --git a/include/uapi/linux/v4l2-controls.h
> b/include/uapi/linux/v4l2-controls.h index 2d225bc..63c65d9 100644
> --- a/include/uapi/linux/v4l2-controls.h
> +++ b/include/uapi/linux/v4l2-controls.h
> @@ -520,6 +520,7 @@ enum v4l2_mpeg_video_h264_hierarchical_coding_type {
>  };
>  #define
> V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER	
(V4L2_CID_MPEG_BASE+381)
> #define
> V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_QP	
(V4L2_CID_MPEG_BASE+3
> 82) +
>  #define V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP	(V4L2_CID_MPEG_BASE+400)
>  #define V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP	(V4L2_CID_MPEG_BASE+401)
>  #define V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP	(V4L2_CID_MPEG_BASE+402)
> @@ -578,6 +579,8 @@ enum v4l2_vp8_golden_frame_sel {
>  #define V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP		(V4L2_CID_MPEG_BASE+510)
>  #define V4L2_CID_MPEG_VIDEO_VPX_PROFILE			(V4L2_CID_MPEG_BASE+511)
> 
> +#define V4L2_CID_MPEG_VIDEO_VP8_FRAME_HDR		(V4L2_CID_MPEG_BASE+512)
> +
>  /*  MPEG-class control IDs specific to the CX2341x driver as defined by
> V4L2 */ #define V4L2_CID_MPEG_CX2341X_BASE 				(V4L2_CTRL_CLASS_MPEG |
> 0x1000) #define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE
> 	(V4L2_CID_MPEG_CX2341X_BASE+0) @@ -963,4 +966,99 @@ enum
> v4l2_detect_md_mode {
>  #define V4L2_CID_DETECT_MD_THRESHOLD_GRID	(V4L2_CID_DETECT_CLASS_BASE + 3)
>  #define V4L2_CID_DETECT_MD_REGION_GRID		(V4L2_CID_DETECT_CLASS_BASE + 4)
> 
> +
> +/* Complex controls */
> +
> +#define V4L2_VP8_SEGMNT_HDR_FLAG_ENABLED              0x01
> +#define V4L2_VP8_SEGMNT_HDR_FLAG_UPDATE_MAP           0x02
> +#define V4L2_VP8_SEGMNT_HDR_FLAG_UPDATE_FEATURE_DATA  0x04
> +struct v4l2_vp8_sgmnt_hdr {
> +	__u8 segment_feature_mode;
> +
> +	__s8 quant_update[4];
> +	__s8 lf_update[4];
> +	__u8 segment_probs[3];
> +
> +	__u8 flags;
> +};
> +
> +#define V4L2_VP8_LF_HDR_ADJ_ENABLE	0x01
> +#define V4L2_VP8_LF_HDR_DELTA_UPDATE	0x02
> +struct v4l2_vp8_loopfilter_hdr {
> +	__u8 type;
> +	__u8 level;
> +	__u8 sharpness_level;
> +	__s8 ref_frm_delta_magnitude[4];
> +	__s8 mb_mode_delta_magnitude[4];
> +
> +	__u8 flags;
> +};
> +
> +struct v4l2_vp8_quantization_hdr {
> +	__u8 y_ac_qi;
> +	__s8 y_dc_delta;
> +	__s8 y2_dc_delta;
> +	__s8 y2_ac_delta;
> +	__s8 uv_dc_delta;
> +	__s8 uv_ac_delta;
> +	__u16 dequant_factors[4][3][2];
> +};
> +
> +struct v4l2_vp8_entropy_hdr {
> +	__u8 coeff_probs[4][8][3][11];
> +	__u8 y_mode_probs[4];
> +	__u8 uv_mode_probs[3];
> +	__u8 mv_probs[2][19];
> +};
> +
> +#define V4L2_VP8_FRAME_HDR_FLAG_EXPERIMENTAL		0x01
> +#define V4L2_VP8_FRAME_HDR_FLAG_SHOW_FRAME		0x02
> +#define V4L2_VP8_FRAME_HDR_FLAG_MB_NO_SKIP_COEFF	0x04
> +struct v4l2_ctrl_vp8_frame_hdr {
> +	/* 0: keyframe, 1: not a keyframe */
> +	__u8 key_frame;
> +	__u8 version;
> +
> +	/* Populated also if not a key frame */
> +	__u16 width;
> +	__u8 horizontal_scale;
> +	__u16 height;
> +	__u8 vertical_scale;
> +
> +	struct v4l2_vp8_sgmnt_hdr sgmnt_hdr;
> +	struct v4l2_vp8_loopfilter_hdr lf_hdr;
> +	struct v4l2_vp8_quantization_hdr quant_hdr;
> +	struct v4l2_vp8_entropy_hdr entropy_hdr;
> +
> +	__u8 sign_bias_golden;
> +	__u8 sign_bias_alternate;
> +
> +	__u8 prob_skip_false;
> +	__u8 prob_intra;
> +	__u8 prob_last;
> +	__u8 prob_gf;
> +
> +	__u32 first_part_size;
> +	__u32 first_part_offset;
> +	/*
> +	 * Offset in bits of MB data in first partition,
> +	 * i.e. bit offset starting from first_part_offset.
> +	 */
> +	__u32 macroblock_bit_offset;
> +
> +	__u8 num_dct_parts;
> +	__u32 dct_part_sizes[8];
> +
> +	__u8 bool_dec_range;
> +	__u8 bool_dec_value;
> +	__u8 bool_dec_count;
> +
> +	/* v4l2_buffer indices of reference frames */
> +	__u32 last_frame;
> +	__u32 golden_frame;
> +	__u32 alt_frame;
> +
> +	__u8 flags;
> +};
> +
>  #endif
> diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
> index 29a6b78..191ca19 100644
> --- a/include/uapi/linux/videodev2.h
> +++ b/include/uapi/linux/videodev2.h
> @@ -593,6 +593,7 @@ struct v4l2_pix_format {
>  #define V4L2_PIX_FMT_VC1_ANNEX_G v4l2_fourcc('V', 'C', '1', 'G') /* SMPTE
> 421M Annex G compliant stream */ #define V4L2_PIX_FMT_VC1_ANNEX_L
> v4l2_fourcc('V', 'C', '1', 'L') /* SMPTE 421M Annex L compliant stream */
> #define V4L2_PIX_FMT_VP8      v4l2_fourcc('V', 'P', '8', '0') /* VP8 */
> +#define V4L2_PIX_FMT_VP8_FRAME	v4l2_fourcc('V', 'P', '8', 'F') /* VP8
> parsed frames */
> 
>  /*  Vendor-specific formats   */
>  #define V4L2_PIX_FMT_CPIA1    v4l2_fourcc('C', 'P', 'I', 'A') /* cpia1 YUV
> */ @@ -1473,6 +1474,7 @@ struct v4l2_ext_control {
>  		__u8 __user *p_u8;
>  		__u16 __user *p_u16;
>  		__u32 __user *p_u32;
> +		struct v4l2_ctrl_vp8_frame_hdr __user *p_vp8_frame_hdr;
>  		void __user *ptr;
>  	};
>  } __attribute__ ((packed));
> @@ -1517,6 +1519,9 @@ enum v4l2_ctrl_type {
>  	V4L2_CTRL_TYPE_U8	     = 0x0100,
>  	V4L2_CTRL_TYPE_U16	     = 0x0101,
>  	V4L2_CTRL_TYPE_U32	     = 0x0102,
> +	V4L2_CTRL_TYPE_VP8_FRAME_HDR	= 0x108,
> +
> +	V4L2_CTRL_TYPE_PRIVATE       = 0xffff,
>  };
> 
>  /*  Used in the VIDIOC_QUERYCTRL ioctl for querying controls */



From shawn.lin at rock-chips.com  Tue Jan 26 01:30:33 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Tue, 26 Jan 2016 17:30:33 +0800
Subject: [PATCH v1 0/3] Add VP8 deocder for rk3229 & rk3288
In-Reply-To: <1453799046-307-1-git-send-email-jung.zhao@rock-chips.com>
References: <1453799046-307-1-git-send-email-jung.zhao@rock-chips.com>
Message-ID: <56A73CB9.2040802@rock-chips.com>

Hi jun,

Where is the dt-bingding documentation about your VP8 controller?

And would you please share some info about rk3229? I can just find
rk3228 in mainline, otherwise may someone think it's a misspell.

Thanks.

On 2016/1/26 17:04, Jung Zhao wrote:
> From: zhaojun <jung.zhao at rock-chips.com>
>
>
> ====================
> Introduction
> ====================
>
> The purpose of this series is to add the driver for vp8
> decoder on rk3229 & rk3288 platform, and will support
> more formats in the future.
>
> The driver uses v4l2 framework and RK IOMMU.
> RK IOMMU has not yet been merged.
>
>
>
> zhaojun (3):
>    media: v4l: Add VP8 format support in V4L2 framework
>    media: VPU: support Rockchip VPU
>    media: vcodec: rockchip: Add Rockchip VP8 decoder driver
>
>   drivers/media/platform/rockchip-vpu/Makefile       |    7 +
>   .../media/platform/rockchip-vpu/rkvpu_hw_vp8d.c    |  798 ++++++++++
>   .../platform/rockchip-vpu/rockchip_vp8d_regs.h     | 1594 ++++++++++++++++++++
>   drivers/media/platform/rockchip-vpu/rockchip_vpu.c |  799 ++++++++++
>   .../platform/rockchip-vpu/rockchip_vpu_common.h    |  439 ++++++
>   .../media/platform/rockchip-vpu/rockchip_vpu_dec.c | 1007 +++++++++++++
>   .../media/platform/rockchip-vpu/rockchip_vpu_dec.h |   33 +
>   .../media/platform/rockchip-vpu/rockchip_vpu_hw.c  |  295 ++++
>   .../media/platform/rockchip-vpu/rockchip_vpu_hw.h  |  100 ++
>   drivers/media/v4l2-core/v4l2-ctrls.c               |   17 +-
>   drivers/media/v4l2-core/v4l2-ioctl.c               |    3 +
>   drivers/media/v4l2-core/videobuf2-dma-contig.c     |   51 +-
>   include/media/v4l2-ctrls.h                         |    2 +
>   include/media/videobuf2-dma-contig.h               |   11 +-
>   include/uapi/linux/v4l2-controls.h                 |   98 ++
>   include/uapi/linux/videodev2.h                     |    5 +
>   16 files changed, 5238 insertions(+), 21 deletions(-)
>   create mode 100644 drivers/media/platform/rockchip-vpu/Makefile
>   create mode 100644 drivers/media/platform/rockchip-vpu/rkvpu_hw_vp8d.c
>   create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vp8d_regs.h
>   create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu.c
>   create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu_common.h
>   create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.c
>   create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.h
>   create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.c
>   create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.h
>


-- 
Best Regards
Shawn Lin



From heiko at sntech.de  Tue Jan 26 01:52:25 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Tue, 26 Jan 2016 10:52:25 +0100
Subject: [PATCH v3 3/9] ASoC: rockchip: i2s: add support for grabbing
 output clock to codec
In-Reply-To: <56A57733.8020200@rock-chips.com>
References: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
 <20160122171815.GD6588@sirena.org.uk> <56A57733.8020200@rock-chips.com>
Message-ID: <2073307.S3XLZp3hP0@diego>

Hi Jay,

Am Montag, 25. Januar 2016, 09:15:31 schrieb Jianqun Xu:
> ? 23/01/2016 01:18, Mark Brown ??:
> > On Fri, Jan 15, 2016 at 01:48:04PM -0800, Sonny Rao wrote:
> >> On Fri, Jan 15, 2016 at 9:46 AM, Mark Brown <broonie at kernel.org> wrote:
> >>> If the I2S block is providing a clock to the CODEC then that's what the
> >>> software should do so that the CODEC can gate and ungate the clock as
> >>> required.  This patch has the I2S block using a clock, not providing
> >>> one.
> >>> 
> >>  From my read of the clock diagram for RK3288  there is a single clock
> >> 
> >> signal (labeled "clk_i2s0") that comes out of a fractional divider,
> >> and it is split such that one path gets sent to the I2S block and the
> >> second path is sent to a mux after which that signal is sent to an
> >> external pin that goes to the codec.
> >> 
> >> There are separate clock gates for the two paths: one for the I2S
> >> block and one after that mux before the external pin.
> >> 
> >> I'm not sure if it's being modeled that way in the Linux code or not,
> >> but at least physically I don't think this clock signal actually goes
> >> through the I2S block before being sent to the codec.
> > 
> > That's not really the issue here, the issue is that it's not the I2S
> > controller that is consuming the clock so it should not be the I2S
> > controller driver that ensures that the clock is enabled.  The driver
> > that manages the clock should be the one that uses it, like I say this
> > means you should add the code to enable the clock to the CODEC driver if
> > the CODEC driver needs the clock enabled.
> 
> Agree, now we almost use the simple-card for the CODEC driver, so I
> think we should enable the mclk(i2s-outclk) in the simple-card driver,
> is it ?
> 
> I found a subnode property from simple-card document:
> - mclk-fs                               : Multiplication factor between
> stream
>                                            rate and codec mclk, applied
> only for
>                                            the dai-link.
> But the property responsible to the factor, not care if the mclk source
> clock is enabled or not. So does the simple-card driver can add support
> to enable/disable mclk ?

The mclk-input is part of the codec I'd think. So you'd want the clocks-
property in the i2c entry describing the codec itself and implement the clk 
operations in the codec driver as well.

See codec-drivers for da7213, da7218,max98090 and many more for reference.


Heiko



> >> Does that help clarify?
> > 
> > The problem here isn't a lack of clarity in the situation.


From al.kochet at gmail.com  Tue Jan 26 04:44:10 2016
From: al.kochet at gmail.com (Alexander Kochetkov)
Date: Tue, 26 Jan 2016 15:44:10 +0300
Subject: [PATCH] clk: rockchip: Reparent hclk_rom,
 hclk_i2s0 and hclk_spdif to hclk_cpubus on RK3066/RK3188
Message-ID: <1453812250-11819-1-git-send-email-al.kochet@gmail.com>

In order to make peripherals (rom, i2s0 or spdif) accessible via cpu,
hclk_cpubus gate must be opened. Without that all accesses (readl/writel)
return wrong data.

The patch fixes regression introduced by recent clock code.

Fixes: 78eaf6095cc763c ("clk: rockchip: disable unused clocks")

Signed-off-by: Alexander Kochetkov <al.kochet at gmail.com>

CC: linux-clk at vger.kernel.org
CC: linux-arm-kernel at lists.infradead.org
CC: linux-rockchip at lists.infradead.org
CC: linux-kernel at vger.kernel.org
CC: stable at vger.kernel.org # 4.1.x-
---
 drivers/clk/rockchip/clk-rk3188.c |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c
index abb4760..54b88a6 100644
--- a/drivers/clk/rockchip/clk-rk3188.c
+++ b/drivers/clk/rockchip/clk-rk3188.c
@@ -429,10 +429,10 @@ static struct rockchip_clk_branch common_clk_branches[] __initdata = {
 	GATE(0, "aclk_strc_sys", "aclk_cpu", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(4), 10, GFLAGS),
 
 	/* hclk_cpu gates */
-	GATE(HCLK_ROM, "hclk_rom", "hclk_cpu", 0, RK2928_CLKGATE_CON(5), 6, GFLAGS),
-	GATE(HCLK_I2S0, "hclk_i2s0", "hclk_cpu", 0, RK2928_CLKGATE_CON(7), 2, GFLAGS),
-	GATE(HCLK_SPDIF, "hclk_spdif", "hclk_cpu", 0, RK2928_CLKGATE_CON(7), 1, GFLAGS),
 	GATE(0, "hclk_cpubus", "hclk_cpu", 0, RK2928_CLKGATE_CON(4), 8, GFLAGS),
+	GATE(HCLK_ROM, "hclk_rom", "hclk_cpubus", 0, RK2928_CLKGATE_CON(5), 6, GFLAGS),
+	GATE(HCLK_I2S0, "hclk_i2s0", "hclk_cpubus", 0, RK2928_CLKGATE_CON(7), 2, GFLAGS),
+	GATE(HCLK_SPDIF, "hclk_spdif", "hclk_cpubus", 0, RK2928_CLKGATE_CON(7), 1, GFLAGS),
 	/* hclk_ahb2apb is part of a clk branch */
 	GATE(0, "hclk_vio_bus", "hclk_cpu", 0, RK2928_CLKGATE_CON(6), 12, GFLAGS),
 	GATE(HCLK_LCDC0, "hclk_lcdc0", "hclk_cpu", 0, RK2928_CLKGATE_CON(6), 1, GFLAGS),
-- 
1.7.9.5



From heiko at sntech.de  Tue Jan 26 05:04:58 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Tue, 26 Jan 2016 14:04:58 +0100
Subject: [PATCH] clk: rockchip: Reparent hclk_rom,
 hclk_i2s0 and hclk_spdif to hclk_cpubus on RK3066/RK3188
In-Reply-To: <1453812250-11819-1-git-send-email-al.kochet@gmail.com>
References: <1453812250-11819-1-git-send-email-al.kochet@gmail.com>
Message-ID: <18195479.b78tX8z5U3@diego>

Hi Alexander,

Am Dienstag, 26. Januar 2016, 15:44:10 schrieb Alexander Kochetkov:
> In order to make peripherals (rom, i2s0 or spdif) accessible via cpu,
> hclk_cpubus gate must be opened. Without that all accesses (readl/writel)
> return wrong data.

The clock hierarchy does reflect the hardware structure. What you're 
introducing are software constraints. Meaning hclk_cpubus is most likely part 
of the interconnect allowing these peripherals to talk to other parts of the 
system, but the relevant clocks are _not_ children of hclk_cpubus.

Can you instead try to add hclk_cpubus to the critical clock list please?


Thanks
Heiko


> 
> The patch fixes regression introduced by recent clock code.
> 
> Fixes: 78eaf6095cc763c ("clk: rockchip: disable unused clocks")
> 
> Signed-off-by: Alexander Kochetkov <al.kochet at gmail.com>
> 
> CC: linux-clk at vger.kernel.org
> CC: linux-arm-kernel at lists.infradead.org
> CC: linux-rockchip at lists.infradead.org
> CC: linux-kernel at vger.kernel.org
> CC: stable at vger.kernel.org # 4.1.x-
> ---
>  drivers/clk/rockchip/clk-rk3188.c |    6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/clk/rockchip/clk-rk3188.c
> b/drivers/clk/rockchip/clk-rk3188.c index abb4760..54b88a6 100644
> --- a/drivers/clk/rockchip/clk-rk3188.c
> +++ b/drivers/clk/rockchip/clk-rk3188.c
> @@ -429,10 +429,10 @@ static struct rockchip_clk_branch
> common_clk_branches[] __initdata = { GATE(0, "aclk_strc_sys", "aclk_cpu",
> CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(4), 10, GFLAGS),
> 
>  	/* hclk_cpu gates */
> -	GATE(HCLK_ROM, "hclk_rom", "hclk_cpu", 0, RK2928_CLKGATE_CON(5), 6,
> GFLAGS), -	GATE(HCLK_I2S0, "hclk_i2s0", "hclk_cpu", 0,
> RK2928_CLKGATE_CON(7), 2, GFLAGS), -	GATE(HCLK_SPDIF, "hclk_spdif",
> "hclk_cpu", 0, RK2928_CLKGATE_CON(7), 1, GFLAGS), GATE(0, "hclk_cpubus",
> "hclk_cpu", 0, RK2928_CLKGATE_CON(4), 8, GFLAGS), +	GATE(HCLK_ROM,
> "hclk_rom", "hclk_cpubus", 0, RK2928_CLKGATE_CON(5), 6, GFLAGS),
> +	GATE(HCLK_I2S0, "hclk_i2s0", "hclk_cpubus", 0, RK2928_CLKGATE_CON(7), 2,
> GFLAGS), +	GATE(HCLK_SPDIF, "hclk_spdif", "hclk_cpubus", 0,
> RK2928_CLKGATE_CON(7), 1, GFLAGS), /* hclk_ahb2apb is part of a clk branch
> */
>  	GATE(0, "hclk_vio_bus", "hclk_cpu", 0, RK2928_CLKGATE_CON(6), 12, GFLAGS),
> GATE(HCLK_LCDC0, "hclk_lcdc0", "hclk_cpu", 0, RK2928_CLKGATE_CON(6), 1,
> GFLAGS),



From lkp at intel.com  Tue Jan 26 05:08:07 2016
From: lkp at intel.com (kbuild test robot)
Date: Tue, 26 Jan 2016 21:08:07 +0800
Subject: [PATCH v4] phy: rockchip-usb: add handler for usb-uart
 functionality
In-Reply-To: <2609586.28qbVUeppF@diego>
Message-ID: <201601262116.ol4BKdgo%fengguang.wu@intel.com>

Hi Heiko,

[auto build test ERROR on rockchip/for-next]
[also build test ERROR on v4.5-rc1 next-20160125]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]

url:    https://github.com/0day-ci/linux/commits/Heiko-St-bner/phy-rockchip-usb-add-handler-for-usb-uart-functionality/20160126-082429
base:   https://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git for-next
config: arm-defconfig (attached as .config)
reproduce:
        wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=arm 

All error/warnings (new ones prefixed by >>):

   In file included from drivers/phy/phy-rockchip-usb.c:21:0:
   include/linux/module.h:128:27: error: redefinition of '__inittest'
     static inline initcall_t __inittest(void)  \
                              ^
   include/linux/module.h:107:29: note: in expansion of macro 'module_init'
    #define early_initcall(fn)  module_init(fn)
                                ^
>> drivers/phy/phy-rockchip-usb.c:481:1: note: in expansion of macro 'early_initcall'
    early_initcall(rockchip_init_usb_uart);
    ^
   include/linux/module.h:128:27: note: previous definition of '__inittest' was here
     static inline initcall_t __inittest(void)  \
                              ^
>> include/linux/device.h:1332:1: note: in expansion of macro 'module_init'
    module_init(__driver##_init); \
    ^
   include/linux/platform_device.h:228:2: note: in expansion of macro 'module_driver'
     module_driver(__platform_driver, platform_driver_register, \
     ^
>> drivers/phy/phy-rockchip-usb.c:436:1: note: in expansion of macro 'module_platform_driver'
    module_platform_driver(rockchip_usb_driver);
    ^
   include/linux/module.h:130:6: error: redefinition of 'init_module'
     int init_module(void) __attribute__((alias(#initfn)));
         ^
   include/linux/module.h:107:29: note: in expansion of macro 'module_init'
    #define early_initcall(fn)  module_init(fn)
                                ^
>> drivers/phy/phy-rockchip-usb.c:481:1: note: in expansion of macro 'early_initcall'
    early_initcall(rockchip_init_usb_uart);
    ^
   include/linux/module.h:130:6: note: previous definition of 'init_module' was here
     int init_module(void) __attribute__((alias(#initfn)));
         ^
>> include/linux/device.h:1332:1: note: in expansion of macro 'module_init'
    module_init(__driver##_init); \
    ^
   include/linux/platform_device.h:228:2: note: in expansion of macro 'module_driver'
     module_driver(__platform_driver, platform_driver_register, \
     ^
>> drivers/phy/phy-rockchip-usb.c:436:1: note: in expansion of macro 'module_platform_driver'
    module_platform_driver(rockchip_usb_driver);
    ^
>> drivers/phy/phy-rockchip-usb.c:488:13: error: expected declaration specifiers or '...' before string constant
    early_param("rockchip.usb_uart", rockchip_usb_uart);
                ^
>> drivers/phy/phy-rockchip-usb.c:488:34: error: expected declaration specifiers or '...' before 'rockchip_usb_uart'
    early_param("rockchip.usb_uart", rockchip_usb_uart);
                                     ^
   drivers/phy/phy-rockchip-usb.c:483:19: warning: 'rockchip_usb_uart' defined but not used [-Wunused-function]
    static int __init rockchip_usb_uart(char *buf)
                      ^

vim +488 drivers/phy/phy-rockchip-usb.c

   430		.driver		= {
   431			.name	= "rockchip-usb-phy",
   432			.of_match_table = rockchip_usb_phy_dt_ids,
   433		},
   434	};
   435	
 > 436	module_platform_driver(rockchip_usb_driver);
   437	
   438	static int __init rockchip_init_usb_uart(void)
   439	{
   440		const struct of_device_id *match;
   441		const struct rockchip_usb_phy_pdata *data;
   442		struct device_node *np;
   443		struct regmap *grf;
   444		int ret;
   445	
   446		if (!enable_usb_uart)
   447			return 0;
   448	
   449		np = of_find_matching_node_and_match(NULL, rockchip_usb_phy_dt_ids,
   450						     &match);
   451		if (!np) {
   452			pr_err("%s: failed to find usbphy node\n", __func__);
   453			return -ENOTSUPP;
   454		}
   455	
   456		pr_debug("%s: using settings for %s\n", __func__, match->compatible);
   457		data = match->data;
   458	
   459		if (!data->init_usb_uart) {
   460			pr_err("%s: usb-uart not available on %s\n",
   461			       __func__, match->compatible);
   462			return -ENOTSUPP;
   463		}
   464	
   465		grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
   466		if (IS_ERR(grf)) {
   467			pr_err("%s: Missing rockchip,grf property, %lu\n",
   468			       __func__, PTR_ERR(grf));
   469			return PTR_ERR(grf);
   470		}
   471	
   472		ret = data->init_usb_uart(grf);
   473		if (ret) {
   474			pr_err("%s: could not init usb_uart, %d\n", __func__, ret);
   475			enable_usb_uart = 0;
   476			return ret;
   477		}
   478	
   479		return 0;
   480	}
 > 481	early_initcall(rockchip_init_usb_uart);
   482	
   483	static int __init rockchip_usb_uart(char *buf)
   484	{
   485		enable_usb_uart = true;
   486		return 0;
   487	}
 > 488	early_param("rockchip.usb_uart", rockchip_usb_uart);
   489	
   490	MODULE_AUTHOR("Yunzhi Li <lyz at rock-chips.com>");
   491	MODULE_DESCRIPTION("Rockchip USB 2.0 PHY driver");

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
-------------- next part --------------
A non-text attachment was scrubbed...
Name: .config.gz
Type: application/octet-stream
Size: 36561 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160126/e4aba1f0/attachment-0001.obj>

From al.kochet at gmail.com  Tue Jan 26 05:16:57 2016
From: al.kochet at gmail.com (Alexander Kochetkov)
Date: Tue, 26 Jan 2016 16:16:57 +0300
Subject: [PATCH] clk: rockchip: Reparent hclk_rom,
 hclk_i2s0 and hclk_spdif to hclk_cpubus on RK3066/RK3188
In-Reply-To: <18195479.b78tX8z5U3@diego>
References: <1453812250-11819-1-git-send-email-al.kochet@gmail.com>
 <18195479.b78tX8z5U3@diego>
Message-ID: <0F6CFF20-C3D2-4A4D-83D5-1DE6014BB75D@gmail.com>


> 26 ???. 2016 ?., ? 16:04, Heiko St?bner <heiko at sntech.de> ???????(?):
> 
> Can you instead try to add hclk_cpubus to the critical clock list please?

Yes, adding hclk_cpubus to rk3188_critical_clocks also fixes problem.
So, I?ll send another patch now.

Thank, you.



From al.kochet at gmail.com  Tue Jan 26 05:34:00 2016
From: al.kochet at gmail.com (Alexander Kochetkov)
Date: Tue, 26 Jan 2016 16:34:00 +0300
Subject: [PATCH] clk: rockchip: add hclk_cpubus to the list of rk3188 critical
 clocks
Message-ID: <1453815240-15354-1-git-send-email-al.kochet@gmail.com>

hclk_cpubus needs to keep running because it is needed for devices like
the rom, i2s0 or spdif to be accessible via cpu. Without that all
accesses to devices (readl/writel) return wrong data. So add it
to the list of critical clocks.

Fixes: 78eaf6095cc763c ("clk: rockchip: disable unused clocks")

Signed-off-by: Alexander Kochetkov <al.kochet at gmail.com>

CC: linux-clk at vger.kernel.org
CC: linux-arm-kernel at lists.infradead.org
CC: linux-rockchip at lists.infradead.org
CC: linux-kernel at vger.kernel.org
CC: stable at vger.kernel.org # 4.1.x-
---
 drivers/clk/rockchip/clk-rk3188.c |    1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c
index abb4760..fe728f8 100644
--- a/drivers/clk/rockchip/clk-rk3188.c
+++ b/drivers/clk/rockchip/clk-rk3188.c
@@ -718,6 +718,7 @@ static const char *const rk3188_critical_clocks[] __initconst = {
 	"hclk_peri",
 	"pclk_cpu",
 	"pclk_peri",
+	"hclk_cpubus"
 };
 
 static void __init rk3188_common_clk_init(struct device_node *np)
-- 
1.7.9.5



From eballetbo at gmail.com  Tue Jan 26 05:53:01 2016
From: eballetbo at gmail.com (Enric Balletbo Serra)
Date: Tue, 26 Jan 2016 14:53:01 +0100
Subject: [PATCH v1 2/3] media: VPU: support Rockchip VPU
In-Reply-To: <1453799046-307-3-git-send-email-jung.zhao@rock-chips.com>
References: <1453799046-307-1-git-send-email-jung.zhao@rock-chips.com>
 <1453799046-307-3-git-send-email-jung.zhao@rock-chips.com>
Message-ID: <CAFqH_51cpqc737qeefDfseyVCGp4JRFnVcz8VrCsp6LYqAq2HQ@mail.gmail.com>

Hi Jung,

I'm interested in this patch series and I'll try to test on my RK3288 board.

Please, see my comments below to improve some things that helped me to
upstream some other patches, based on feedback from others
maintainers. Also I tend to use checkpatch with --strict option, it's
not a must but helps.

2016-01-26 10:04 GMT+01:00 Jung Zhao <jung.zhao at rock-chips.com>:
> From: zhaojun <jung.zhao at rock-chips.com>
>
> The VPU driver for rk3229 & rk3288 platform.
> Now only support VP8 decoder.
>

The VP8 decoder support is introduced in patch 3, right ? So here the
message is inaccurate. It's preferred have a good explanation when you
introduce a new driver so will be good if you extend a bit more the
patch description, for example.

Add rockchip-vpu driver

The VPU driver for hw video codec in Rockchip's RK3229 and RK3288
SOCs, is able to handle video decoding/encoding of in a range of
formats ... blablabla

> Signed-off-by: zhaojun <jung.zhao at rock-chips.com>

If I'm not wrong this patch and the others are based on some patches
from the chromium tree, in such case, will be good, when possible to
maintain the signed-off chain

> ---
>
>  drivers/media/platform/rockchip-vpu/Makefile       |   6 +
>  drivers/media/platform/rockchip-vpu/rockchip_vpu.c | 799 +++++++++++++++++
>  .../platform/rockchip-vpu/rockchip_vpu_common.h    | 439 +++++++++
>  .../media/platform/rockchip-vpu/rockchip_vpu_dec.c | 994 +++++++++++++++++++++
>  .../media/platform/rockchip-vpu/rockchip_vpu_dec.h |  33 +
>  .../media/platform/rockchip-vpu/rockchip_vpu_hw.c  | 278 ++++++
>  .../media/platform/rockchip-vpu/rockchip_vpu_hw.h  |  78 ++
>  7 files changed, 2627 insertions(+)
>  create mode 100644 drivers/media/platform/rockchip-vpu/Makefile

Did you miss to add the Kconfig file here ?

>  create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu.c
>  create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu_common.h
>  create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.c
>  create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.h
>  create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.c
>  create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.h
>
> diff --git a/drivers/media/platform/rockchip-vpu/Makefile b/drivers/media/platform/rockchip-vpu/Makefile
> new file mode 100644
> index 0000000..5aab094
> --- /dev/null
> +++ b/drivers/media/platform/rockchip-vpu/Makefile
> @@ -0,0 +1,6 @@
> +
> +obj-$(CONFIG_VIDEO_ROCKCHIP_VPU) += rockchip-vpu.o
> +
> +rockchip-vpu-y += rockchip_vpu.o \
> +               rockchip_vpu_dec.o \
> +               rockchip_vpu_hw.o
> diff --git a/drivers/media/platform/rockchip-vpu/rockchip_vpu.c b/drivers/media/platform/rockchip-vpu/rockchip_vpu.c
> new file mode 100644
> index 0000000..e7e7cb5
> --- /dev/null
> +++ b/drivers/media/platform/rockchip-vpu/rockchip_vpu.c
> @@ -0,0 +1,799 @@
> +/*
> + * Rockchip VPU codec driver
> + *
> + * Copyright (C) 2014 Google, Inc.
> + *     Tomasz Figa <tfiga at chromium.org>
> + *

We're in 2016, so I think you should update the copyright notice. The
same for all the new files you create

> + * Based on s5p-mfc driver by Samsung Electronics Co., Ltd.
> + *
> + * Copyright (C) 2011 Samsung Electronics Co., Ltd.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include "rockchip_vpu_common.h"
> +
> +#include <linux/fs.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/videodev2.h>
> +#include <media/v4l2-event.h>
> +#include <linux/workqueue.h>
> +#include <linux/of.h>
> +#include <media/videobuf2-core.h>
> +#include <media/videobuf2-dma-contig.h>
> +
> +#include "rockchip_vpu_dec.h"
> +#include "rockchip_vpu_hw.h"
> +

Alphabetical order is preferred, the same for other files.

> +int debug;
> +module_param(debug, int, S_IRUGO | S_IWUSR);
> +MODULE_PARM_DESC(debug,
> +                "Debug level - higher value produces more verbose messages");
> +
> +#define DUMP_FILE "/tmp/vpu.out"
> +
> +int rockchip_vpu_write(const char *file, void *buf, size_t size)
> +{
> +       const char __user *p = (__force const char __user *)buf;
> +       struct file *filp = filp_open(file ? file : DUMP_FILE,
> +                       O_CREAT | O_RDWR | O_APPEND, 0644);

For me is more clear define only the variable here and do the open
before check the error

            struct file *filp;

> +       mm_segment_t fs;
> +       int ret;
> +

            flip = filp_open(file ? file : DUMP_FILE, O_CREAT | O_RDWR
| O_APPEND, 0644);
> +       if (IS_ERR(filp)) {
> +               printk(KERN_ERR "open(%s) failed\n", file ? file : DUMP_FILE);
> +               return -ENODEV;
> +       }
> +
> +       fs = get_fs();
> +       set_fs(KERNEL_DS);
> +
> +       filp->f_pos = 0;
> +       ret = filp->f_op->write(filp, p, size, &filp->f_pos);
> +
> +       filp_close(filp, NULL);
> +       set_fs(fs);
> +
> +       return ret;
> +}

BTW, is this function used ? If not remove it.

> +
> +/*
> + * DMA coherent helpers.
> + */
> +
> +int rockchip_vpu_aux_buf_alloc(struct rockchip_vpu_dev *vpu,
> +                              struct rockchip_vpu_aux_buf *buf, size_t size)
> +{
> +       buf->cpu = dma_alloc_coherent(vpu->dev, size, &buf->dma, GFP_KERNEL);
> +       if (!buf->cpu)
> +               return -ENOMEM;
> +
> +       buf->size = size;
> +       return 0;
> +}
> +
> +void rockchip_vpu_aux_buf_free(struct rockchip_vpu_dev *vpu,
> +                              struct rockchip_vpu_aux_buf *buf)
> +{
> +       dma_free_coherent(vpu->dev, buf->size, buf->cpu, buf->dma);
> +
> +       buf->cpu = NULL;
> +       buf->dma = 0;
> +       buf->size = 0;
> +}
> +
> +/*
> + * Context scheduling.
> + */
> +
> +static void rockchip_vpu_prepare_run(struct rockchip_vpu_ctx *ctx)
> +{
> +       if (ctx->run_ops->prepare_run)
> +               ctx->run_ops->prepare_run(ctx);
> +}
> +
> +static void __rockchip_vpu_dequeue_run_locked(struct rockchip_vpu_ctx *ctx)
> +{
> +       struct rockchip_vpu_buf *src, *dst;
> +
> +       /*
> +        * Since ctx was dequeued from ready_ctxs list, we know that it has
> +        * at least one buffer in each queue.
> +        */
> +       src = list_first_entry(&ctx->src_queue, struct rockchip_vpu_buf, list);
> +       dst = list_first_entry(&ctx->dst_queue, struct rockchip_vpu_buf, list);
> +
> +       list_del(&src->list);
> +       list_del(&dst->list);
> +
> +       ctx->run.src = src;
> +       ctx->run.dst = dst;
> +}
> +
> +static void rockchip_vpu_try_run(struct rockchip_vpu_dev *dev)
> +{
> +       struct rockchip_vpu_ctx *ctx = NULL;
> +       unsigned long flags;
> +
> +       vpu_debug_enter();
> +

This is only to trace the function call, you can use ftrace for that
purpose so you can remove this and the others.

> +       spin_lock_irqsave(&dev->irqlock, flags);
> +
> +       if (list_empty(&dev->ready_ctxs) ||
> +           test_bit(VPU_SUSPENDED, &dev->state))
> +               /* Nothing to do. */
> +               goto out;
> +
> +       if (test_and_set_bit(VPU_RUNNING, &dev->state))
> +               /*
> +               * The hardware is already running. We will pick another
> +               * run after we get the notification in rockchip_vpu_run_done().
> +               */
> +               goto out;
> +
> +       ctx = list_entry(dev->ready_ctxs.next, struct rockchip_vpu_ctx, list);
> +
> +       list_del_init(&ctx->list);
> +       __rockchip_vpu_dequeue_run_locked(ctx);
> +
> +       dev->current_ctx = ctx;
> +
> +out:
> +       spin_unlock_irqrestore(&dev->irqlock, flags);
> +
> +       if (ctx) {
> +               rockchip_vpu_prepare_run(ctx);
> +               rockchip_vpu_run(ctx);
> +       }
> +
> +       vpu_debug_leave();

I think you can remove all these call too.

> +}
> +
> +static void __rockchip_vpu_try_context_locked(struct rockchip_vpu_dev *dev,
> +                                             struct rockchip_vpu_ctx *ctx)
> +{
> +       if (!list_empty(&ctx->list))
> +               /* Context already queued. */
> +               return;
> +
> +       if (!list_empty(&ctx->dst_queue) && !list_empty(&ctx->src_queue))
> +               list_add_tail(&ctx->list, &dev->ready_ctxs);
> +}
> +
> +void rockchip_vpu_run_done(struct rockchip_vpu_ctx *ctx,
> +                          enum vb2_buffer_state result)
> +{
> +       struct rockchip_vpu_dev *dev = ctx->dev;
> +       unsigned long flags;
> +
> +       vpu_debug_enter();
> +
> +       if (ctx->run_ops->run_done)
> +               ctx->run_ops->run_done(ctx, result);
> +
> +       struct vb2_buffer *src = &ctx->run.src->b.vb2_buf;
> +       struct vb2_buffer *dst = &ctx->run.dst->b.vb2_buf;
> +
> +       to_vb2_v4l2_buffer(dst)->timestamp =
> +               to_vb2_v4l2_buffer(src)->timestamp;
> +       vb2_buffer_done(&ctx->run.src->b.vb2_buf, result);
> +       vb2_buffer_done(&ctx->run.dst->b.vb2_buf, result);
> +
> +       dev->current_ctx = NULL;
> +       wake_up_all(&dev->run_wq);
> +
> +       spin_lock_irqsave(&dev->irqlock, flags);
> +
> +       __rockchip_vpu_try_context_locked(dev, ctx);
> +       clear_bit(VPU_RUNNING, &dev->state);
> +
> +       spin_unlock_irqrestore(&dev->irqlock, flags);
> +
> +       /* Try scheduling another run to see if we have anything left to do. */
> +       rockchip_vpu_try_run(dev);
> +
> +       vpu_debug_leave();
> +}
> +
> +void rockchip_vpu_try_context(struct rockchip_vpu_dev *dev,
> +                             struct rockchip_vpu_ctx *ctx)
> +{
> +       unsigned long flags;
> +
> +       vpu_debug_enter();
> +
> +       spin_lock_irqsave(&dev->irqlock, flags);
> +
> +       __rockchip_vpu_try_context_locked(dev, ctx);
> +
> +       spin_unlock_irqrestore(&dev->irqlock, flags);
> +
> +       rockchip_vpu_try_run(dev);
> +
> +       vpu_debug_enter();
> +}
> +
> +/*
> + * Control registration.
> + */
> +
> +#define IS_VPU_PRIV(x) ((V4L2_CTRL_ID2WHICH(x) == V4L2_CTRL_CLASS_MPEG) && \
> +                         V4L2_CTRL_DRIVER_PRIV(x))
> +
> +int rockchip_vpu_ctrls_setup(struct rockchip_vpu_ctx *ctx,
> +                            const struct v4l2_ctrl_ops *ctrl_ops,
> +                            struct rockchip_vpu_control *controls,
> +                            unsigned num_ctrls,
> +                            const char* const* (*get_menu)(u32))
> +{
> +       struct v4l2_ctrl_config cfg;
> +       int i;
> +
> +       if (num_ctrls > ARRAY_SIZE(ctx->ctrls)) {
> +               vpu_err("context control array not large enough\n");
> +               return -ENOSPC;
> +       }
> +
> +       v4l2_ctrl_handler_init(&ctx->ctrl_handler, num_ctrls);
> +       if (ctx->ctrl_handler.error) {
> +               vpu_err("v4l2_ctrl_handler_init failed\n");
> +               return ctx->ctrl_handler.error;
> +       }
> +
> +       for (i = 0; i < num_ctrls; i++) {
> +               if (IS_VPU_PRIV(controls[i].id)
> +                   || controls[i].id >= V4L2_CID_CUSTOM_BASE
> +                   || controls[i].type == V4L2_CTRL_TYPE_PRIVATE) {
> +                       memset(&cfg, 0, sizeof(struct v4l2_ctrl_config));
> +
> +                       cfg.ops = ctrl_ops;
> +                       cfg.id = controls[i].id;
> +                       cfg.min = controls[i].minimum;
> +                       cfg.max = controls[i].maximum;
> +                       cfg.max_reqs = controls[i].max_reqs;
> +                       cfg.def = controls[i].default_value;
> +                       cfg.name = controls[i].name;
> +                       cfg.type = controls[i].type;
> +                       cfg.elem_size = controls[i].elem_size;
> +                       memcpy(cfg.dims, controls[i].dims, sizeof(cfg.dims));
> +
> +                       if (cfg.type == V4L2_CTRL_TYPE_MENU) {
> +                               cfg.menu_skip_mask = cfg.menu_skip_mask;
> +                               cfg.qmenu = get_menu(cfg.id);
> +                       } else {
> +                               cfg.step = controls[i].step;
> +                       }
> +
> +                       ctx->ctrls[i] = v4l2_ctrl_new_custom(
> +                                                            &ctx->ctrl_handler,
> +                                                            &cfg, NULL);
> +               } else {
> +                       if (controls[i].type == V4L2_CTRL_TYPE_MENU) {
> +                               ctx->ctrls[i] =
> +                                       v4l2_ctrl_new_std_menu
> +                                       (&ctx->ctrl_handler,
> +                                        ctrl_ops,
> +                                        controls[i].id,
> +                                        controls[i].maximum,
> +                                        0,
> +                                        controls[i].
> +                                        default_value);
> +                       } else {
> +                               ctx->ctrls[i] =
> +                                       v4l2_ctrl_new_std(&ctx->ctrl_handler,
> +                                                         ctrl_ops,
> +                                                         controls[i].id,
> +                                                         controls[i].minimum,
> +                                                         controls[i].maximum,
> +                                                         controls[i].step,
> +                                                         controls[i].
> +                                                         default_value);
> +                       }
> +               }
> +
> +               if (ctx->ctrl_handler.error) {
> +                       vpu_err("Adding control (%d) failed\n", i);
> +                       return ctx->ctrl_handler.error;
> +               }
> +
> +               if (controls[i].is_volatile && ctx->ctrls[i])
> +                       ctx->ctrls[i]->flags |= V4L2_CTRL_FLAG_VOLATILE;
> +               if (controls[i].is_read_only && ctx->ctrls[i])
> +                       ctx->ctrls[i]->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +               if (controls[i].can_store && ctx->ctrls[i])
> +                       ctx->ctrls[i]->flags |= V4L2_CTRL_FLAG_REQ_KEEP;
> +       }
> +
> +       v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
> +       ctx->num_ctrls = num_ctrls;
> +       return 0;
> +}
> +
> +void rockchip_vpu_ctrls_delete(struct rockchip_vpu_ctx *ctx)
> +{
> +       int i;
> +
> +       v4l2_ctrl_handler_free(&ctx->ctrl_handler);
> +       for (i = 0; i < ctx->num_ctrls; i++)
> +               ctx->ctrls[i] = NULL;
> +}
> +
> +/*
> + * V4L2 file operations.
> + */
> +
> +static int rockchip_vpu_open(struct file *filp)
> +{
> +       struct video_device *vdev = video_devdata(filp);
> +       struct rockchip_vpu_dev *dev = video_drvdata(filp);
> +       struct rockchip_vpu_ctx *ctx = NULL;
> +       struct vb2_queue *q;
> +       int ret = 0;
> +
> +       /*
> +        * We do not need any extra locking here, because we operate only
> +        * on local data here, except reading few fields from dev, which
> +        * do not change through device's lifetime (which is guaranteed by
> +        * reference on module from open()) and V4L2 internal objects (such
> +        * as vdev and ctx->fh), which have proper locking done in respective
> +        * helper functions used here.
> +        */
> +
> +       vpu_debug_enter();
> +
> +       /* Allocate memory for context */
> +       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
> +       if (!ctx) {
> +               ret = -ENOMEM;
> +               goto err_leave;
> +       }
> +
> +       v4l2_fh_init(&ctx->fh, video_devdata(filp));
> +       filp->private_data = &ctx->fh;
> +       v4l2_fh_add(&ctx->fh);
> +       ctx->dev = dev;
> +       INIT_LIST_HEAD(&ctx->src_queue);
> +       INIT_LIST_HEAD(&ctx->dst_queue);
> +       INIT_LIST_HEAD(&ctx->list);
> +
> +       if (vdev == dev->vfd_dec) {
> +               /* only for decoder */
> +               ret = rockchip_vpu_dec_init(ctx);
> +               if (ret) {
> +                       vpu_err("Failed to initialize decoder context\n");
> +                       goto err_fh_free;
> +               }
> +       } else {
> +               ret = -ENOENT;
> +               goto err_fh_free;
> +       }
> +       ctx->fh.ctrl_handler = &ctx->ctrl_handler;
> +
> +       /* Init videobuf2 queue for CAPTURE */
> +       q = &ctx->vq_dst;
> +       q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
> +       q->drv_priv = &ctx->fh;
> +       q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
> +       q->lock = &dev->vpu_mutex;
> +       q->buf_struct_size = sizeof(struct rockchip_vpu_buf);
> +
> +       if (vdev == dev->vfd_dec)
> +               q->ops = get_dec_queue_ops();
> +
> +       q->mem_ops = &vb2_dma_contig_memops;
> +       q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
> +       q->v4l2_allow_requests = true;
> +       ret = vb2_queue_init(q);
> +       if (ret) {
> +               vpu_err("Failed to initialize videobuf2 queue(capture)\n");
> +               goto err_dec_exit;
> +       }
> +
> +       /* Init videobuf2 queue for OUTPUT */
> +       q = &ctx->vq_src;
> +       q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
> +       q->drv_priv = &ctx->fh;
> +       q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
> +       q->lock = &dev->vpu_mutex;
> +       q->buf_struct_size = sizeof(struct rockchip_vpu_buf);
> +
> +       if (vdev == dev->vfd_dec)
> +               q->ops = get_dec_queue_ops();
> +
> +       q->mem_ops = &vb2_dma_contig_memops;
> +       q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
> +       q->v4l2_allow_requests = true;
> +       ret = vb2_queue_init(q);
> +       if (ret) {
> +               vpu_err("Failed to initialize videobuf2 queue(output)\n");
> +               goto err_vq_dst_release;
> +       }
> +
> +       vpu_debug_leave();
> +
> +       return 0;
> +
> +err_vq_dst_release:
> +       vb2_queue_release(&ctx->vq_dst);
> +err_dec_exit:
> +       if (vdev == dev->vfd_dec)
> +               rockchip_vpu_dec_exit(ctx);
> +err_fh_free:
> +       v4l2_fh_del(&ctx->fh);
> +       v4l2_fh_exit(&ctx->fh);
> +       kfree(ctx);
> +err_leave:
> +       vpu_debug_leave();
> +
> +       return ret;
> +}
> +
> +static int rockchip_vpu_release(struct file *filp)
> +{
> +       struct rockchip_vpu_ctx *ctx = fh_to_ctx(filp->private_data);
> +       struct video_device *vdev = video_devdata(filp);
> +       struct rockchip_vpu_dev *dev = ctx->dev;
> +
> +       /*
> +        * No need for extra locking because this was the last reference
> +        * to this file.
> +        */
> +
> +       vpu_debug_enter();
> +
> +       /*
> +        * vb2_queue_release() ensures that streaming is stopped, which
> +        * in turn means that there are no frames still being processed
> +        * by hardware.
> +        */
> +       vb2_queue_release(&ctx->vq_src);
> +       vb2_queue_release(&ctx->vq_dst);
> +
> +       v4l2_fh_del(&ctx->fh);
> +       v4l2_fh_exit(&ctx->fh);
> +
> +       if (vdev == dev->vfd_dec)
> +               rockchip_vpu_dec_exit(ctx);
> +
> +       kfree(ctx);
> +
> +       vpu_debug_leave();
> +
> +       return 0;
> +}
> +
> +static unsigned int rockchip_vpu_poll(struct file *filp,
> +                                     struct poll_table_struct *wait)
> +{
> +       struct rockchip_vpu_ctx *ctx = fh_to_ctx(filp->private_data);
> +       struct vb2_queue *src_q, *dst_q;
> +       struct vb2_buffer *src_vb = NULL, *dst_vb = NULL;
> +       unsigned int rc = 0;
> +       unsigned long flags;
> +
> +       vpu_debug_enter();
> +
> +       src_q = &ctx->vq_src;
> +       dst_q = &ctx->vq_dst;
> +
> +       /*
> +        * There has to be at least one buffer queued on each queued_list, which
> +        * means either in driver already or waiting for driver to claim it
> +        * and start processing.
> +        */
> +       if ((!vb2_is_streaming(src_q) || list_empty(&src_q->queued_list)) &&
> +           (!vb2_is_streaming(dst_q) || list_empty(&dst_q->queued_list))) {
> +               vpu_debug(0, "src q streaming %d, dst q streaming %d, src list empty(%d), dst list empty(%d)\n",
> +                         src_q->streaming, dst_q->streaming,
> +                         list_empty(&src_q->queued_list),
> +                         list_empty(&dst_q->queued_list));
> +               return POLLERR;
> +       }
> +
> +       poll_wait(filp, &ctx->fh.wait, wait);
> +       poll_wait(filp, &src_q->done_wq, wait);
> +       poll_wait(filp, &dst_q->done_wq, wait);
> +
> +       if (v4l2_event_pending(&ctx->fh))
> +               rc |= POLLPRI;
> +
> +       spin_lock_irqsave(&src_q->done_lock, flags);
> +
> +       if (!list_empty(&src_q->done_list))
> +               src_vb = list_first_entry(&src_q->done_list, struct vb2_buffer,
> +                                         done_entry);
> +
> +       if (src_vb && (src_vb->state == VB2_BUF_STATE_DONE ||
> +                      src_vb->state == VB2_BUF_STATE_ERROR))
> +               rc |= POLLOUT | POLLWRNORM;
> +
> +       spin_unlock_irqrestore(&src_q->done_lock, flags);
> +
> +       spin_lock_irqsave(&dst_q->done_lock, flags);
> +
> +       if (!list_empty(&dst_q->done_list))
> +               dst_vb = list_first_entry(&dst_q->done_list, struct vb2_buffer,
> +                                         done_entry);
> +
> +       if (dst_vb && (dst_vb->state == VB2_BUF_STATE_DONE ||
> +                      dst_vb->state == VB2_BUF_STATE_ERROR))
> +               rc |= POLLIN | POLLRDNORM;
> +
> +       spin_unlock_irqrestore(&dst_q->done_lock, flags);
> +
> +       return rc;
> +}
> +
> +static int rockchip_vpu_mmap(struct file *filp, struct vm_area_struct *vma)
> +{
> +       struct rockchip_vpu_ctx *ctx = fh_to_ctx(filp->private_data);
> +       unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
> +       int ret;
> +
> +       vpu_debug_enter();
> +
> +       if (offset < DST_QUEUE_OFF_BASE) {
> +               vpu_debug(4, "mmaping source\n");
> +
> +               ret = vb2_mmap(&ctx->vq_src, vma);
> +       } else {        /* capture */
> +               vpu_debug(4, "mmaping destination\n");
> +
> +               vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT);
> +               ret = vb2_mmap(&ctx->vq_dst, vma);
> +       }
> +
> +       vpu_debug_leave();
> +
> +       return ret;
> +}
> +
> +static const struct v4l2_file_operations rockchip_vpu_fops = {
> +       .owner = THIS_MODULE,
> +       .open = rockchip_vpu_open,
> +       .release = rockchip_vpu_release,
> +       .poll = rockchip_vpu_poll,
> +       .unlocked_ioctl = video_ioctl2,
> +       .mmap = rockchip_vpu_mmap,
> +};
> +
> +/*
> + * Platform driver.
> + */
> +
> +static void *rockchip_get_drv_data(struct platform_device *pdev);
> +
> +static int rockchip_vpu_probe(struct platform_device *pdev)
> +{
> +       struct rockchip_vpu_dev *vpu = NULL;
> +       DEFINE_DMA_ATTRS(attrs_novm);
> +       struct video_device *vfd;
> +       int ret = 0;
> +
> +       vpu_debug_enter();
> +
> +       vpu = devm_kzalloc(&pdev->dev, sizeof(*vpu), GFP_KERNEL);
> +       if (!vpu)
> +               return -ENOMEM;
> +
> +       vpu->dev = &pdev->dev;
> +       vpu->pdev = pdev;
> +       mutex_init(&vpu->vpu_mutex);
> +       spin_lock_init(&vpu->irqlock);
> +       INIT_LIST_HEAD(&vpu->ready_ctxs);
> +       init_waitqueue_head(&vpu->run_wq);
> +
> +       vpu->variant = rockchip_get_drv_data(pdev);
> +
> +       ret = rockchip_vpu_hw_probe(vpu);
> +       if (ret) {
> +               dev_err(&pdev->dev, "rockchip_vpu_hw_probe failed\n");
> +               goto err_hw_probe;
> +       }
> +
> +       dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &attrs_novm);
> +       vpu->alloc_ctx = vb2_dma_contig_init_ctx_attrs(&pdev->dev,
> +                                                      &attrs_novm);
> +       if (IS_ERR(vpu->alloc_ctx)) {
> +               ret = PTR_ERR(vpu->alloc_ctx);
> +               goto err_dma_contig;
> +       }
> +
> +       vpu->alloc_ctx_vm = vb2_dma_contig_init_ctx(&pdev->dev);
> +       if (IS_ERR(vpu->alloc_ctx_vm)) {
> +               ret = PTR_ERR(vpu->alloc_ctx_vm);
> +               goto err_dma_contig_vm;
> +       }
> +
> +       ret = v4l2_device_register(&pdev->dev, &vpu->v4l2_dev);
> +       if (ret) {
> +               dev_err(&pdev->dev, "Failed to register v4l2 device\n");
> +               goto err_v4l2_dev_reg;
> +       }
> +
> +       platform_set_drvdata(pdev, vpu);
> +
> +       /* decoder */
> +       vfd = video_device_alloc();
> +       if (!vfd) {
> +               v4l2_err(&vpu->v4l2_dev, "Failed to allocate video device\n");
> +               ret = -ENOMEM;
> +               goto err_v4l2_dev_reg;
> +       }
> +
> +       vfd->fops = &rockchip_vpu_fops;
> +       vfd->ioctl_ops = get_dec_v4l2_ioctl_ops();
> +       vfd->release = video_device_release;
> +       vfd->lock = &vpu->vpu_mutex;
> +       vfd->v4l2_dev = &vpu->v4l2_dev;
> +       vfd->vfl_dir = VFL_DIR_M2M;
> +       snprintf(vfd->name, sizeof(vfd->name), "%s", ROCKCHIP_VPU_DEC_NAME);
> +       vpu->vfd_dec = vfd;
> +
> +       video_set_drvdata(vfd, vpu);
> +
> +       ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
> +       if (ret) {
> +               v4l2_err(&vpu->v4l2_dev, "Failed to register video device\n");
> +               video_device_release(vfd);
> +               goto err_dec_reg;
> +       }
> +
> +       v4l2_info(&vpu->v4l2_dev,
> +                 "Rockchip VPU decoder registered as /vpu/video%d\n",
> +                 vfd->num);
> +
> +       vpu_debug_leave();
> +
> +       return 0;
> +
> +err_dec_reg:
> +       video_device_release(vpu->vfd_dec);
> +err_v4l2_dev_reg:
> +       vb2_dma_contig_cleanup_ctx(vpu->alloc_ctx_vm);
> +err_dma_contig_vm:
> +       vb2_dma_contig_cleanup_ctx(vpu->alloc_ctx);
> +err_dma_contig:
> +       rockchip_vpu_hw_remove(vpu);
> +err_hw_probe:
> +       pr_debug("%s-- with error\n", __func__);
> +       vpu_debug_leave();
> +
> +       return ret;
> +}
> +
> +static int rockchip_vpu_remove(struct platform_device *pdev)
> +{
> +       struct rockchip_vpu_dev *vpu = platform_get_drvdata(pdev);
> +
> +       vpu_debug_enter();
> +
> +       v4l2_info(&vpu->v4l2_dev, "Removing %s\n", pdev->name);
> +
> +       /*
> +        * We are safe here assuming that .remove() got called as
> +        * a result of module removal, which guarantees that all
> +        * contexts have been released.
> +        */
> +
> +       video_unregister_device(vpu->vfd_dec);
> +       v4l2_device_unregister(&vpu->v4l2_dev);
> +       vb2_dma_contig_cleanup_ctx(vpu->alloc_ctx_vm);
> +       vb2_dma_contig_cleanup_ctx(vpu->alloc_ctx);
> +       rockchip_vpu_hw_remove(vpu);
> +
> +       vpu_debug_leave();
> +
> +       return 0;
> +}
> +
> +/* Supported VPU variants. */
> +static const struct rockchip_vpu_variant rk3288_vpu_variant = {
> +       .vpu_type = RK3288_VPU,
> +       .name = "Rk3288 vpu",
> +       .dec_offset = 0x400,
> +       .dec_reg_num = 60 + 41,
> +};
> +
> +static const struct rockchip_vpu_variant rk3229_vpu_variant = {
> +       .vpu_type = RK3229_VPU,
> +       .name = "Rk3229 vpu",
> +       .dec_offset = 0x400,
> +       .dec_reg_num = 159,
> +};
> +
> +static struct platform_device_id vpu_driver_ids[] = {
> +       {
> +               .name = "rk3288-vpu",
> +               .driver_data = (unsigned long)&rk3288_vpu_variant,
> +       }, {
> +               .name = "rk3229-vpu",
> +               .driver_data = (unsigned long)&rk3229_vpu_variant,
> +       },
> +       { /* sentinel */ }
> +};
> +
> +MODULE_DEVICE_TABLE(platform, vpu_driver_ids);
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id of_rockchip_vpu_match[] = {
> +       { .compatible = "rockchip,rk3288-vpu", .data = &rk3288_vpu_variant, },
> +       { .compatible = "rockchip,rk3229-vpu", .data = &rk3229_vpu_variant, },
> +       { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, of_rockchip_vpu_match);
> +#endif
> +
> +static void *rockchip_get_drv_data(struct platform_device *pdev)
> +{
> +       void *driver_data = NULL;
> +
> +       if (pdev->dev.of_node) {
> +               const struct of_device_id *match;
> +
> +               match = of_match_node(of_rockchip_vpu_match,
> +                                     pdev->dev.of_node);
> +               if (match)
> +                       driver_data = (void *)match->data;
> +       } else {
> +               driver_data = (void *)platform_get_device_id(pdev)->driver_data;
> +       }
> +       return driver_data;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int rockchip_vpu_suspend(struct device *dev)
> +{
> +       struct rockchip_vpu_dev *vpu = dev_get_drvdata(dev);
> +
> +       set_bit(VPU_SUSPENDED, &vpu->state);
> +       wait_event(vpu->run_wq, vpu->current_ctx == NULL);
> +
> +       return 0;
> +}
> +
> +static int rockchip_vpu_resume(struct device *dev)
> +{
> +       struct rockchip_vpu_dev *vpu = dev_get_drvdata(dev);
> +
> +       clear_bit(VPU_SUSPENDED, &vpu->state);
> +       rockchip_vpu_try_run(vpu);
> +
> +       return 0;
> +}
> +#endif
> +
> +static const struct dev_pm_ops rockchip_vpu_pm_ops = {
> +       SET_SYSTEM_SLEEP_PM_OPS(rockchip_vpu_suspend, rockchip_vpu_resume)
> +};
> +
> +static struct platform_driver rockchip_vpu_driver = {
> +       .probe = rockchip_vpu_probe,
> +       .remove = rockchip_vpu_remove,
> +       .id_table = vpu_driver_ids,
> +       .driver = {
> +               .name = ROCKCHIP_VPU_NAME,
> +               .owner = THIS_MODULE,
> +#ifdef CONFIG_OF

I think this is  not required, of_match_ptr is already protected when !OF

> +               .of_match_table = of_match_ptr(of_rockchip_vpu_match),
> +#endif
> +               .pm = &rockchip_vpu_pm_ops,
> +       },
> +};
> +module_platform_driver(rockchip_vpu_driver);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_AUTHOR("Jung Zhao <jung.zhao at rock-chips.com>");
> +MODULE_AUTHOR("Alpha Lin <Alpha.Lin at Rock-Chips.com>");
> +MODULE_AUTHOR("Tomasz Figa <tfiga at chromium.org>");
> +MODULE_DESCRIPTION("Rockchip VPU codec driver");
> diff --git a/drivers/media/platform/rockchip-vpu/rockchip_vpu_common.h b/drivers/media/platform/rockchip-vpu/rockchip_vpu_common.h
> new file mode 100644
> index 0000000..0d77304
> --- /dev/null
> +++ b/drivers/media/platform/rockchip-vpu/rockchip_vpu_common.h
> @@ -0,0 +1,439 @@
> +/*
> + * Rockchip VPU codec driver
> + *
> + * Copyright (C) 2014 Google, Inc.
> + *     Tomasz Figa <tfiga at chromium.org>
> + *
> + * Based on s5p-mfc driver by Samsung Electronics Co., Ltd.
> + *
> + * Copyright (C) 2011 Samsung Electronics Co., Ltd.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef ROCKCHIP_VPU_COMMON_H_
> +#define ROCKCHIP_VPU_COMMON_H_
> +
> +/* Enable debugging by default for now. */
> +#define DEBUG
> +
> +#include <linux/platform_device.h>
> +#include <linux/videodev2.h>
> +#include <linux/wait.h>
> +
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/videobuf2-core.h>
> +#include <media/videobuf2-dma-contig.h>
> +
> +#include "rockchip_vpu_hw.h"
> +
> +#define ROCKCHIP_VPU_NAME              "rockchip-vpu"
> +#define ROCKCHIP_VPU_DEC_NAME          "rockchip-vpu-dec"
> +
> +#define V4L2_CID_CUSTOM_BASE           (V4L2_CID_USER_BASE | 0x1000)
> +
> +#define DST_QUEUE_OFF_BASE             (TASK_SIZE / 2)
> +
> +#define ROCKCHIP_VPU_MAX_CTRLS         32
> +
> +#define MB_DIM                         16
> +#define MB_WIDTH(x_size)               DIV_ROUND_UP(x_size, MB_DIM)
> +#define MB_HEIGHT(y_size)              DIV_ROUND_UP(y_size, MB_DIM)
> +
> +struct rockchip_vpu_ctx;
> +struct rockchip_vpu_codec_ops;
> +
> +/**
> + * struct rockchip_vpu_variant - information about VPU hardware variant
> + *
> + * @hw_id:             Top 16 bits (product ID) of hardware ID register.
> + * @vpu_type:          Vpu type.
> + * @name:              Vpu name.
> + * @dec_offset:                Offset from VPU base to decoder registers.
> + * @dec_reg_num:       Number of registers of decoder block.
> + */
> +struct rockchip_vpu_variant {
> +       enum rockchip_vpu_type vpu_type;
> +       char *name;
> +       unsigned dec_offset;
> +       unsigned dec_reg_num;
> +};
> +
> +/**
> + * enum rockchip_vpu_codec_mode - codec operating mode.
> + * @RK_VPU_CODEC_NONE: No operating mode. Used for RAW video formats.
> + * @RK3288_VPU_CODEC_VP8D:     Rk3288 VP8 decoder.
> + * @RK3229_VPU_CODEC_VP8D:     Rk3229 VP8 decoder.
> + */
> +enum rockchip_vpu_codec_mode {
> +       RK_VPU_CODEC_NONE = -1,
> +       RK3288_VPU_CODEC_VP8D,
> +       RK3229_VPU_CODEC_VP8D,
> +};
> +
> +/**
> + * enum rockchip_vpu_plane - indices of planes inside a VB2 buffer.
> + * @PLANE_Y:           Plane containing luminance data (also denoted as Y).
> + * @PLANE_CB_CR:       Plane containing interleaved chrominance data (also
> + *                     denoted as CbCr).
> + * @PLANE_CB:          Plane containing CB part of chrominance data.
> + * @PLANE_CR:          Plane containing CR part of chrominance data.
> + */
> +enum rockchip_vpu_plane {
> +       PLANE_Y         = 0,
> +       PLANE_CB_CR     = 1,
> +       PLANE_CB        = 1,
> +       PLANE_CR        = 2,
> +};
> +
> +/**
> + * struct rockchip_vpu_buf - Private data related to each VB2 buffer.
> + * @vb:                        Pointer to related VB2 buffer.
> + * @list:              List head for queuing in buffer queue.
> + * @flags:             Buffer state. See enum rockchip_vpu_buf_flags.
> + */
> +struct rockchip_vpu_buf {
> +       struct vb2_v4l2_buffer b;
> +       struct list_head list;
> +       /* Mode-specific data. */
> +};
> +
> +/**
> + * enum rockchip_vpu_state - bitwise flags indicating hardware state.
> + * @VPU_RUNNING:       The hardware has been programmed for operation
> + *                     and is running at the moment.
> + * @VPU_SUSPENDED:     System is entering sleep state and no more runs
> + *                     should be executed on hardware.
> + */
> +enum rockchip_vpu_state {
> +       VPU_RUNNING     = BIT(0),
> +       VPU_SUSPENDED   = BIT(1),
> +};
> +
> +/**
> + * struct rockchip_vpu_dev - driver data
> + * @v4l2_dev:          V4L2 device to register video devices for.
> + * @vfd_dec:           Video device for decoder.
> + * @pdev:              Pointer to VPU platform device.
> + * @dev:               Pointer to device for convenient logging using
> + *                     dev_ macros.
> + * @alloc_ctx:         VB2 allocator context
> + *                     (for allocations without kernel mapping).
> + * @alloc_ctx_vm:      VB2 allocator context
> + *                     (for allocations with kernel mapping).
> + * @aclk:              Handle of ACLK clock.
> + * @hclk:              Handle of HCLK clock.
> + * @base:              Mapped address of VPU registers.
> + * @dec_base:          Mapped address of VPU decoder register for convenience.
> + * @mapping:           DMA IOMMU mapping.
> + * @vpu_mutex:         Mutex to synchronize V4L2 calls.
> + * @irqlock:           Spinlock to synchronize access to data structures
> + *                     shared with interrupt handlers.
> + * @state:             Device state.
> + * @ready_ctxs:                List of contexts ready to run.
> + * @variant:           Hardware variant-specific parameters.
> + * @current_ctx:       Context being currently processed by hardware.
> + * @run_wq:            Wait queue to wait for run completion.
> + * @watchdog_work:     Delayed work for hardware timeout handling.
> + */
> +struct rockchip_vpu_dev {
> +       struct v4l2_device v4l2_dev;
> +       struct video_device *vfd_dec;
> +       struct platform_device *pdev;
> +       struct device *dev;
> +       void *alloc_ctx;
> +       void *alloc_ctx_vm;
> +       struct clk *aclk;
> +       struct clk *hclk;
> +       void __iomem *base;
> +       void __iomem *dec_base;
> +       struct dma_iommu_mapping *mapping;
> +
> +       struct mutex vpu_mutex; /* video_device lock */
> +       spinlock_t irqlock;
> +       unsigned long state;
> +       struct list_head ready_ctxs;
> +       const struct rockchip_vpu_variant *variant;
> +       struct rockchip_vpu_ctx *current_ctx;
> +       wait_queue_head_t run_wq;
> +       struct delayed_work watchdog_work;
> +};
> +
> +/**
> + * struct rockchip_vpu_run_ops - per context operations on run data.
> + * @prepare_run:       Called when the context was selected for running
> + *                     to prepare operating mode specific data.
> + * @run_done:          Called when hardware completed the run to collect
> + *                     operating mode specific data from hardware and
> + *                     finalize the processing.
> + */
> +struct rockchip_vpu_run_ops {
> +       void (*prepare_run)(struct rockchip_vpu_ctx *);
> +       void (*run_done)(struct rockchip_vpu_ctx *, enum vb2_buffer_state);
> +};
> +
> +/**
> + * struct rockchip_vpu_vp8d_run - per-run data specific to VP8
> + * decoding.
> + * @frame_hdr: Pointer to a buffer containing per-run frame data which
> + *                     is needed by setting vpu register.
> + */
> +struct rockchip_vpu_vp8d_run {
> +       const struct v4l2_ctrl_vp8_frame_hdr *frame_hdr;
> +};
> +
> +/**
> + * struct rockchip_vpu_run - per-run data for hardware code.
> + * @src:               Source buffer to be processed.
> + * @dst:               Destination buffer to be processed.
> + * @priv_src:          Hardware private source buffer.
> + * @priv_dst:          Hardware private destination buffer.
> + */
> +struct rockchip_vpu_run {
> +       /* Generic for more than one operating mode. */
> +       struct rockchip_vpu_buf *src;
> +       struct rockchip_vpu_buf *dst;
> +
> +       struct rockchip_vpu_aux_buf priv_src;
> +       struct rockchip_vpu_aux_buf priv_dst;
> +
> +       /* Specific for particular operating modes. */
> +       union {
> +               struct rockchip_vpu_vp8d_run vp8d;
> +               /* Other modes will need different data. */
> +       };
> +};
> +
> +/**
> + * struct rockchip_vpu_ctx - Context (instance) private data.
> + *
> + * @dev:               VPU driver data to which the context belongs.
> + * @fh:                        V4L2 file handler.
> + *
> + * @vpu_src_fmt:       Descriptor of active source format.
> + * @src_fmt:           V4L2 pixel format of active source format.
> + * @vpu_dst_fmt:       Descriptor of active destination format.
> + * @dst_fmt:           V4L2 pixel format of active destination format.
> + *
> + * @vq_src:            Videobuf2 source queue.
> + * @src_queue:         Internal source buffer queue.
> + * @src_crop:          Configured source crop rectangle (encoder-only).
> + * @vq_dst:            Videobuf2 destination queue
> + * @dst_queue:         Internal destination buffer queue.
> + * @dst_bufs:          Private buffers wrapping VB2 buffers (destination).
> + *
> + * @ctrls:             Array containing pointer to registered controls.
> + * @ctrl_handler:      Control handler used to register controls.
> + * @num_ctrls:         Number of registered controls.
> + *
> + * @list:              List head for queue of ready contexts.
> + *
> + * @run:               Structure containing data about currently scheduled
> + *                     processing run.
> + * @run_ops:           Set of operations related to currently scheduled run.
> + * @hw:                        Structure containing hardware-related context.
> + */
> +struct rockchip_vpu_ctx {
> +       struct rockchip_vpu_dev *dev;
> +       struct v4l2_fh fh;
> +
> +       /* Format info */
> +       struct rockchip_vpu_fmt *vpu_src_fmt;
> +       struct v4l2_pix_format_mplane src_fmt;
> +       struct rockchip_vpu_fmt *vpu_dst_fmt;
> +       struct v4l2_pix_format_mplane dst_fmt;
> +
> +       /* VB2 queue data */
> +       struct vb2_queue vq_src;
> +       struct list_head src_queue;
> +       struct v4l2_rect src_crop;
> +       struct vb2_queue vq_dst;
> +       struct list_head dst_queue;
> +       struct vb2_buffer *dst_bufs[VIDEO_MAX_FRAME];
> +
> +       /* Controls */
> +       struct v4l2_ctrl *ctrls[ROCKCHIP_VPU_MAX_CTRLS];
> +       struct v4l2_ctrl_handler ctrl_handler;
> +       unsigned num_ctrls;
> +
> +       /* Various runtime data */
> +       struct list_head list;
> +
> +       struct rockchip_vpu_run run;
> +       const struct rockchip_vpu_run_ops *run_ops;
> +       struct rockchip_vpu_hw_ctx hw;
> +};
> +
> +/**
> + * struct rockchip_vpu_fmt - information about supported video formats.
> + * @name:      Human readable name of the format.
> + * @fourcc:    FourCC code of the format. See V4L2_PIX_FMT_*.
> + * @vpu_type:  Vpu_type;
> + * @codec_mode:        Codec mode related to this format. See
> + *             enum rockchip_vpu_codec_mode.
> + * @num_planes:        Number of planes used by this format.
> + * @depth:     Depth of each plane in bits per pixel.
> + */
> +struct rockchip_vpu_fmt {
> +       char *name;
> +       u32 fourcc;
> +       enum rockchip_vpu_type vpu_type;
> +       enum rockchip_vpu_codec_mode codec_mode;
> +       int num_planes;
> +       u8 depth[VIDEO_MAX_PLANES];
> +};
> +
> +/**
> + * struct rockchip_vpu_control - information about controls to be registered.
> + * @id:                        Control ID.
> + * @type:              Type of the control.
> + * @name:              Human readable name of the control.
> + * @minimum:           Minimum value of the control.
> + * @maximum:           Maximum value of the control.
> + * @step:              Control value increase step.
> + * @menu_skip_mask:    Mask of invalid menu positions.
> + * @default_value:     Initial value of the control.
> + * @max_reqs:          Maximum number of configration request.
> + * @dims:              Size of each dimension of compound control.
> + * @elem_size:         Size of individual element of compound control.
> + * @is_volatile:       Control is volatile.
> + * @is_read_only:      Control is read-only.
> + * @can_store:         Control uses configuration stores.
> + *
> + * See also struct v4l2_ctrl_config.
> + */
> +struct rockchip_vpu_control {
> +       u32 id;
> +
> +       enum v4l2_ctrl_type type;
> +       const char *name;
> +       s32 minimum;
> +       s32 maximum;
> +       s32 step;
> +       u32 menu_skip_mask;
> +       s32 default_value;
> +       s32 max_reqs;
> +       u32 dims[V4L2_CTRL_MAX_DIMS];
> +       u32 elem_size;
> +
> +       bool is_volatile:1;
> +       bool is_read_only:1;
> +       bool can_store:1;
> +};
> +
> +/* Logging helpers */
> +
> +/**
> + * debug - Module parameter to control level of debugging messages.
> + *
> + * Level of debugging messages can be controlled by bits of module parameter
> + * called "debug". Meaning of particular bits is as follows:
> + *
> + * bit 0 - global information: mode, size, init, release
> + * bit 1 - each run start/result information
> + * bit 2 - contents of small controls from userspace
> + * bit 3 - contents of big controls from userspace
> + * bit 4 - detail fmt, ctrl, buffer q/dq information
> + * bit 5 - detail function enter/leave trace information
> + * bit 6 - register write/read information
> + */
> +extern int debug;
> +
> +#define vpu_debug(level, fmt, args...)                         \
> +       do {                                                    \
> +               if (debug & BIT(level))                         \
> +                       pr_err("%s:%d: " fmt,                   \
> +                              __func__, __LINE__, ##args);     \
> +       } while (0)
> +
> +#define vpu_debug_enter()      vpu_debug(5, "enter\n")
> +#define vpu_debug_leave()      vpu_debug(5, "leave\n")
> +

Remove these

> +#define vpu_err(fmt, args...)                                  \
> +       pr_err("%s:%d: " fmt, __func__, __LINE__, ##args)
> +
> +static inline char *fmt2str(u32 fmt, char *str)
> +{
> +       char a = fmt & 0xFF;
> +       char b = (fmt >> 8) & 0xFF;
> +       char c = (fmt >> 16) & 0xFF;
> +       char d = (fmt >> 24) & 0xFF;
> +
> +       sprintf(str, "%c%c%c%c", a, b, c, d);
> +
> +       return str;
> +}
> +
> +/* Structure access helpers. */
> +static inline struct rockchip_vpu_ctx *fh_to_ctx(struct v4l2_fh *fh)
> +{
> +       return container_of(fh, struct rockchip_vpu_ctx, fh);
> +}
> +
> +static inline struct rockchip_vpu_ctx *ctrl_to_ctx(struct v4l2_ctrl *ctrl)
> +{
> +       return container_of(ctrl->handler,
> +                           struct rockchip_vpu_ctx, ctrl_handler);
> +}
> +
> +static inline struct rockchip_vpu_buf *vb_to_buf(struct vb2_buffer *vb)
> +{
> +       return container_of(to_vb2_v4l2_buffer(vb), struct rockchip_vpu_buf, b);
> +}
> +
> +static inline bool rockchip_vpu_ctx_is_encoder(struct rockchip_vpu_ctx *ctx)
> +{
> +       return ctx->vpu_dst_fmt->codec_mode != RK_VPU_CODEC_NONE;
> +}
> +
> +int rockchip_vpu_ctrls_setup(struct rockchip_vpu_ctx *ctx,
> +                            const struct v4l2_ctrl_ops *ctrl_ops,
> +                            struct rockchip_vpu_control *controls,
> +                            unsigned num_ctrls,
> +                            const char* const* (*get_menu)(u32));
> +void rockchip_vpu_ctrls_delete(struct rockchip_vpu_ctx *ctx);
> +
> +void rockchip_vpu_try_context(struct rockchip_vpu_dev *dev,
> +                             struct rockchip_vpu_ctx *ctx);
> +
> +void rockchip_vpu_run_done(struct rockchip_vpu_ctx *ctx,
> +                          enum vb2_buffer_state result);
> +
> +int rockchip_vpu_aux_buf_alloc(struct rockchip_vpu_dev *vpu,
> +                              struct rockchip_vpu_aux_buf *buf, size_t size);
> +void rockchip_vpu_aux_buf_free(struct rockchip_vpu_dev *vpu,
> +                              struct rockchip_vpu_aux_buf *buf);
> +
> +static inline void vdpu_write_relaxed(struct rockchip_vpu_dev *vpu,
> +                                     u32 val, u32 reg)
> +{
> +       vpu_debug(6, "MARK: set reg[%03d]: %08x\n", reg / 4, val);
> +       writel_relaxed(val, vpu->dec_base + reg);
> +}
> +
> +static inline void vdpu_write(struct rockchip_vpu_dev *vpu, u32 val, u32 reg)
> +{
> +       vpu_debug(6, "MARK: set reg[%03d]: %08x\n", reg / 4, val);
> +       writel(val, vpu->dec_base + reg);
> +}
> +
> +static inline u32 vdpu_read(struct rockchip_vpu_dev *vpu, u32 reg)
> +{
> +       u32 val = readl(vpu->dec_base + reg);
> +
> +       vpu_debug(6, "MARK: get reg[%03d]: %08x\n", reg / 4, val);
> +       return val;
> +}
> +
> +int rockchip_vpu_write(const char *file, void *buf, size_t size);
> +
> +#endif /* ROCKCHIP_VPU_COMMON_H_ */
> diff --git a/drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.c b/drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.c
> new file mode 100644
> index 0000000..33e9a89
> --- /dev/null
> +++ b/drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.c
> @@ -0,0 +1,994 @@
> +/*
> + * Rockchip VPU codec driver
> + *
> + * Copyright (C) 2014 Rockchip Electronics Co., Ltd.
> + *     Hertz Wong <hertz.wong at rock-chips.com>
> + *
> + * Copyright (C) 2014 Google, Inc.
> + *     Tomasz Figa <tfiga at chromium.org>
> + *
> + * Based on s5p-mfc driver by Samsung Electronics Co., Ltd.
> + *
> + * Copyright (C) 2010-2011 Samsung Electronics Co., Ltd.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include "rockchip_vpu_common.h"
> +
> +#include <linux/module.h>
> +#include <linux/version.h>
> +#include <linux/videodev2.h>
> +
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-event.h>
> +#include <media/videobuf2-core.h>
> +#include <media/videobuf2-dma-sg.h>
> +
> +#include "rockchip_vpu_dec.h"
> +#include "rockchip_vpu_hw.h"
> +
> +#define DEF_SRC_FMT_DEC                                V4L2_PIX_FMT_H264_SLICE
> +#define DEF_DST_FMT_DEC                                V4L2_PIX_FMT_NV12
> +
> +#define ROCKCHIP_DEC_MIN_WIDTH                 48U
> +#define ROCKCHIP_DEC_MAX_WIDTH                 3840U
> +#define ROCKCHIP_DEC_MIN_HEIGHT                        48U
> +#define ROCKCHIP_DEC_MAX_HEIGHT                        2160U
> +
> +static struct rockchip_vpu_fmt formats[] = {
> +       {
> +               .name = "4:2:0 1 plane Y/CbCr",
> +               .fourcc = V4L2_PIX_FMT_NV12,
> +               .vpu_type = RK_VPU_NONE,
> +               .codec_mode = RK_VPU_CODEC_NONE,
> +               .num_planes = 1,
> +               .depth = { 12 },
> +       },
> +       {
> +               .name = "Frames of VP8 Decoded Stream",
> +               .fourcc = V4L2_PIX_FMT_VP8_FRAME,
> +               .vpu_type = RK3288_VPU,
> +               .codec_mode = RK3288_VPU_CODEC_VP8D,
> +               .num_planes = 1,
> +       },
> +       {
> +               .name = "Frames of VP8 Decoded Stream",
> +               .fourcc = V4L2_PIX_FMT_VP8_FRAME,
> +               .vpu_type = RK3229_VPU,
> +               .codec_mode = RK3229_VPU_CODEC_VP8D,
> +               .num_planes = 1,
> +       },
> +};
> +
> +static struct rockchip_vpu_fmt *find_format(u32 fourcc, bool bitstream,
> +                                           struct rockchip_vpu_dev *dev)
> +{
> +       unsigned int i;
> +
> +       vpu_debug_enter();
> +
> +       for (i = 0; i < ARRAY_SIZE(formats); i++) {
> +               if (formats[i].fourcc != fourcc)
> +                       continue;
> +               if (bitstream && formats[i].codec_mode != RK_VPU_CODEC_NONE
> +                   && formats[i].vpu_type == dev->variant->vpu_type)
> +                       return &formats[i];
> +               if (!bitstream && formats[i].codec_mode == RK_VPU_CODEC_NONE)
> +                       return &formats[i];
> +       }
> +
> +       return NULL;
> +}
> +
> +/* Indices of controls that need to be accessed directly. */
> +enum {
> +       ROCKCHIP_VPU_DEC_CTRL_VP8_FRAME_HDR,
> +};
> +
> +static struct rockchip_vpu_control controls[0];
> +
> +static inline const void *get_ctrl_ptr(struct rockchip_vpu_ctx *ctx,
> +                                      unsigned id)
> +{
> +       struct v4l2_ctrl *ctrl = ctx->ctrls[id];
> +
> +       return ctrl->p_cur.p;
> +}
> +
> +/* Query capabilities of the device */
> +static int vidioc_querycap(struct file *file, void *priv,
> +                          struct v4l2_capability *cap)
> +{
> +       struct rockchip_vpu_dev *dev = video_drvdata(file);
> +
> +       vpu_debug_enter();
> +
> +       strlcpy(cap->driver, ROCKCHIP_VPU_DEC_NAME, sizeof(cap->driver));
> +       strlcpy(cap->card, dev->pdev->name, sizeof(cap->card));
> +       strlcpy(cap->bus_info, "platform:" ROCKCHIP_VPU_NAME,
> +               sizeof(cap->bus_info));
> +
> +       /*
> +        * This is only a mem-to-mem video device. The capture and output
> +        * device capability flags are left only for backward compatibility
> +        * and are scheduled for removal.
> +        */
> +       cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING |
> +           V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
> +       cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
> +
> +       vpu_debug_leave();
> +
> +       return 0;
> +}
> +
> +static int vidioc_enum_framesizes(struct file *file, void *prov,
> +                                 struct v4l2_frmsizeenum *fsize)
> +{
> +       struct rockchip_vpu_dev *dev = video_drvdata(file);
> +       struct v4l2_frmsize_stepwise *s = &fsize->stepwise;
> +       struct rockchip_vpu_fmt *fmt;
> +
> +       if (fsize->index != 0) {
> +               vpu_debug(0, "invalid frame size index (expected 0, got %d)\n",
> +                               fsize->index);
> +               return -EINVAL;
> +       }
> +
> +       fmt = find_format(fsize->pixel_format, true, dev);
> +       if (!fmt) {
> +               vpu_debug(0, "unsupported bitstream format (%08x)\n",
> +                               fsize->pixel_format);
> +               return -EINVAL;
> +       }
> +
> +       fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
> +
> +       s->min_width = ROCKCHIP_DEC_MIN_WIDTH;
> +       s->max_width = ROCKCHIP_DEC_MAX_WIDTH;
> +       s->step_width = MB_DIM;
> +       s->min_height = ROCKCHIP_DEC_MIN_HEIGHT;
> +       s->max_height = ROCKCHIP_DEC_MAX_HEIGHT;
> +       s->step_height = MB_DIM;
> +
> +       return 0;
> +}
> +
> +static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool out,
> +                          struct rockchip_vpu_dev *dev)
> +{
> +       struct rockchip_vpu_fmt *fmt;
> +       int i, j = 0;
> +
> +       vpu_debug_enter();
> +
> +       for (i = 0; i < ARRAY_SIZE(formats); ++i) {
> +               if (out && formats[i].codec_mode == RK_VPU_CODEC_NONE)
> +                       continue;
> +               else if (!out && (formats[i].codec_mode != RK_VPU_CODEC_NONE))
> +                       continue;
> +               else if (formats[i].vpu_type != dev->variant->vpu_type &&
> +                        formats[i].vpu_type != RK_VPU_NONE)
> +                       continue;
> +
> +               if (j == f->index) {
> +                       fmt = &formats[i];
> +                       strlcpy(f->description, fmt->name,
> +                               sizeof(f->description));
> +                       f->pixelformat = fmt->fourcc;
> +
> +                       f->flags = 0;
> +                       if (formats[i].codec_mode != RK_VPU_CODEC_NONE)
> +                               f->flags |= V4L2_FMT_FLAG_COMPRESSED;
> +
> +                       vpu_debug_leave();
> +
> +                       return 0;
> +               }
> +
> +               ++j;
> +       }
> +
> +       vpu_debug_leave();
> +
> +       return -EINVAL;
> +}
> +
> +static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *priv,
> +                                         struct v4l2_fmtdesc *f)
> +{
> +       struct rockchip_vpu_dev *dev = video_drvdata(file);
> +
> +       return vidioc_enum_fmt(f, false, dev);
> +}
> +
> +static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *priv,
> +                                         struct v4l2_fmtdesc *f)
> +{
> +       struct rockchip_vpu_dev *dev = video_drvdata(file);
> +
> +       return vidioc_enum_fmt(f, true, dev);
> +}
> +
> +static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
> +{
> +       struct rockchip_vpu_ctx *ctx = fh_to_ctx(priv);
> +
> +       vpu_debug_enter();
> +
> +       vpu_debug(4, "f->type = %d\n", f->type);
> +
> +       switch (f->type) {
> +       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> +               f->fmt.pix_mp = ctx->dst_fmt;
> +               break;
> +
> +       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +               f->fmt.pix_mp = ctx->src_fmt;
> +               break;
> +
> +       default:
> +               vpu_err("invalid buf type\n");
> +               return -EINVAL;
> +       }
> +
> +       vpu_debug_leave();
> +
> +       return 0;
> +}
> +
> +static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
> +{
> +       struct rockchip_vpu_dev *dev = video_drvdata(file);
> +       struct rockchip_vpu_fmt *fmt;
> +       struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
> +       char str[5];
> +
> +       vpu_debug_enter();
> +
> +       switch (f->type) {
> +       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +               vpu_debug(4, "%s\n", fmt2str(f->fmt.pix_mp.pixelformat, str));
> +
> +               fmt = find_format(pix_fmt_mp->pixelformat, true, dev);
> +               if (!fmt) {
> +                       vpu_err("failed to try output format\n");
> +                       return -EINVAL;
> +               }
> +
> +               if (pix_fmt_mp->plane_fmt[0].sizeimage == 0) {
> +                       vpu_err("sizeimage of output format must be given\n");
> +                       return -EINVAL;
> +               }
> +
> +               pix_fmt_mp->plane_fmt[0].bytesperline = 0;
> +               break;
> +
> +       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> +               vpu_debug(4, "%s\n", fmt2str(f->fmt.pix_mp.pixelformat, str));
> +
> +               fmt = find_format(pix_fmt_mp->pixelformat, false, dev);
> +               if (!fmt) {
> +                       vpu_err("failed to try capture format\n");
> +                       return -EINVAL;
> +               }
> +
> +               if (fmt->num_planes != pix_fmt_mp->num_planes) {
> +                       vpu_err("plane number mismatches on capture format\n");
> +                       return -EINVAL;
> +               }
> +
> +               /* Limit to hardware min/max. */
> +               pix_fmt_mp->width = clamp(pix_fmt_mp->width,
> +                                         ROCKCHIP_DEC_MIN_WIDTH,
> +                                         ROCKCHIP_DEC_MAX_WIDTH);
> +               pix_fmt_mp->height = clamp(pix_fmt_mp->height,
> +                                          ROCKCHIP_DEC_MIN_HEIGHT,
> +                                          ROCKCHIP_DEC_MAX_HEIGHT);
> +
> +               /* Round up to macroblocks. */
> +               pix_fmt_mp->width = round_up(pix_fmt_mp->width, MB_DIM);
> +               pix_fmt_mp->height = round_up(pix_fmt_mp->height, MB_DIM);
> +               break;
> +
> +       default:
> +               vpu_err("invalid buf type\n");
> +               return -EINVAL;
> +       }
> +
> +       vpu_debug_leave();
> +
> +       return 0;
> +}
> +
> +static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
> +{
> +       struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
> +       struct rockchip_vpu_ctx *ctx = fh_to_ctx(priv);
> +       unsigned int mb_width, mb_height;
> +       struct rockchip_vpu_dev *dev = ctx->dev;
> +       struct rockchip_vpu_fmt *fmt;
> +       int ret = 0;
> +       int i;
> +
> +       vpu_debug_enter();
> +
> +       switch (f->type) {
> +       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +               /* Change not allowed if any queue is streaming. */
> +               if (vb2_is_streaming(&ctx->vq_src)
> +                   || vb2_is_streaming(&ctx->vq_dst)) {
> +                       ret = -EBUSY;
> +                       goto out;
> +               }
> +               /*
> +                * Pixel format change is not allowed when the other queue has
> +                * buffers allocated.
> +                */
> +               if (vb2_is_busy(&ctx->vq_dst)
> +                   && pix_fmt_mp->pixelformat != ctx->src_fmt.pixelformat) {
> +                       ret = -EBUSY;
> +                       goto out;
> +               }
> +
> +               ret = vidioc_try_fmt(file, priv, f);
> +               if (ret)
> +                       goto out;
> +
> +               ctx->vpu_src_fmt = find_format(pix_fmt_mp->pixelformat,
> +                                              true, dev);
> +               ctx->src_fmt = *pix_fmt_mp;
> +               break;
> +
> +       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> +               /*
> +                * Change not allowed if this queue is streaming.
> +                *
> +                * NOTE: We allow changes with source queue streaming
> +                * to support resolution change in decoded stream.
> +                */
> +               if (vb2_is_streaming(&ctx->vq_dst)) {
> +                       ret = -EBUSY;
> +                       goto out;
> +               }
> +               /*
> +                * Pixel format change is not allowed when the other queue has
> +                * buffers allocated.
> +                */
> +               if (vb2_is_busy(&ctx->vq_src)
> +                   && pix_fmt_mp->pixelformat != ctx->dst_fmt.pixelformat) {
> +                       ret = -EBUSY;
> +                       goto out;
> +               }
> +
> +               ret = vidioc_try_fmt(file, priv, f);
> +               if (ret)
> +                       goto out;
> +
> +               fmt = find_format(pix_fmt_mp->pixelformat, false, dev);
> +               ctx->vpu_dst_fmt = fmt;
> +
> +               mb_width = MB_WIDTH(pix_fmt_mp->width);
> +               mb_height = MB_HEIGHT(pix_fmt_mp->height);
> +
> +               vpu_debug(0, "CAPTURE codec mode: %d\n", fmt->codec_mode);
> +               vpu_debug(0, "fmt - w: %d, h: %d, mb - w: %d, h: %d\n",
> +                         pix_fmt_mp->width, pix_fmt_mp->height,
> +                         mb_width, mb_height);
> +
> +               for (i = 0; i < fmt->num_planes; ++i) {
> +                       pix_fmt_mp->plane_fmt[i].bytesperline =
> +                               mb_width * MB_DIM * fmt->depth[i] / 8;
> +                       pix_fmt_mp->plane_fmt[i].sizeimage =
> +                               pix_fmt_mp->plane_fmt[i].bytesperline
> +                               * mb_height * MB_DIM;
> +                       /*
> +                        * All of multiplanar formats we support have chroma
> +                        * planes subsampled by 2.
> +                        */
> +                       if (i != 0)
> +                               pix_fmt_mp->plane_fmt[i].sizeimage /= 2;
> +               }
> +
> +               ctx->dst_fmt = *pix_fmt_mp;
> +               break;
> +
> +       default:
> +               vpu_err("invalid buf type\n");
> +               return -EINVAL;
> +       }
> +
> +out:
> +       vpu_debug_leave();
> +
> +       return ret;
> +}
> +
> +static int vidioc_reqbufs(struct file *file, void *priv,
> +                         struct v4l2_requestbuffers *reqbufs)
> +{
> +       struct rockchip_vpu_ctx *ctx = fh_to_ctx(priv);
> +       int ret;
> +
> +       vpu_debug_enter();
> +
> +       switch (reqbufs->type) {
> +       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +               ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
> +               if (ret != 0) {
> +                       vpu_err("error in vb2_reqbufs() for E(S)\n");
> +                       goto out;
> +               }
> +               break;
> +
> +       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> +               ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
> +               if (ret != 0) {
> +                       vpu_err("error in vb2_reqbufs() for E(D)\n");
> +                       goto out;
> +               }
> +               break;
> +
> +       default:
> +               vpu_err("invalid buf type\n");
> +               ret = -EINVAL;
> +               goto out;
> +       }
> +
> +out:
> +       vpu_debug_leave();
> +
> +       return ret;
> +}
> +
> +static int vidioc_querybuf(struct file *file, void *priv,
> +                          struct v4l2_buffer *buf)
> +{
> +       struct rockchip_vpu_ctx *ctx = fh_to_ctx(priv);
> +       int ret;
> +
> +       vpu_debug_enter();
> +
> +       switch (buf->type) {
> +       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> +               ret = vb2_querybuf(&ctx->vq_dst, buf);
> +               if (ret != 0) {
> +                       vpu_err("error in vb2_querybuf() for E(D)\n");
> +                       goto out;
> +               }
> +
> +               buf->m.planes[0].m.mem_offset += DST_QUEUE_OFF_BASE;
> +               break;
> +
> +       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +               ret = vb2_querybuf(&ctx->vq_src, buf);
> +               if (ret != 0) {
> +                       vpu_err("error in vb2_querybuf() for E(S)\n");
> +                       goto out;
> +               }
> +               break;
> +
> +       default:
> +               vpu_err("invalid buf type\n");
> +               ret = -EINVAL;
> +               goto out;
> +       }
> +
> +out:
> +       vpu_debug_leave();
> +
> +       return ret;
> +}
> +
> +/* Queue a buffer */
> +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
> +{
> +       struct rockchip_vpu_ctx *ctx = fh_to_ctx(priv);
> +       int ret;
> +       int i;
> +
> +       vpu_debug_enter();
> +
> +       for (i = 0; i < buf->length; i++)
> +               vpu_debug(4, "plane[%d]->length %d bytesused %d\n",
> +                               i, buf->m.planes[i].length,
> +                               buf->m.planes[i].bytesused);
> +
> +       switch (buf->type) {
> +       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +               ret = vb2_qbuf(&ctx->vq_src, buf);
> +               vpu_debug(4, "OUTPUT_MPLANE : vb2_qbuf return %d\n", ret);
> +               break;
> +
> +       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> +               ret = vb2_qbuf(&ctx->vq_dst, buf);
> +               vpu_debug(4, "CAPTURE_MPLANE: vb2_qbuf return %d\n", ret);
> +               break;
> +
> +       default:
> +               ret = -EINVAL;
> +       }
> +
> +       vpu_debug_leave();
> +
> +       return ret;
> +}
> +
> +/* Dequeue a buffer */
> +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
> +{
> +       struct rockchip_vpu_ctx *ctx = fh_to_ctx(priv);
> +       int ret;
> +
> +       vpu_debug_enter();
> +
> +       switch (buf->type) {
> +       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +               ret = vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK);
> +               break;
> +
> +       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> +               ret = vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK);
> +               break;
> +
> +       default:
> +               ret = -EINVAL;
> +       }
> +
> +       vpu_debug_leave();
> +
> +       return ret;
> +}
> +
> +/* Export DMA buffer */
> +static int vidioc_expbuf(struct file *file, void *priv,
> +                        struct v4l2_exportbuffer *eb)
> +{
> +       struct rockchip_vpu_ctx *ctx = fh_to_ctx(priv);
> +       int ret;
> +
> +       vpu_debug_enter();
> +
> +       switch (eb->type) {
> +       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +               ret = vb2_expbuf(&ctx->vq_src, eb);
> +               break;
> +
> +       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> +               ret = vb2_expbuf(&ctx->vq_dst, eb);
> +               break;
> +
> +       default:
> +               ret = -EINVAL;
> +       }
> +
> +       vpu_debug_leave();
> +
> +       return ret;
> +}
> +
> +/* Stream on */
> +static int vidioc_streamon(struct file *file, void *priv,
> +                          enum v4l2_buf_type type)
> +{
> +       struct rockchip_vpu_ctx *ctx = fh_to_ctx(priv);
> +       int ret;
> +
> +       vpu_debug_enter();
> +
> +       switch (type) {
> +       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +               ret = vb2_streamon(&ctx->vq_src, type);
> +               break;
> +
> +       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> +               ret = vb2_streamon(&ctx->vq_dst, type);
> +               break;
> +
> +       default:
> +               ret = -EINVAL;
> +       }
> +
> +       vpu_debug_leave();
> +
> +       return ret;
> +}
> +
> +/* Stream off, which equals to a pause */
> +static int vidioc_streamoff(struct file *file, void *priv,
> +                           enum v4l2_buf_type type)
> +{
> +       struct rockchip_vpu_ctx *ctx = fh_to_ctx(priv);
> +       int ret;
> +
> +       vpu_debug_enter();
> +
> +       switch (type) {
> +       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +               ret = vb2_streamoff(&ctx->vq_src, type);
> +               break;
> +
> +       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> +               ret = vb2_streamoff(&ctx->vq_dst, type);
> +               break;
> +
> +       default:
> +               ret = -EINVAL;
> +       }
> +
> +       vpu_debug_leave();
> +
> +       return ret;
> +}
> +
> +static int rockchip_vpu_dec_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +       struct rockchip_vpu_ctx *ctx = ctrl_to_ctx(ctrl);
> +       struct rockchip_vpu_dev *dev = ctx->dev;
> +       int ret = 0;
> +
> +       vpu_debug_enter();
> +
> +       vpu_debug(4, "ctrl id %d\n", ctrl->id);
> +
> +       switch (ctrl->id) {
> +       case V4L2_CID_MPEG_VIDEO_VP8_FRAME_HDR:
> +               /* These controls are used directly. */
> +               break;
> +
> +       default:
> +               v4l2_err(&dev->v4l2_dev, "Invalid control, id=%d, val=%d\n",
> +                        ctrl->id, ctrl->val);
> +               ret = -EINVAL;
> +       }
> +
> +       vpu_debug_leave();
> +
> +       return ret;
> +}
> +
> +static const struct v4l2_ctrl_ops rockchip_vpu_dec_ctrl_ops = {
> +       .s_ctrl = rockchip_vpu_dec_s_ctrl,
> +};
> +
> +static const struct v4l2_ioctl_ops rockchip_vpu_dec_ioctl_ops = {
> +       .vidioc_querycap = vidioc_querycap,
> +       .vidioc_enum_framesizes = vidioc_enum_framesizes,
> +       .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane,
> +       .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane,
> +       .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt,
> +       .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt,
> +       .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt,
> +       .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt,
> +       .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt,
> +       .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt,
> +       .vidioc_reqbufs = vidioc_reqbufs,
> +       .vidioc_querybuf = vidioc_querybuf,
> +       .vidioc_qbuf = vidioc_qbuf,
> +       .vidioc_dqbuf = vidioc_dqbuf,
> +       .vidioc_expbuf = vidioc_expbuf,
> +       .vidioc_streamon = vidioc_streamon,
> +       .vidioc_streamoff = vidioc_streamoff,
> +};
> +
> +static int rockchip_vpu_queue_setup(struct vb2_queue *vq,
> +                                 const void *parg,
> +                                 unsigned int *buf_count,
> +                                 unsigned int *plane_count,
> +                                 unsigned int psize[], void *allocators[])
> +{
> +       struct rockchip_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv);
> +       int ret = 0;
> +
> +       vpu_debug_enter();
> +
> +       switch (vq->type) {
> +       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +               *plane_count = ctx->vpu_src_fmt->num_planes;
> +
> +               if (*buf_count < 1)
> +                       *buf_count = 1;
> +
> +               if (*buf_count > VIDEO_MAX_FRAME)
> +                       *buf_count = VIDEO_MAX_FRAME;
> +
> +               psize[0] = ctx->src_fmt.plane_fmt[0].sizeimage;
> +               allocators[0] = ctx->dev->alloc_ctx;
> +               vpu_debug(0, "output psize[%d]: %d\n", 0, psize[0]);
> +               break;
> +
> +       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> +               *plane_count = ctx->vpu_dst_fmt->num_planes;
> +
> +               if (*buf_count < 1)
> +                       *buf_count = 1;
> +
> +               if (*buf_count > VIDEO_MAX_FRAME)
> +                       *buf_count = VIDEO_MAX_FRAME;
> +
> +               psize[0] = round_up(ctx->dst_fmt.plane_fmt[0].sizeimage, 8);
> +               allocators[0] = ctx->dev->alloc_ctx;
> +
> +               vpu_debug(0, "capture psize[%d]: %d\n", 0, psize[0]);
> +               break;
> +
> +       default:
> +               vpu_err("invalid queue type: %d\n", vq->type);
> +               ret = -EINVAL;
> +       }
> +
> +       vpu_debug_leave();
> +
> +       return ret;
> +}
> +
> +static int rockchip_vpu_buf_init(struct vb2_buffer *vb)
> +{
> +       struct vb2_queue *vq = vb->vb2_queue;
> +       struct rockchip_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv);
> +
> +       vpu_debug_enter();
> +
> +       if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> +               ctx->dst_bufs[vb->index] = vb;
> +
> +       vpu_debug_leave();
> +
> +       return 0;
> +}
> +
> +static void rockchip_vpu_buf_cleanup(struct vb2_buffer *vb)
> +{
> +       struct vb2_queue *vq = vb->vb2_queue;
> +       struct rockchip_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv);
> +
> +       vpu_debug_enter();
> +
> +       if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> +               ctx->dst_bufs[vb->index] = NULL;
> +
> +       vpu_debug_leave();
> +}
> +
> +static int rockchip_vpu_buf_prepare(struct vb2_buffer *vb)
> +{
> +       struct vb2_queue *vq = vb->vb2_queue;
> +       struct rockchip_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv);
> +       int ret = 0;
> +       int i;
> +
> +       vpu_debug_enter();
> +
> +       switch (vq->type) {
> +       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +               vpu_debug(4, "plane size: %ld, dst size: %d\n",
> +                               vb2_plane_size(vb, 0),
> +                               ctx->src_fmt.plane_fmt[0].sizeimage);
> +
> +               if (vb2_plane_size(vb, 0)
> +                   < ctx->src_fmt.plane_fmt[0].sizeimage) {
> +                       vpu_err("plane size is too small for output\n");
> +                       ret = -EINVAL;
> +               }
> +               break;
> +
> +       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> +               for (i = 0; i < ctx->vpu_dst_fmt->num_planes; ++i) {
> +                       vpu_debug(4, "plane %d size: %ld, sizeimage: %u\n", i,
> +                                       vb2_plane_size(vb, i),
> +                                       ctx->dst_fmt.plane_fmt[i].sizeimage);
> +
> +                       if (vb2_plane_size(vb, i)
> +                           < ctx->dst_fmt.plane_fmt[i].sizeimage) {
> +                               vpu_err("size of plane %d is too small for capture\n",
> +                                       i);
> +                               break;
> +                       }
> +               }
> +
> +               if (i != ctx->vpu_dst_fmt->num_planes)
> +                       ret = -EINVAL;
> +               break;
> +
> +       default:
> +               vpu_err("invalid queue type: %d\n", vq->type);
> +               ret = -EINVAL;
> +       }
> +
> +       vpu_debug_leave();
> +
> +       return ret;
> +}
> +
> +static int rockchip_vpu_start_streaming(struct vb2_queue *q, unsigned int count)
> +{
> +       int ret = 0;
> +       struct rockchip_vpu_ctx *ctx = fh_to_ctx(q->drv_priv);
> +       struct rockchip_vpu_dev *dev = ctx->dev;
> +       bool ready = false;
> +
> +       vpu_debug_enter();
> +
> +       if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> +               ret = rockchip_vpu_init(ctx);
> +               if (ret < 0) {
> +                       vpu_err("rockchip_vpu_init failed\n");
> +                       return ret;
> +               }
> +
> +               ready = vb2_is_streaming(&ctx->vq_src);
> +       } else if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +               ready = vb2_is_streaming(&ctx->vq_dst);
> +       }
> +
> +       if (ready)
> +               rockchip_vpu_try_context(dev, ctx);
> +
> +       vpu_debug_leave();
> +
> +       return 0;
> +}
> +
> +static void rockchip_vpu_stop_streaming(struct vb2_queue *q)
> +{
> +       unsigned long flags;
> +       struct rockchip_vpu_ctx *ctx = fh_to_ctx(q->drv_priv);
> +       struct rockchip_vpu_dev *dev = ctx->dev;
> +       struct rockchip_vpu_buf *b;
> +       LIST_HEAD(queue);
> +       int i;
> +
> +       vpu_debug_enter();
> +
> +       spin_lock_irqsave(&dev->irqlock, flags);
> +
> +       list_del_init(&ctx->list);
> +
> +       switch (q->type) {
> +       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> +               list_splice_init(&ctx->dst_queue, &queue);
> +               break;
> +
> +       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +               list_splice_init(&ctx->src_queue, &queue);
> +               break;
> +
> +       default:
> +               break;
> +       }
> +
> +       spin_unlock_irqrestore(&dev->irqlock, flags);
> +
> +       wait_event(dev->run_wq, dev->current_ctx != ctx);
> +
> +       while (!list_empty(&queue)) {
> +               b = list_first_entry(&queue, struct rockchip_vpu_buf, list);
> +               for (i = 0; i < b->b.vb2_buf.num_planes; i++)
> +                       vb2_set_plane_payload(&b->b.vb2_buf, i, 0);
> +               vb2_buffer_done(&b->b.vb2_buf, VB2_BUF_STATE_ERROR);
> +               list_del(&b->list);
> +       }
> +
> +       if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> +               rockchip_vpu_deinit(ctx);
> +
> +       vpu_debug_leave();
> +}
> +
> +static void rockchip_vpu_buf_queue(struct vb2_buffer *vb)
> +{
> +       struct vb2_queue *vq = vb->vb2_queue;
> +       struct rockchip_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv);
> +       struct rockchip_vpu_dev *dev = ctx->dev;
> +       struct rockchip_vpu_buf *vpu_buf;
> +       unsigned long flags;
> +
> +       vpu_debug_enter();
> +
> +       switch (vq->type) {
> +       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> +               vpu_buf = vb_to_buf(vb);
> +
> +               /* Mark destination as available for use by VPU */
> +               spin_lock_irqsave(&dev->irqlock, flags);
> +
> +               list_add_tail(&vpu_buf->list, &ctx->dst_queue);
> +
> +               spin_unlock_irqrestore(&dev->irqlock, flags);
> +               break;
> +
> +       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +               vpu_buf = vb_to_buf(vb);
> +
> +               spin_lock_irqsave(&dev->irqlock, flags);
> +
> +               list_add_tail(&vpu_buf->list, &ctx->src_queue);
> +
> +               spin_unlock_irqrestore(&dev->irqlock, flags);
> +               break;
> +
> +       default:
> +               vpu_err("unsupported buffer type (%d)\n", vq->type);
> +       }
> +
> +       if (vb2_is_streaming(&ctx->vq_src) && vb2_is_streaming(&ctx->vq_dst))
> +               rockchip_vpu_try_context(dev, ctx);
> +
> +       vpu_debug_leave();
> +}
> +
> +static struct vb2_ops rockchip_vpu_dec_qops = {
> +       .queue_setup = rockchip_vpu_queue_setup,
> +       .wait_prepare = vb2_ops_wait_prepare,
> +       .wait_finish = vb2_ops_wait_finish,
> +       .buf_init = rockchip_vpu_buf_init,
> +       .buf_prepare = rockchip_vpu_buf_prepare,
> +       .buf_cleanup = rockchip_vpu_buf_cleanup,
> +       .start_streaming = rockchip_vpu_start_streaming,
> +       .stop_streaming = rockchip_vpu_stop_streaming,
> +       .buf_queue = rockchip_vpu_buf_queue,
> +};
> +
> +struct vb2_ops *get_dec_queue_ops(void)
> +{
> +       return &rockchip_vpu_dec_qops;
> +}
> +
> +const struct v4l2_ioctl_ops *get_dec_v4l2_ioctl_ops(void)
> +{
> +       return &rockchip_vpu_dec_ioctl_ops;
> +}
> +
> +static void rockchip_vpu_dec_prepare_run(struct rockchip_vpu_ctx *ctx)
> +{
> +       struct vb2_v4l2_buffer *src =
> +               to_vb2_v4l2_buffer(&ctx->run.src->b.vb2_buf);
> +
> +       v4l2_ctrl_apply_request(&ctx->ctrl_handler, src->request);
> +
> +}
> +
> +static void rockchip_vpu_dec_run_done(struct rockchip_vpu_ctx *ctx,
> +                                   enum vb2_buffer_state result)
> +{
> +       struct v4l2_plane_pix_format *plane_fmts = ctx->dst_fmt.plane_fmt;
> +       struct vb2_buffer *dst = &ctx->run.dst->b.vb2_buf;
> +       int i;
> +
> +       if (result != VB2_BUF_STATE_DONE) {
> +               /* Assume no payload after failed run. */
> +               for (i = 0; i < dst->num_planes; ++i)
> +                       vb2_set_plane_payload(dst, i, 0);
> +               return;
> +       }
> +
> +       for (i = 0; i < dst->num_planes; ++i)
> +               vb2_set_plane_payload(dst, i, plane_fmts[i].sizeimage);
> +}
> +
> +static const struct rockchip_vpu_run_ops rockchip_vpu_dec_run_ops = {
> +       .prepare_run = rockchip_vpu_dec_prepare_run,
> +       .run_done = rockchip_vpu_dec_run_done,
> +};
> +
> +int rockchip_vpu_dec_init(struct rockchip_vpu_ctx *ctx)
> +{
> +
> +       ctx->run_ops = &rockchip_vpu_dec_run_ops;
> +
> +       return rockchip_vpu_ctrls_setup(ctx, &rockchip_vpu_dec_ctrl_ops,
> +                                       controls, ARRAY_SIZE(controls), NULL);
> +}
> +
> +void rockchip_vpu_dec_exit(struct rockchip_vpu_ctx *ctx)
> +{
> +       rockchip_vpu_ctrls_delete(ctx);
> +}
> diff --git a/drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.h b/drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.h
> new file mode 100644
> index 0000000..267a089
> --- /dev/null
> +++ b/drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.h
> @@ -0,0 +1,33 @@
> +/*
> + * Rockchip VPU codec driver
> + *
> + * Copyright (C) 2014 Rockchip Electronics Co., Ltd.
> + *     Hertz Wong <hertz.wong at rock-chips.com>
> + *
> + * Copyright (C) 2014 Google, Inc.
> + *     Tomasz Figa <tfiga at chromium.org>
> + *
> + * Based on s5p-mfc driver by Samsung Electronics Co., Ltd.
> + *
> + * Copyright (C) 2011 Samsung Electronics Co., Ltd.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef ROCKCHIP_VPU_DEC_H_
> +#define ROCKCHIP_VPU_DEC_H_
> +
> +struct vb2_ops *get_dec_queue_ops(void);
> +const struct v4l2_ioctl_ops *get_dec_v4l2_ioctl_ops(void);
> +struct rockchip_vpu_fmt *get_dec_def_fmt(bool src);
> +int rockchip_vpu_dec_init(struct rockchip_vpu_ctx *ctx);
> +void rockchip_vpu_dec_exit(struct rockchip_vpu_ctx *ctx);
> +
> +#endif /* ROCKCHIP_VPU_DEC_H_ */
> diff --git a/drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.c b/drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.c
> new file mode 100644
> index 0000000..dc7abc7
> --- /dev/null
> +++ b/drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.c
> @@ -0,0 +1,278 @@
> +/*
> + * Rockchip VPU codec driver
> + *
> + * Copyright (C) 2014 Google, Inc.
> + *     Tomasz Figa <tfiga at chromium.org>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include "rockchip_vpu_common.h"
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/pm.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +
> +#include <asm/dma-iommu.h>
> +
> +/**
> + * struct rockchip_vpu_codec_ops - codec mode specific operations
> + *
> + * @init:      Prepare for streaming. Called from VB2 .start_streaming()
> + *             when streaming from both queues is being enabled.
> + * @exit:      Clean-up after streaming. Called from VB2 .stop_streaming()
> + *             when streaming from first of both enabled queues is being
> + *             disabled.
> + * @run:       Start single {en,de)coding run. Called from non-atomic context
> + *             to indicate that a pair of buffers is ready and the hardware
> + *             should be programmed and started.
> + * @done:      Read back processing results and additional data from hardware.
> + * @reset:     Reset the hardware in case of a timeout.
> + */
> +struct rockchip_vpu_codec_ops {
> +       int (*init)(struct rockchip_vpu_ctx *);
> +       void (*exit)(struct rockchip_vpu_ctx *);
> +
> +       int (*irq)(int, struct rockchip_vpu_dev *);
> +       void (*run)(struct rockchip_vpu_ctx *);
> +       void (*done)(struct rockchip_vpu_ctx *, enum vb2_buffer_state);
> +       void (*reset)(struct rockchip_vpu_ctx *);
> +};
> +
> +/*
> + * Hardware control routines.
> + */
> +
> +void rockchip_vpu_power_on(struct rockchip_vpu_dev *vpu)
> +{
> +       vpu_debug_enter();
> +
> +       /* TODO: Clock gating. */
> +
> +       pm_runtime_get_sync(vpu->dev);
> +
> +       vpu_debug_leave();
> +}
> +
> +static void rockchip_vpu_power_off(struct rockchip_vpu_dev *vpu)
> +{
> +       vpu_debug_enter();
> +
> +       pm_runtime_mark_last_busy(vpu->dev);
> +       pm_runtime_put_autosuspend(vpu->dev);
> +
> +       /* TODO: Clock gating. */
> +
> +       vpu_debug_leave();
> +}
> +
> +/*
> + * Interrupt handlers.
> + */
> +
> +static irqreturn_t vdpu_irq(int irq, void *dev_id)
> +{
> +       struct rockchip_vpu_dev *vpu = dev_id;
> +       struct rockchip_vpu_ctx *ctx = vpu->current_ctx;
> +
> +       if (!ctx->hw.codec_ops->irq(irq, vpu)) {
> +               rockchip_vpu_power_off(vpu);
> +               cancel_delayed_work(&vpu->watchdog_work);
> +
> +               ctx->hw.codec_ops->done(ctx, VB2_BUF_STATE_DONE);
> +       }
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static void rockchip_vpu_watchdog(struct work_struct *work)
> +{
> +       struct rockchip_vpu_dev *vpu = container_of(to_delayed_work(work),
> +                                       struct rockchip_vpu_dev, watchdog_work);
> +       struct rockchip_vpu_ctx *ctx = vpu->current_ctx;
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&vpu->irqlock, flags);
> +
> +       ctx->hw.codec_ops->reset(ctx);
> +
> +       spin_unlock_irqrestore(&vpu->irqlock, flags);
> +
> +       vpu_err("frame processing timed out!\n");
> +
> +       rockchip_vpu_power_off(vpu);
> +       ctx->hw.codec_ops->done(ctx, VB2_BUF_STATE_ERROR);
> +}
> +
> +/*
> + * Initialization/clean-up.
> + */
> +
> +#if defined(CONFIG_ROCKCHIP_IOMMU)
> +static int rockchip_vpu_iommu_init(struct rockchip_vpu_dev *vpu)
> +{
> +       int ret;
> +
> +       vpu->mapping = arm_iommu_create_mapping(&platform_bus_type,
> +                                               0x10000000, SZ_2G);
> +       if (IS_ERR(vpu->mapping)) {
> +               ret = PTR_ERR(vpu->mapping);
> +               return ret;
> +       }
> +
> +       vpu->dev->dma_parms = devm_kzalloc(vpu->dev,
> +                               sizeof(*vpu->dev->dma_parms), GFP_KERNEL);
> +       if (!vpu->dev->dma_parms)
> +               goto err_release_mapping;
> +
> +       dma_set_max_seg_size(vpu->dev, 0xffffffffu);
> +
> +       ret = arm_iommu_attach_device(vpu->dev, vpu->mapping);
> +       if (ret)
> +               goto err_release_mapping;
> +
> +       return 0;
> +
> +err_release_mapping:
> +       arm_iommu_release_mapping(vpu->mapping);
> +
> +       return ret;
> +}
> +
> +static void rockchip_vpu_iommu_cleanup(struct rockchip_vpu_dev *vpu)
> +{
> +       arm_iommu_detach_device(vpu->dev);
> +       arm_iommu_release_mapping(vpu->mapping);
> +}
> +#else
> +static inline int rockchip_vpu_iommu_init(struct rockchip_vpu_dev *vpu)
> +{
> +       return 0;
> +}
> +
> +static inline void rockchip_vpu_iommu_cleanup(struct rockchip_vpu_dev *vpu) { }
> +#endif
> +
> +int rockchip_vpu_hw_probe(struct rockchip_vpu_dev *vpu)
> +{
> +       struct resource *res;
> +       int irq_dec;
> +       int ret;
> +
> +       pr_info("probe device %s\n", dev_name(vpu->dev));
> +
> +       INIT_DELAYED_WORK(&vpu->watchdog_work, rockchip_vpu_watchdog);
> +
> +       vpu->aclk = devm_clk_get(vpu->dev, "aclk");
> +       if (IS_ERR(vpu->aclk)) {
> +               dev_err(vpu->dev, "failed to get aclk\n");
> +               return PTR_ERR(vpu->aclk);
> +       }
> +
> +       vpu->hclk = devm_clk_get(vpu->dev, "hclk");
> +       if (IS_ERR(vpu->hclk)) {
> +               dev_err(vpu->dev, "failed to get hclk\n");
> +               return PTR_ERR(vpu->hclk);
> +       }
> +
> +       /*
> +        * Bump ACLK to max. possible freq. (400 MHz) to improve performance.
> +        */
> +       clk_set_rate(vpu->aclk, 400*1000*1000);
> +
> +       res = platform_get_resource(vpu->pdev, IORESOURCE_MEM, 0);
> +       vpu->base = devm_ioremap_resource(vpu->dev, res);
> +       if (IS_ERR(vpu->base))
> +               return PTR_ERR(vpu->base);
> +
> +       clk_prepare_enable(vpu->aclk);
> +       clk_prepare_enable(vpu->hclk);
> +
> +       vpu->dec_base = vpu->base + vpu->variant->dec_offset;
> +
> +       ret = dma_set_coherent_mask(vpu->dev, DMA_BIT_MASK(32));
> +       if (ret) {
> +               dev_err(vpu->dev, "could not set DMA coherent mask\n");
> +               goto err_power;
> +       }
> +
> +       ret = rockchip_vpu_iommu_init(vpu);
> +       if (ret)
> +               goto err_power;
> +
> +       irq_dec = platform_get_irq_byname(vpu->pdev, "vdpu");
> +       if (irq_dec <= 0) {
> +               dev_err(vpu->dev, "could not get vdpu IRQ\n");
> +               ret = -ENXIO;
> +               goto err_iommu;
> +       }
> +
> +       ret = devm_request_threaded_irq(vpu->dev, irq_dec, NULL, vdpu_irq,
> +                                       IRQF_ONESHOT, dev_name(vpu->dev), vpu);
> +       if (ret) {
> +               dev_err(vpu->dev, "could not request vdpu IRQ\n");
> +               goto err_iommu;
> +       }
> +
> +       pm_runtime_set_autosuspend_delay(vpu->dev, 100);
> +       pm_runtime_use_autosuspend(vpu->dev);
> +       pm_runtime_enable(vpu->dev);
> +
> +       return 0;
> +
> +err_iommu:
> +       rockchip_vpu_iommu_cleanup(vpu);
> +err_power:
> +       clk_disable_unprepare(vpu->hclk);
> +       clk_disable_unprepare(vpu->aclk);
> +
> +       return ret;
> +}
> +
> +void rockchip_vpu_hw_remove(struct rockchip_vpu_dev *vpu)
> +{
> +       rockchip_vpu_iommu_cleanup(vpu);
> +
> +       pm_runtime_disable(vpu->dev);
> +
> +       clk_disable_unprepare(vpu->hclk);
> +       clk_disable_unprepare(vpu->aclk);
> +}
> +
> +static const struct rockchip_vpu_codec_ops mode_ops[0];
> +
> +void rockchip_vpu_run(struct rockchip_vpu_ctx *ctx)
> +{
> +       ctx->hw.codec_ops->run(ctx);
> +}
> +
> +int rockchip_vpu_init(struct rockchip_vpu_ctx *ctx)
> +{
> +       enum rockchip_vpu_codec_mode codec_mode;
> +
> +       codec_mode = ctx->vpu_src_fmt->codec_mode; /* Decoder */
> +
> +       ctx->hw.codec_ops = &mode_ops[codec_mode];
> +
> +       return ctx->hw.codec_ops->init(ctx);
> +}
> +
> +void rockchip_vpu_deinit(struct rockchip_vpu_ctx *ctx)
> +{
> +       ctx->hw.codec_ops->exit(ctx);
> +}
> diff --git a/drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.h b/drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.h
> new file mode 100644
> index 0000000..975357da
> --- /dev/null
> +++ b/drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.h
> @@ -0,0 +1,78 @@
> +/*
> + * Rockchip VPU codec driver
> + *
> + * Copyright (C) 2014 Google, Inc.
> + *     Tomasz Figa <tfiga at chromium.org>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef ROCKCHIP_VPU_HW_H_
> +#define ROCKCHIP_VPU_HW_H_
> +
> +#include <media/videobuf2-core.h>
> +
> +#define ROCKCHIP_HEADER_SIZE           1280
> +#define ROCKCHIP_HW_PARAMS_SIZE                5487
> +#define ROCKCHIP_RET_PARAMS_SIZE       488
> +
> +struct rockchip_vpu_dev;
> +struct rockchip_vpu_ctx;
> +struct rockchip_vpu_buf;
> +
> +/**
> + * enum rockchip_vpu_type - vpu type.
> + * @RK_VPU_NONE:       No vpu type. Used for RAW video formats.
> + * @RK3288_VPU:                Vpu on rk3288 soc.
> + * @RK3229_VPU:                Vpu on rk3229 soc.
> + * @RKVDEC:            Rkvdec.
> + */
> +enum rockchip_vpu_type {
> +       RK_VPU_NONE     = -1,
> +       RK3288_VPU,
> +       RK3229_VPU,
> +};
> +
> +#define ROCKCHIP_VPU_MATCHES(type1, type2) \
> +       (type1 == RK_VPU_NONE || type2 == RK_VPU_NONE || type1 == type2)
> +
> +/**
> + * struct rockchip_vpu_aux_buf - auxiliary DMA buffer for hardware data
> + * @cpu:       CPU pointer to the buffer.
> + * @dma:       DMA address of the buffer.
> + * @size:      Size of the buffer.
> + */
> +struct rockchip_vpu_aux_buf {
> +       void *cpu;
> +       dma_addr_t dma;
> +       size_t size;
> +};
> +
> +/**
> + * struct rockchip_vpu_hw_ctx - Context private data of hardware code.
> + * @codec_ops:         Set of operations associated with current codec mode.
> + */
> +struct rockchip_vpu_hw_ctx {
> +       const struct rockchip_vpu_codec_ops *codec_ops;
> +
> +       /* Specific for particular codec modes. */
> +};
> +
> +int rockchip_vpu_hw_probe(struct rockchip_vpu_dev *vpu);
> +void rockchip_vpu_hw_remove(struct rockchip_vpu_dev *vpu);
> +
> +int rockchip_vpu_init(struct rockchip_vpu_ctx *ctx);
> +void rockchip_vpu_deinit(struct rockchip_vpu_ctx *ctx);
> +
> +void rockchip_vpu_run(struct rockchip_vpu_ctx *ctx);
> +
> +void rockchip_vpu_power_on(struct rockchip_vpu_dev *vpu);
> +
> +#endif /* RK3288_VPU_HW_H_ */
> --
> 1.9.1
>


From eballetbo at gmail.com  Tue Jan 26 06:00:56 2016
From: eballetbo at gmail.com (Enric Balletbo Serra)
Date: Tue, 26 Jan 2016 15:00:56 +0100
Subject: [PATCH v1 0/3] Add VP8 deocder for rk3229 & rk3288
In-Reply-To: <56A73CB9.2040802@rock-chips.com>
References: <1453799046-307-1-git-send-email-jung.zhao@rock-chips.com>
 <56A73CB9.2040802@rock-chips.com>
Message-ID: <CAFqH_52UbNW86Bx3Zo9rxPWjx_018j+BBy55ZwzNs9Kf2X7v7Q@mail.gmail.com>

Hi Jung,

2016-01-26 10:30 GMT+01:00 Shawn Lin <shawn.lin at rock-chips.com>:
> Hi jun,
>
> Where is the dt-bingding documentation about your VP8 controller?
>
> And would you please share some info about rk3229? I can just find
> rk3228 in mainline, otherwise may someone think it's a misspell.
>
> Thanks.
>
>
> On 2016/1/26 17:04, Jung Zhao wrote:
>>
>> From: zhaojun <jung.zhao at rock-chips.com>
>>
>>
>> ====================
>> Introduction
>> ====================
>>
>> The purpose of this series is to add the driver for vp8
>> decoder on rk3229 & rk3288 platform, and will support
>> more formats in the future.
>>
>> The driver uses v4l2 framework and RK IOMMU.
>> RK IOMMU has not yet been merged.
>>

Can you share or specify what patches are needed, are they already
send to upstream ? So people that want to test your series knows what
they need to apply

I think that, at least, this patch is required:

iommu/rockchip: reconstruct to support multi slaves [1]

If this is not already accepted, maybe is a good idea include this
patch in the patch series

[1] http://www.gossamer-threads.com/lists/linux/kernel/2347458

>>
>>
>> zhaojun (3):
>>    media: v4l: Add VP8 format support in V4L2 framework
>>    media: VPU: support Rockchip VPU
>>    media: vcodec: rockchip: Add Rockchip VP8 decoder driver
>>
>>   drivers/media/platform/rockchip-vpu/Makefile       |    7 +
>>   .../media/platform/rockchip-vpu/rkvpu_hw_vp8d.c    |  798 ++++++++++
>>   .../platform/rockchip-vpu/rockchip_vp8d_regs.h     | 1594
>> ++++++++++++++++++++
>>   drivers/media/platform/rockchip-vpu/rockchip_vpu.c |  799 ++++++++++
>>   .../platform/rockchip-vpu/rockchip_vpu_common.h    |  439 ++++++
>>   .../media/platform/rockchip-vpu/rockchip_vpu_dec.c | 1007 +++++++++++++
>>   .../media/platform/rockchip-vpu/rockchip_vpu_dec.h |   33 +
>>   .../media/platform/rockchip-vpu/rockchip_vpu_hw.c  |  295 ++++
>>   .../media/platform/rockchip-vpu/rockchip_vpu_hw.h  |  100 ++
>>   drivers/media/v4l2-core/v4l2-ctrls.c               |   17 +-
>>   drivers/media/v4l2-core/v4l2-ioctl.c               |    3 +
>>   drivers/media/v4l2-core/videobuf2-dma-contig.c     |   51 +-
>>   include/media/v4l2-ctrls.h                         |    2 +
>>   include/media/videobuf2-dma-contig.h               |   11 +-
>>   include/uapi/linux/v4l2-controls.h                 |   98 ++
>>   include/uapi/linux/videodev2.h                     |    5 +
>>   16 files changed, 5238 insertions(+), 21 deletions(-)
>>   create mode 100644 drivers/media/platform/rockchip-vpu/Makefile
>>   create mode 100644 drivers/media/platform/rockchip-vpu/rkvpu_hw_vp8d.c
>>   create mode 100644
>> drivers/media/platform/rockchip-vpu/rockchip_vp8d_regs.h
>>   create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu.c
>>   create mode 100644
>> drivers/media/platform/rockchip-vpu/rockchip_vpu_common.h
>>   create mode 100644
>> drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.c
>>   create mode 100644
>> drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.h
>>   create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.c
>>   create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.h
>>
>
>
> --
> Best Regards
> Shawn Lin
>

Best Regards,
Enric


From dianders at chromium.org  Tue Jan 26 08:05:44 2016
From: dianders at chromium.org (Doug Anderson)
Date: Tue, 26 Jan 2016 08:05:44 -0800
Subject: [Bug] usb: dwc2: Add functions to set and clear force mode
In-Reply-To: <56A7603C.1000409@rock-chips.com>
References: <56A7603C.1000409@rock-chips.com>
Message-ID: <CAD=FV=Xk4v=1QMYG9XmXh6hJLKU6i_wVakOArF1du-Z_4_+Neg@mail.gmail.com>

Hi,

On Tue, Jan 26, 2016 at 4:02 AM, Caesar Wang <wxt at rock-chips.com> wrote:
> Hi  John, Felipe
>
>
> I'm no familiar with usb stuff.
> then I found this patch will break usb working for rk3036 SoCs, maybe more
> SoCs.
> Says, U disk can't work on usb host.
>
> The failure log:
>
>    32.645481] usb usb2-port1: connect-debounce failed
>
>
> Tested by following branch:
> https://github.com/Caesar-github/rockchip/tree/kylin/next   (kernel:
> 4.5-rc1)
>
> Revert "usb: dwc2: Add functions to set and clear force mode" will work for
> it.
>
>
> Maybe, someone have some suggestions or ideas?

Can you check if this series helps you?

http://marc.info/?l=linux-usb&m=145255851516121&w=2

I know USB on mainline was failing pretty badly for me for rk3288
until I applied those patches, though I didn't debug it further than
just applying those patches.

-Doug


From dianders at chromium.org  Tue Jan 26 08:32:07 2016
From: dianders at chromium.org (Doug Anderson)
Date: Tue, 26 Jan 2016 08:32:07 -0800
Subject: [PATCH 0/5] clk: rockchip: add full support for HDMI clock on
 rk3288
In-Reply-To: <CAAObsKBS45kbvdmdvFxwSo=B9GXRLoX2g7WQYVmZJ0KFnwSejw@mail.gmail.com>
References: <1415087559-19444-1-git-send-email-kever.yang@rock-chips.com>
 <48806799.thBSifdUp8@diego> <54647142.4080800@rock-chips.com>
 <CAD=FV=Vzec3Qm-kc6zZ4VxSREoZbzVkWbzp-noHYdcEVrLYYSw@mail.gmail.com>
 <CAAObsKChYN5nmcm5OWnAzNRGDi_QGSoKvN+0FizhjeT-3mmeXQ@mail.gmail.com>
 <CAD=FV=Wgu=xQ9PcVqfzcedXVU3Rh-HKbBVxHuqgV9OZJEYhYvg@mail.gmail.com>
 <CAAObsKC1v--mZwkiG-c7P2Bwi2-_txbeqaW4Vd5h-noAVNbhjA@mail.gmail.com>
 <CAD=FV=VB93wYigRJ8wa8YL6P+Q+-fRokPiCB-Fo1pC_zAdh2oA@mail.gmail.com>
 <CAAObsKCMh3Bk_oNJdLbU2F9b7jrnFZOp2uFR_oPqUoJ3W1gfaQ@mail.gmail.com>
 <CAD=FV=W=LCJk4m049hBnm7BOYudnVe=oa3jdcSrrZZUU9rFtog@mail.gmail.com>
 <CAAObsKBS45kbvdmdvFxwSo=B9GXRLoX2g7WQYVmZJ0KFnwSejw@mail.gmail.com>
Message-ID: <CAD=FV=Une-DJ+0zV6Hmx5EmKXCEApb2Zx1FuJaUQf7MT9OPsug@mail.gmail.com>

Tomeu,

On Tue, Jan 26, 2016 at 12:28 AM, Tomeu Vizoso <tomeu at tomeuvizoso.net> wrote:
> On 22 January 2016 at 18:07, Doug Anderson <dianders at chromium.org> wrote:
>> Tomeu,
>>
>> On Fri, Jan 22, 2016 at 6:00 AM, Tomeu Vizoso <tomeu at tomeuvizoso.net> wrote:
>>> On 21 January 2016 at 21:11, Doug Anderson <dianders at chromium.org> wrote:
>>>> Hi,
>>>>
>>>> On Thu, Jan 21, 2016 at 1:03 AM, Tomeu Vizoso <tomeu at tomeuvizoso.net> wrote:
>>>>> So we have a mechanism for detecting a conflict in the clock
>>>>> hierarchy, and a mechanism to solve it, but we are missing a way for
>>>>> userspace to communicate policy regarding which clocks should be given
>>>>> priority when solving such a conflict?
>>>>
>>>> Hrmmm, I guess it could be userspace that makes the decision.  It does
>>>> seem a little odd to force it to userspace in all cases, though.  For
>>>> a particular laptop that is designed with a specific panel connected
>>>> up eDP it seems less than ideal to push this into userspace.  If the
>>>> kernel could just work in the expected sane way (or at least work that
>>>> way by default) it would be ideal.
>>>
>>> Ah, I was wrongly assuming that the kernel didn't have enough
>>> information to make an informed decision in this case, sorry.
>>>
>>> Guess the per-user rate limits don't help here because the consumer
>>> with higher priority could work with frequencies other than the ideal.
>>>
>>> And we cannot have a consumer listening for PRE_RATE_CHANGE and
>>> aborting unwanted changes or rerouting the ancestors of the clocks of
>>> other consumers because that would be a massive violation of
>>> separation of concerns.
>>>
>>> If we were to rearrange the clock topology from within the CCF, then
>>> consumers need to have a way to communicate to the core that they are
>>> more important than other consumers. clk_set_important(clk, true)
>>> could be enough in this case, but would be insufficient in more
>>> complex cases where more than two clocks could use the same PLL.
>>
>> With something like the above I'd still expect some complexity
>> depending on the probe order.  If a less important device grabs the
>> clock first, it might be forced to re-think its clocks later.  That
>> might be disconcerting.
>
> How much disconcerting do you think this could be? Hopefully those
> devices should probe quite close to each other, right?

Probe: probably, though with defers it could be several seconds.

...but remember that display interfaces tend to be hotplug.  That
might mean that the HDMI interface won't try to set the clock until
much, much later.


>> OK, so I was just involved in a change recently that made me realize
>> that maybe our original problems were tied to the fact that our
>> builtin panels were trying to specify a clock that was impossible to
>> achieve with CPLL / GPLL.  It was a known problem that the request
>> would be denied and the CCF would just pick the closest rate it could.
>> Probably the right thing is to solve _that_ problem first.  If using
>> simple panel you could do a change like
>> <https://chromium-review.googlesource.com/#/c/323211/> (though
>> presumably you'd have to handle people using the same panel in other
>> laptops).  You might also be able to do funny things to fixup the mode
>> like dbehr tried to do in
>> <https://chromium-review.googlesource.com/#/c/270017/>.  By doing this
>> and making sure that
>
> Are we missing something here?

Eh?


From heiko at sntech.de  Tue Jan 26 11:00:58 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Tue, 26 Jan 2016 20:00:58 +0100
Subject: [PATCH v5] phy: rockchip-usb: add handler for usb-uart functionality
Message-ID: <1535200.sepIlriAJS@phil>

Most newer Rockchip SoCs provide the possibility to use a usb-phy
as passthrough for the debug uart (uart2), making it possible to
for example get console output without needing to open the device.

This patch adds an early_initcall to enable this functionality
conditionally via the commandline and also disables the corresponding
usb controller in the devicetree.

Currently only data for the rk3288 is provided, but at least the
rk3188 and arm64 rk3368 also provide this functionality and will be
enabled later.

On a spliced usb cable the signals are tx on white wire(D+) and
rx on green wire(D-).

The one caveat is that currently the reconfiguration of the phy
happens as early_initcall, as the code depends on the unflattened
devicetree being available. Everything is fine if only a regular
console is active as the console-replay will happen after the
reconfiguation. But with earlycon active output up to smp-init
currently will get lost.

The phy is an optional property for the connected dwc2 controller,
so we still provide the phy device but fail all phy-ops with -EBUSY
to make sure the dwc2 does not try to transmit anything on the
repurposed phy.

Signed-off-by: Heiko Stuebner <heiko at sntech.de>
---
changes in v5:
- only compile debug uart functionality if the phy is compiled in
  fixes initcall conflict and debug functionality also is only really
  usable if it's available early
changes in v4:
- drop the rest of the phy-series, as the patches have gotten applied

So far, this hasn't gotten eyeballs yet, so citing discussion-parts from
the v3 coverletter:

The patch, while not associated with the new pll handling, also builds
on the groundwork introduced there and adds support for the function
repurposing one of the phys as passthrough for uart-data. This enables
attaching a ttl converter to the D+ and D- pins of an usb cable to
receive uart data this way, when it is not really possible to attach
a regular serial console to a board.

One point of critique in my first iteration [0] of this was, that
due to when the reconfiguration happens we may miss parts of the logs
when earlycon is enabled. So far early_initcall gets used as the
unflattened devicetree is necessary to set this up. Doing this for
example in the early_param directly would require parsing the flattened
devicetree to get needed nodes and properties.

I still maintain that if you're working on anything before smp-bringup
you should use a real dev-board instead or try to solder uart cables
on hopefully available test-points  .


 Documentation/kernel-parameters.txt |   6 +
 drivers/phy/phy-rockchip-usb.c      | 233 
++++++++++++++++++++++++++++++------
 2 files changed, 203 insertions(+), 36 deletions(-)

diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-
parameters.txt
index 87d40a7..d91417b 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -3486,6 +3486,12 @@ bytes respectively. Such letter suffixes can also be 
entirely omitted.
 
 	ro		[KNL] Mount root device read-only on boot
 
+	rockchip.usb_uart
+			Enable the uart passthrough on the designated usb port
+			on Rockchip SoCs. When active, the signals of the
+			debug-uart get routed to the D+ and D- pins of the usb
+			port and the regular usb controller gets disabled.
+
 	root=		[KNL] Root filesystem
 			See name_to_dev_t comment in init/do_mounts.c.
 
diff --git a/drivers/phy/phy-rockchip-usb.c b/drivers/phy/phy-rockchip-usb.c
index 33a80eb..f62d899 100644
--- a/drivers/phy/phy-rockchip-usb.c
+++ b/drivers/phy/phy-rockchip-usb.c
@@ -30,21 +30,23 @@
 #include <linux/regmap.h>
 #include <linux/mfd/syscon.h>
 
-/*
- * The higher 16-bit of this register is used for write protection
- * only if BIT(13 + 16) set to 1 the BIT(13) can be written.
- */
-#define SIDDQ_WRITE_ENA	BIT(29)
-#define SIDDQ_ON		BIT(13)
-#define SIDDQ_OFF		(0 << 13)
+static int enable_usb_uart;
+
+#define HIWORD_UPDATE(val, mask) \
+		((val) | (mask) << 16)
+
+#define UOC_CON0_SIDDQ BIT(13)
 
 struct rockchip_usb_phys {
 	int reg;
 	const char *pll_name;
 };
 
+struct rockchip_usb_phy_base;
 struct rockchip_usb_phy_pdata {
 	struct rockchip_usb_phys *phys;
+	int (*init_usb_uart)(struct regmap *grf);
+	int usb_uart_phy;
 };
 
 struct rockchip_usb_phy_base {
@@ -61,13 +63,15 @@ struct rockchip_usb_phy {
 	struct clk      *clk480m;
 	struct clk_hw	clk480m_hw;
 	struct phy	*phy;
+	bool		uart_enabled;
 };
 
 static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy,
 					   bool siddq)
 {
-	return regmap_write(phy->base->reg_base, phy->reg_offset,
-			    SIDDQ_WRITE_ENA | (siddq ? SIDDQ_ON : SIDDQ_OFF));
+	u32 val = HIWORD_UPDATE(siddq ? UOC_CON0_SIDDQ : 0, UOC_CON0_SIDDQ);
+
+	return regmap_write(phy->base->reg_base, phy->reg_offset, val);
 }
 
 static unsigned long rockchip_usb_phy480m_recalc_rate(struct clk_hw *hw,
@@ -108,7 +112,7 @@ static int rockchip_usb_phy480m_is_enabled(struct clk_hw 
*hw)
 	if (ret < 0)
 		return ret;
 
-	return (val & SIDDQ_ON) ? 0 : 1;
+	return (val & UOC_CON0_SIDDQ) ? 0 : 1;
 }
 
 static const struct clk_ops rockchip_usb_phy480m_ops = {
@@ -122,6 +126,9 @@ static int rockchip_usb_phy_power_off(struct phy *_phy)
 {
 	struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
 
+	if (phy->uart_enabled)
+		return -EBUSY;
+
 	clk_disable_unprepare(phy->clk480m);
 
 	return 0;
@@ -131,6 +138,9 @@ static int rockchip_usb_phy_power_on(struct phy *_phy)
 {
 	struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
 
+	if (phy->uart_enabled)
+		return -EBUSY;
+
 	return clk_prepare_enable(phy->clk480m);
 }
 
@@ -144,8 +154,10 @@ static void rockchip_usb_phy_action(void *data)
 {
 	struct rockchip_usb_phy *rk_phy = data;
 
-	of_clk_del_provider(rk_phy->np);
-	clk_unregister(rk_phy->clk480m);
+	if (!rk_phy->uart_enabled) {
+		of_clk_del_provider(rk_phy->np);
+		clk_unregister(rk_phy->clk480m);
+	}
 
 	if (rk_phy->clk)
 		clk_put(rk_phy->clk);
@@ -194,30 +206,35 @@ static int rockchip_usb_phy_init(struct 
rockchip_usb_phy_base *base,
 		return -EINVAL;
 	}
 
-	if (rk_phy->clk) {
-		clk_name = __clk_get_name(rk_phy->clk);
-		init.flags = 0;
-		init.parent_names = &clk_name;
-		init.num_parents = 1;
+	if (enable_usb_uart && base->pdata->usb_uart_phy == i) {
+		dev_dbg(base->dev, "phy%d used as uart output\n", i);
+		rk_phy->uart_enabled = true;
 	} else {
-		init.flags = CLK_IS_ROOT;
-		init.parent_names = NULL;
-		init.num_parents = 0;
-	}
+		if (rk_phy->clk) {
+			clk_name = __clk_get_name(rk_phy->clk);
+			init.flags = 0;
+			init.parent_names = &clk_name;
+			init.num_parents = 1;
+		} else {
+			init.flags = CLK_IS_ROOT;
+			init.parent_names = NULL;
+			init.num_parents = 0;
+		}
 
-	init.ops = &rockchip_usb_phy480m_ops;
-	rk_phy->clk480m_hw.init = &init;
+		init.ops = &rockchip_usb_phy480m_ops;
+		rk_phy->clk480m_hw.init = &init;
 
-	rk_phy->clk480m = clk_register(base->dev, &rk_phy->clk480m_hw);
-	if (IS_ERR(rk_phy->clk480m)) {
-		err = PTR_ERR(rk_phy->clk480m);
-		goto err_clk;
-	}
+		rk_phy->clk480m = clk_register(base->dev, &rk_phy->clk480m_hw);
+		if (IS_ERR(rk_phy->clk480m)) {
+			err = PTR_ERR(rk_phy->clk480m);
+			goto err_clk;
+		}
 
-	err = of_clk_add_provider(child, of_clk_src_simple_get,
-				  rk_phy->clk480m);
-	if (err < 0)
-		goto err_clk_prov;
+		err = of_clk_add_provider(child, of_clk_src_simple_get,
+					rk_phy->clk480m);
+		if (err < 0)
+			goto err_clk_prov;
+	}
 
 	err = devm_add_action(base->dev, rockchip_usb_phy_action, rk_phy);
 	if (err)
@@ -230,13 +247,21 @@ static int rockchip_usb_phy_init(struct 
rockchip_usb_phy_base *base,
 	}
 	phy_set_drvdata(rk_phy->phy, rk_phy);
 
-	/* only power up usb phy when it use, so disable it when init*/
-	return rockchip_usb_phy_power(rk_phy, 1);
+	/*
+	 * When acting as uart-pipe, just keep clock on otherwise
+	 * only power up usb phy when it use, so disable it when init
+	 */
+	if (rk_phy->uart_enabled)
+		return clk_prepare_enable(rk_phy->clk);
+	else
+		return rockchip_usb_phy_power(rk_phy, 1);
 
 err_devm_action:
-	of_clk_del_provider(child);
+	if (!rk_phy->uart_enabled)
+		of_clk_del_provider(child);
 err_clk_prov:
-	clk_unregister(rk_phy->clk480m);
+	if (!rk_phy->uart_enabled)
+		clk_unregister(rk_phy->clk480m);
 err_clk:
 	if (rk_phy->clk)
 		clk_put(rk_phy->clk);
@@ -259,6 +284,86 @@ static const struct rockchip_usb_phy_pdata rk3188_pdata 
= {
 	},
 };
 
+#define RK3288_UOC0_CON0				0x320
+#define RK3288_UOC0_CON0_COMMON_ON_N			BIT(0)
+#define RK3288_UOC0_CON0_DISABLE			BIT(4)
+
+#define RK3288_UOC0_CON2				0x328
+#define RK3288_UOC0_CON2_SOFT_CON_SEL			BIT(2)
+
+#define RK3288_UOC0_CON3				0x32c
+#define RK3288_UOC0_CON3_UTMI_SUSPENDN			BIT(0)
+#define RK3288_UOC0_CON3_UTMI_OPMODE_NODRIVING		(1 << 1)
+#define RK3288_UOC0_CON3_UTMI_OPMODE_MASK		(3 << 1)
+#define RK3288_UOC0_CON3_UTMI_XCVRSEELCT_FSTRANSC	(1 << 3)
+#define RK3288_UOC0_CON3_UTMI_XCVRSEELCT_MASK		(3 << 3)
+#define RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED		BIT(5)
+#define RK3288_UOC0_CON3_BYPASSDMEN			BIT(6)
+#define RK3288_UOC0_CON3_BYPASSSEL			BIT(7)
+
+/*
+ * Enable the bypass of uart2 data through the otg usb phy.
+ * Original description in the TRM.
+ * 1. Disable the OTG block by setting OTGDISABLE0 to 1?b1.
+ * 2. Disable the pull-up resistance on the D+ line by setting
+ *    OPMODE0[1:0] to 2?b01.
+ * 3. To ensure that the XO, Bias, and PLL blocks are powered down in 
Suspend
+ *    mode, set COMMONONN to 1?b1.
+ * 4. Place the USB PHY in Suspend mode by setting SUSPENDM0 to 1?b0.
+ * 5. Set BYPASSSEL0 to 1?b1.
+ * 6. To transmit data, controls BYPASSDMEN0, and BYPASSDMDATA0.
+ * To receive data, monitor FSVPLUS0.
+ *
+ * The actual code in the vendor kernel does some things differently.
+ */
+static int __init rk3288_init_usb_uart(struct regmap *grf)
+{
+	u32 val;
+	int ret;
+
+	/*
+	 * COMMON_ON and DISABLE settings are described in the TRM,
+	 * but were not present in the original code.
+	 * Also disable the analog phy components to save power.
+	 */
+	val = HIWORD_UPDATE(RK3288_UOC0_CON0_COMMON_ON_N
+				| RK3288_UOC0_CON0_DISABLE
+				| UOC_CON0_SIDDQ,
+			    RK3288_UOC0_CON0_COMMON_ON_N
+				| RK3288_UOC0_CON0_DISABLE
+				| UOC_CON0_SIDDQ);
+	ret = regmap_write(grf, RK3288_UOC0_CON0, val);
+	if (ret)
+		return ret;
+
+	val = HIWORD_UPDATE(RK3288_UOC0_CON2_SOFT_CON_SEL,
+			    RK3288_UOC0_CON2_SOFT_CON_SEL);
+	ret = regmap_write(grf, RK3288_UOC0_CON2, val);
+	if (ret)
+		return ret;
+
+	val = HIWORD_UPDATE(RK3288_UOC0_CON3_UTMI_OPMODE_NODRIVING
+				| RK3288_UOC0_CON3_UTMI_XCVRSEELCT_FSTRANSC
+				| RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED,
+			    RK3288_UOC0_CON3_UTMI_SUSPENDN
+				| RK3288_UOC0_CON3_UTMI_OPMODE_MASK
+				| RK3288_UOC0_CON3_UTMI_XCVRSEELCT_MASK
+				| RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED);
+	ret = regmap_write(grf, RK3288_UOC0_CON3, val);
+	if (ret)
+		return ret;
+
+	val = HIWORD_UPDATE(RK3288_UOC0_CON3_BYPASSSEL
+				| RK3288_UOC0_CON3_BYPASSDMEN,
+			    RK3288_UOC0_CON3_BYPASSSEL
+				| RK3288_UOC0_CON3_BYPASSDMEN);
+	ret = regmap_write(grf, RK3288_UOC0_CON3, val);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
 static const struct rockchip_usb_phy_pdata rk3288_pdata = {
 	.phys = (struct rockchip_usb_phys[]){
 		{ .reg = 0x320, .pll_name = "sclk_otgphy0_480m" },
@@ -266,6 +371,8 @@ static const struct rockchip_usb_phy_pdata rk3288_pdata 
= {
 		{ .reg = 0x348, .pll_name = "sclk_otgphy2_480m" },
 		{ /* sentinel */ }
 	},
+	.init_usb_uart = rk3288_init_usb_uart,
+	.usb_uart_phy = 0,
 };
 
 static int rockchip_usb_phy_probe(struct platform_device *pdev)
@@ -328,6 +435,60 @@ static struct platform_driver rockchip_usb_driver = {
 
 module_platform_driver(rockchip_usb_driver);
 
+#ifndef MODULE
+static int __init rockchip_init_usb_uart(void)
+{
+	const struct of_device_id *match;
+	const struct rockchip_usb_phy_pdata *data;
+	struct device_node *np;
+	struct regmap *grf;
+	int ret;
+
+	if (!enable_usb_uart)
+		return 0;
+
+	np = of_find_matching_node_and_match(NULL, rockchip_usb_phy_dt_ids,
+					     &match);
+	if (!np) {
+		pr_err("%s: failed to find usbphy node\n", __func__);
+		return -ENOTSUPP;
+	}
+
+	pr_debug("%s: using settings for %s\n", __func__, match->compatible);
+	data = match->data;
+
+	if (!data->init_usb_uart) {
+		pr_err("%s: usb-uart not available on %s\n",
+		       __func__, match->compatible);
+		return -ENOTSUPP;
+	}
+
+	grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+	if (IS_ERR(grf)) {
+		pr_err("%s: Missing rockchip,grf property, %lu\n",
+		       __func__, PTR_ERR(grf));
+		return PTR_ERR(grf);
+	}
+
+	ret = data->init_usb_uart(grf);
+	if (ret) {
+		pr_err("%s: could not init usb_uart, %d\n", __func__, ret);
+		enable_usb_uart = 0;
+		return ret;
+	}
+
+	return 0;
+}
+early_initcall(rockchip_init_usb_uart);
+
+static int __init rockchip_usb_uart(char *buf)
+{
+	enable_usb_uart = true;
+	return 0;
+}
+early_param("rockchip.usb_uart", rockchip_usb_uart);
+#endif
+
 MODULE_AUTHOR("Yunzhi Li <lyz at rock-chips.com>");
 MODULE_DESCRIPTION("Rockchip USB 2.0 PHY driver");
 MODULE_LICENSE("GPL v2");
-- 
2.6.4



From heiko at sntech.de  Tue Jan 26 12:21:38 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Tue, 26 Jan 2016 21:21:38 +0100
Subject: [PATCH] clk: rockchip: add hclk_cpubus to the list of rk3188
 critical clocks
In-Reply-To: <1453815240-15354-1-git-send-email-al.kochet@gmail.com>
References: <1453815240-15354-1-git-send-email-al.kochet@gmail.com>
Message-ID: <12983946.PGshY20RJE@phil>

Am Dienstag, 26. Januar 2016, 16:34:00 schrieb Alexander Kochetkov:
> hclk_cpubus needs to keep running because it is needed for devices like
> the rom, i2s0 or spdif to be accessible via cpu. Without that all
> accesses to devices (readl/writel) return wrong data. So add it
> to the list of critical clocks.
> 
> Fixes: 78eaf6095cc763c ("clk: rockchip: disable unused clocks")
> 
> Signed-off-by: Alexander Kochetkov <al.kochet at gmail.com>

applied to my clk-branch for 4.6


Thanks
Heiko


From nicolas.dufresne at collabora.com  Tue Jan 26 15:53:33 2016
From: nicolas.dufresne at collabora.com (Nicolas Dufresne)
Date: Tue, 26 Jan 2016 18:53:33 -0500
Subject: [PATCH v1 1/3] media: v4l: Add VP8 format support in V4L2
 framework
In-Reply-To: <1453799046-307-2-git-send-email-jung.zhao@rock-chips.com>
References: <1453799046-307-1-git-send-email-jung.zhao@rock-chips.com>
 <1453799046-307-2-git-send-email-jung.zhao@rock-chips.com>
Message-ID: <1453852413.5584.15.camel@collabora.com>

Hi Jung,

this patch adds new public API to videodev2.h. It would be appropriate
to also add the associated documentation for it (see Documentation/DocBook/media). I also believe there is multiple way VP8 support could have been added. A proper commit message that explains the approach would also be appropriate.

cheers,
Nicolas

Le mardi 26 janvier 2016 ? 17:04 +0800, Jung Zhao a ?crit?:
> From: zhaojun <jung.zhao at rock-chips.com>
> 
> Signed-off-by: zhaojun <jung.zhao at rock-chips.com>
> ---
> 
> ?drivers/media/v4l2-core/v4l2-ctrls.c???????????| 17 ++++-
> ?drivers/media/v4l2-core/v4l2-ioctl.c???????????|??3 +
> ?drivers/media/v4l2-core/videobuf2-dma-contig.c | 51 +++++++++-----
> ?include/media/v4l2-ctrls.h?????????????????????|??2 +
> ?include/media/videobuf2-dma-contig.h???????????| 11 ++-
> ?include/uapi/linux/v4l2-controls.h?????????????| 98
> ++++++++++++++++++++++++++
> ?include/uapi/linux/videodev2.h?????????????????|??5 ++
> ?7 files changed, 166 insertions(+), 21 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c
> b/drivers/media/v4l2-core/v4l2-ctrls.c
> index 890520d..22821e94 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls.c
> @@ -761,7 +761,7 @@ const char *v4l2_ctrl_get_name(u32 id)
> ?	case V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP:		retu
> rn "VPX I-Frame QP Value";
> ?	case V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP:		retu
> rn "VPX P-Frame QP Value";
> ?	case V4L2_CID_MPEG_VIDEO_VPX_PROFILE:			
> return "VPX Profile";
> -
> +	case V4L2_CID_MPEG_VIDEO_VP8_FRAME_HDR:			
> return "VP8 Frame Header";
> ?	/* CAMERA controls */
> ?	/* Keep the order of the 'case's the same as in v4l2-
> controls.h! */
> ?	case V4L2_CID_CAMERA_CLASS:		return "Camera
> Controls";
> @@ -1126,6 +1126,9 @@ void v4l2_ctrl_fill(u32 id, const char **name,
> enum v4l2_ctrl_type *type,
> ?	case V4L2_CID_RDS_TX_ALT_FREQS:
> ?		*type = V4L2_CTRL_TYPE_U32;
> ?		break;
> +	case V4L2_CID_MPEG_VIDEO_VP8_FRAME_HDR:
> +		*type = V4L2_CTRL_TYPE_VP8_FRAME_HDR;
> +		break;
> ?	default:
> ?		*type = V4L2_CTRL_TYPE_INTEGER;
> ?		break;
> @@ -1525,6 +1528,13 @@ static int std_validate(const struct v4l2_ctrl
> *ctrl, u32 idx,
> ?			return -ERANGE;
> ?		return 0;
> ?
> +	/* FIXME:just return 0 for now */
> +	case V4L2_CTRL_TYPE_PRIVATE:
> +		return 0;
> +
> +	case V4L2_CTRL_TYPE_VP8_FRAME_HDR:
> +		return 0;
> +
> ?	default:
> ?		return -EINVAL;
> ?	}
> @@ -2074,6 +2084,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct
> v4l2_ctrl_handler *hdl,
> ?	case V4L2_CTRL_TYPE_U32:
> ?		elem_size = sizeof(u32);
> ?		break;
> +	case V4L2_CTRL_TYPE_VP8_FRAME_HDR:
> +		elem_size = sizeof(struct v4l2_ctrl_vp8_frame_hdr);
> +		break;
> ?	default:
> ?		if (type < V4L2_CTRL_COMPOUND_TYPES)
> ?			elem_size = sizeof(s32);
> @@ -2098,7 +2111,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct
> v4l2_ctrl_handler *hdl,
> ?		handler_set_err(hdl, -ERANGE);
> ?		return NULL;
> ?	}
> -	if (is_array &&
> +	if ((is_array || (flags & V4L2_CTRL_FLAG_REQ_KEEP)) &&
> ?	????(type == V4L2_CTRL_TYPE_BUTTON ||
> ?	?????type == V4L2_CTRL_TYPE_CTRL_CLASS)) {
> ?		handler_set_err(hdl, -EINVAL);
> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c
> b/drivers/media/v4l2-core/v4l2-ioctl.c
> index 7d028d1..8aa5812 100644
> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> @@ -1259,6 +1259,9 @@ static void v4l_fill_fmtdesc(struct
> v4l2_fmtdesc *fmt)
> ?		case V4L2_PIX_FMT_VC1_ANNEX_G:	descr = "VC-1
> (SMPTE 412M Annex G)"; break;
> ?		case V4L2_PIX_FMT_VC1_ANNEX_L:	descr = "VC-1
> (SMPTE 412M Annex L)"; break;
> ?		case V4L2_PIX_FMT_VP8:		descr = "VP8";
> break;
> +		case V4L2_PIX_FMT_VP8_FRAME:
> +			descr = "VP8 FRAME";
> +			break;
> ?		case V4L2_PIX_FMT_CPIA1:	descr = "GSPCA CPiA
> YUV"; break;
> ?		case V4L2_PIX_FMT_WNVA:		descr =
> "WNVA"; break;
> ?		case V4L2_PIX_FMT_SN9C10X:	descr = "GSPCA
> SN9C10X"; break;
> diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c
> b/drivers/media/v4l2-core/videobuf2-dma-contig.c
> index c331272..aebcc7f 100644
> --- a/drivers/media/v4l2-core/videobuf2-dma-contig.c
> +++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c
> @@ -23,13 +23,16 @@
> ?
> ?struct vb2_dc_conf {
> ?	struct device		*dev;
> +	struct dma_attrs	attrs;
> ?};
> ?
> ?struct vb2_dc_buf {
> ?	struct device			*dev;
> ?	void				*vaddr;
> ?	unsigned long			size;
> +	void				*cookie;
> ?	dma_addr_t			dma_addr;
> +	struct dma_attrs		attrs;
> ?	enum dma_data_direction		dma_dir;
> ?	struct sg_table			*dma_sgt;
> ?	struct frame_vector		*vec;
> @@ -131,7 +134,8 @@ static void vb2_dc_put(void *buf_priv)
> ?		sg_free_table(buf->sgt_base);
> ?		kfree(buf->sgt_base);
> ?	}
> -	dma_free_coherent(buf->dev, buf->size, buf->vaddr, buf-
> >dma_addr);
> +	dma_free_attrs(buf->dev, buf->size, buf->cookie, buf-
> >dma_addr,
> +			&buf->attrs);
> ?	put_device(buf->dev);
> ?	kfree(buf);
> ?}
> @@ -143,18 +147,22 @@ static void *vb2_dc_alloc(void *alloc_ctx,
> unsigned long size,
> ?	struct device *dev = conf->dev;
> ?	struct vb2_dc_buf *buf;
> ?
> -	buf = kzalloc(sizeof *buf, GFP_KERNEL);
> +	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
> ?	if (!buf)
> ?		return ERR_PTR(-ENOMEM);
> ?
> -	buf->vaddr = dma_alloc_coherent(dev, size, &buf->dma_addr,
> -						GFP_KERNEL |
> gfp_flags);
> -	if (!buf->vaddr) {
> +	buf->attrs = conf->attrs;
> +	buf->cookie = dma_alloc_attrs(dev, size, &buf->dma_addr,
> +					GFP_KERNEL | gfp_flags,
> &buf->attrs);
> +	if (!buf->cookie) {
> ?		dev_err(dev, "dma_alloc_coherent of size %ld
> failed\n", size);
> ?		kfree(buf);
> ?		return ERR_PTR(-ENOMEM);
> ?	}
> ?
> +	if (!dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, &buf->attrs))
> +		buf->vaddr = buf->cookie;
> +
> ?	/* Prevent the device from being released while the buffer
> is used */
> ?	buf->dev = get_device(dev);
> ?	buf->size = size;
> @@ -185,8 +193,8 @@ static int vb2_dc_mmap(void *buf_priv, struct
> vm_area_struct *vma)
> ?	?*/
> ?	vma->vm_pgoff = 0;
> ?
> -	ret = dma_mmap_coherent(buf->dev, vma, buf->vaddr,
> -		buf->dma_addr, buf->size);
> +	ret = dma_mmap_attrs(buf->dev, vma, buf->cookie,
> +		buf->dma_addr, buf->size, &buf->attrs);
> ?
> ?	if (ret) {
> ?		pr_err("Remapping memory failed, error: %d\n", ret);
> @@ -329,7 +337,7 @@ static void *vb2_dc_dmabuf_ops_kmap(struct
> dma_buf *dbuf, unsigned long pgnum)
> ?{
> ?	struct vb2_dc_buf *buf = dbuf->priv;
> ?
> -	return buf->vaddr + pgnum * PAGE_SIZE;
> +	return buf->vaddr ? buf->vaddr + pgnum * PAGE_SIZE : NULL;
> ?}
> ?
> ?static void *vb2_dc_dmabuf_ops_vmap(struct dma_buf *dbuf)
> @@ -368,8 +376,8 @@ static struct sg_table
> *vb2_dc_get_base_sgt(struct vb2_dc_buf *buf)
> ?		return NULL;
> ?	}
> ?
> -	ret = dma_get_sgtable(buf->dev, sgt, buf->vaddr, buf-
> >dma_addr,
> -		buf->size);
> +	ret = dma_get_sgtable_attrs(buf->dev, sgt, buf->cookie, buf-
> >dma_addr,
> +		buf->size, &buf->attrs);
> ?	if (ret < 0) {
> ?		dev_err(buf->dev, "failed to get scatterlist from
> DMA API\n");
> ?		kfree(sgt);
> @@ -448,22 +456,26 @@ static void vb2_dc_put_userptr(void *buf_priv)
> ? */
> ?
> ?#ifdef __arch_pfn_to_dma
> -static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev,
> unsigned long pfn)
> +static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev,
> +					???unsigned long pfn)
> ?{
> ?	return (dma_addr_t)__arch_pfn_to_dma(dev, pfn);
> ?}
> ?#elif defined(__pfn_to_bus)
> -static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev,
> unsigned long pfn)
> +static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev,
> +					???unsigned long pfn)
> ?{
> ?	return (dma_addr_t)__pfn_to_bus(pfn);
> ?}
> ?#elif defined(__pfn_to_phys)
> -static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev,
> unsigned long pfn)
> +static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev,
> +					???unsigned long pfn)
> ?{
> ?	return (dma_addr_t)__pfn_to_phys(pfn);
> ?}
> ?#else
> -static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev,
> unsigned long pfn)
> +static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev,
> +					???unsigned long pfn)
> ?{
> ?	/* really, we cannot do anything better at this point */
> ?	return (dma_addr_t)(pfn) << PAGE_SHIFT;
> @@ -497,7 +509,7 @@ static void *vb2_dc_get_userptr(void *alloc_ctx,
> unsigned long vaddr,
> ?		return ERR_PTR(-EINVAL);
> ?	}
> ?
> -	buf = kzalloc(sizeof *buf, GFP_KERNEL);
> +	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
> ?	if (!buf)
> ?		return ERR_PTR(-ENOMEM);
> ?
> @@ -721,19 +733,22 @@ const struct vb2_mem_ops vb2_dma_contig_memops
> = {
> ?};
> ?EXPORT_SYMBOL_GPL(vb2_dma_contig_memops);
> ?
> -void *vb2_dma_contig_init_ctx(struct device *dev)
> +void *vb2_dma_contig_init_ctx_attrs(struct device *dev,
> +				????struct dma_attrs *attrs)
> ?{
> ?	struct vb2_dc_conf *conf;
> ?
> -	conf = kzalloc(sizeof *conf, GFP_KERNEL);
> +	conf = kzalloc(sizeof(*conf), GFP_KERNEL);
> ?	if (!conf)
> ?		return ERR_PTR(-ENOMEM);
> ?
> ?	conf->dev = dev;
> +	if (attrs)
> +		conf->attrs = *attrs;
> ?
> ?	return conf;
> ?}
> -EXPORT_SYMBOL_GPL(vb2_dma_contig_init_ctx);
> +EXPORT_SYMBOL_GPL(vb2_dma_contig_init_ctx_attrs);
> ?
> ?void vb2_dma_contig_cleanup_ctx(void *alloc_ctx)
> ?{
> diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
> index 5f9526f..0424cdc 100644
> --- a/include/media/v4l2-ctrls.h
> +++ b/include/media/v4l2-ctrls.h
> @@ -46,6 +46,7 @@ struct poll_table_struct;
> ? * @p_u16:	Pointer to a 16-bit unsigned value.
> ? * @p_u32:	Pointer to a 32-bit unsigned value.
> ? * @p_char:	Pointer to a string.
> + * @p_vp8_frame_hdr:	Pointer to a struct
> v4l2_ctrl_vp8_frame_hdr.
> ? * @p:		Pointer to a compound value.
> ? */
> ?union v4l2_ctrl_ptr {
> @@ -55,6 +56,7 @@ union v4l2_ctrl_ptr {
> ?	u16 *p_u16;
> ?	u32 *p_u32;
> ?	char *p_char;
> +	struct v4l2_ctrl_vp8_frame_hdr *p_vp8_frame_hdr;
> ?	void *p;
> ?};
> ?
> diff --git a/include/media/videobuf2-dma-contig.h
> b/include/media/videobuf2-dma-contig.h
> index c33dfa6..2087c9a 100644
> --- a/include/media/videobuf2-dma-contig.h
> +++ b/include/media/videobuf2-dma-contig.h
> @@ -16,6 +16,8 @@
> ?#include <media/videobuf2-v4l2.h>
> ?#include <linux/dma-mapping.h>
> ?
> +struct dma_attrs;
> +
> ?static inline dma_addr_t
> ?vb2_dma_contig_plane_dma_addr(struct vb2_buffer *vb, unsigned int
> plane_no)
> ?{
> @@ -24,7 +26,14 @@ vb2_dma_contig_plane_dma_addr(struct vb2_buffer
> *vb, unsigned int plane_no)
> ?	return *addr;
> ?}
> ?
> -void *vb2_dma_contig_init_ctx(struct device *dev);
> +void *vb2_dma_contig_init_ctx_attrs(struct device *dev,
> +				????struct dma_attrs *attrs);
> +
> +static inline void *vb2_dma_contig_init_ctx(struct device *dev)
> +{
> +	return vb2_dma_contig_init_ctx_attrs(dev, NULL);
> +}
> +
> ?void vb2_dma_contig_cleanup_ctx(void *alloc_ctx);
> ?
> ?extern const struct vb2_mem_ops vb2_dma_contig_memops;
> diff --git a/include/uapi/linux/v4l2-controls.h
> b/include/uapi/linux/v4l2-controls.h
> index 2d225bc..63c65d9 100644
> --- a/include/uapi/linux/v4l2-controls.h
> +++ b/include/uapi/linux/v4l2-controls.h
> @@ -520,6 +520,7 @@ enum
> v4l2_mpeg_video_h264_hierarchical_coding_type {
> ?};
> ?#define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER	(V
> 4L2_CID_MPEG_BASE+381)
> ?#define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_QP	
> (V4L2_CID_MPEG_BASE+382)
> +
> ?#define V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP	(V4L2_CID_MPEG_B
> ASE+400)
> ?#define V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP	(V4L2_CID_MPEG_B
> ASE+401)
> ?#define V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP	(V4L2_CID_MPEG_B
> ASE+402)
> @@ -578,6 +579,8 @@ enum v4l2_vp8_golden_frame_sel {
> ?#define V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP		(V4L2_CID_
> MPEG_BASE+510)
> ?#define V4L2_CID_MPEG_VIDEO_VPX_PROFILE			(V4L2
> _CID_MPEG_BASE+511)
> ?
> +#define V4L2_CID_MPEG_VIDEO_VP8_FRAME_HDR		(V4L2_CID_M
> PEG_BASE+512)
> +
> ?/*??MPEG-class control IDs specific to the CX2341x driver as defined
> by V4L2 */
> ?#define V4L2_CID_MPEG_CX2341X_BASE?				(
> V4L2_CTRL_CLASS_MPEG | 0x1000)
> ?#define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE?	(V4L
> 2_CID_MPEG_CX2341X_BASE+0)
> @@ -963,4 +966,99 @@ enum v4l2_detect_md_mode {
> ?#define V4L2_CID_DETECT_MD_THRESHOLD_GRID	(V4L2_CID_DETECT_CL
> ASS_BASE + 3)
> ?#define V4L2_CID_DETECT_MD_REGION_GRID		(V4L2_CID_DETE
> CT_CLASS_BASE + 4)
> ?
> +
> +/* Complex controls */
> +
> +#define V4L2_VP8_SEGMNT_HDR_FLAG_ENABLED??????????????0x01
> +#define V4L2_VP8_SEGMNT_HDR_FLAG_UPDATE_MAP???????????0x02
> +#define V4L2_VP8_SEGMNT_HDR_FLAG_UPDATE_FEATURE_DATA??0x04
> +struct v4l2_vp8_sgmnt_hdr {
> +	__u8 segment_feature_mode;
> +
> +	__s8 quant_update[4];
> +	__s8 lf_update[4];
> +	__u8 segment_probs[3];
> +
> +	__u8 flags;
> +};
> +
> +#define V4L2_VP8_LF_HDR_ADJ_ENABLE	0x01
> +#define V4L2_VP8_LF_HDR_DELTA_UPDATE	0x02
> +struct v4l2_vp8_loopfilter_hdr {
> +	__u8 type;
> +	__u8 level;
> +	__u8 sharpness_level;
> +	__s8 ref_frm_delta_magnitude[4];
> +	__s8 mb_mode_delta_magnitude[4];
> +
> +	__u8 flags;
> +};
> +
> +struct v4l2_vp8_quantization_hdr {
> +	__u8 y_ac_qi;
> +	__s8 y_dc_delta;
> +	__s8 y2_dc_delta;
> +	__s8 y2_ac_delta;
> +	__s8 uv_dc_delta;
> +	__s8 uv_ac_delta;
> +	__u16 dequant_factors[4][3][2];
> +};
> +
> +struct v4l2_vp8_entropy_hdr {
> +	__u8 coeff_probs[4][8][3][11];
> +	__u8 y_mode_probs[4];
> +	__u8 uv_mode_probs[3];
> +	__u8 mv_probs[2][19];
> +};
> +
> +#define V4L2_VP8_FRAME_HDR_FLAG_EXPERIMENTAL		0x01
> +#define V4L2_VP8_FRAME_HDR_FLAG_SHOW_FRAME		0x02
> +#define V4L2_VP8_FRAME_HDR_FLAG_MB_NO_SKIP_COEFF	0x04
> +struct v4l2_ctrl_vp8_frame_hdr {
> +	/* 0: keyframe, 1: not a keyframe */
> +	__u8 key_frame;
> +	__u8 version;
> +
> +	/* Populated also if not a key frame */
> +	__u16 width;
> +	__u8 horizontal_scale;
> +	__u16 height;
> +	__u8 vertical_scale;
> +
> +	struct v4l2_vp8_sgmnt_hdr sgmnt_hdr;
> +	struct v4l2_vp8_loopfilter_hdr lf_hdr;
> +	struct v4l2_vp8_quantization_hdr quant_hdr;
> +	struct v4l2_vp8_entropy_hdr entropy_hdr;
> +
> +	__u8 sign_bias_golden;
> +	__u8 sign_bias_alternate;
> +
> +	__u8 prob_skip_false;
> +	__u8 prob_intra;
> +	__u8 prob_last;
> +	__u8 prob_gf;
> +
> +	__u32 first_part_size;
> +	__u32 first_part_offset;
> +	/*
> +	?* Offset in bits of MB data in first partition,
> +	?* i.e. bit offset starting from first_part_offset.
> +	?*/
> +	__u32 macroblock_bit_offset;
> +
> +	__u8 num_dct_parts;
> +	__u32 dct_part_sizes[8];
> +
> +	__u8 bool_dec_range;
> +	__u8 bool_dec_value;
> +	__u8 bool_dec_count;
> +
> +	/* v4l2_buffer indices of reference frames */
> +	__u32 last_frame;
> +	__u32 golden_frame;
> +	__u32 alt_frame;
> +
> +	__u8 flags;
> +};
> +
> ?#endif
> diff --git a/include/uapi/linux/videodev2.h
> b/include/uapi/linux/videodev2.h
> index 29a6b78..191ca19 100644
> --- a/include/uapi/linux/videodev2.h
> +++ b/include/uapi/linux/videodev2.h
> @@ -593,6 +593,7 @@ struct v4l2_pix_format {
> ?#define V4L2_PIX_FMT_VC1_ANNEX_G v4l2_fourcc('V', 'C', '1', 'G') /*
> SMPTE 421M Annex G compliant stream */
> ?#define V4L2_PIX_FMT_VC1_ANNEX_L v4l2_fourcc('V', 'C', '1', 'L') /*
> SMPTE 421M Annex L compliant stream */
> ?#define V4L2_PIX_FMT_VP8??????v4l2_fourcc('V', 'P', '8', '0') /* VP8
> */
> +#define V4L2_PIX_FMT_VP8_FRAME	v4l2_fourcc('V', 'P', '8',
> 'F') /* VP8 parsed frames */
> ?
> ?/*??Vendor-specific formats???*/
> ?#define V4L2_PIX_FMT_CPIA1????v4l2_fourcc('C', 'P', 'I', 'A') /*
> cpia1 YUV */
> @@ -1473,6 +1474,7 @@ struct v4l2_ext_control {
> ?		__u8 __user *p_u8;
> ?		__u16 __user *p_u16;
> ?		__u32 __user *p_u32;
> +		struct v4l2_ctrl_vp8_frame_hdr __user
> *p_vp8_frame_hdr;
> ?		void __user *ptr;
> ?	};
> ?} __attribute__ ((packed));
> @@ -1517,6 +1519,9 @@ enum v4l2_ctrl_type {
> ?	V4L2_CTRL_TYPE_U8	?????= 0x0100,
> ?	V4L2_CTRL_TYPE_U16	?????= 0x0101,
> ?	V4L2_CTRL_TYPE_U32	?????= 0x0102,
> +	V4L2_CTRL_TYPE_VP8_FRAME_HDR	= 0x108,
> +
> +	V4L2_CTRL_TYPE_PRIVATE???????= 0xffff,
> ?};
> ?
> ?/*??Used in the VIDIOC_QUERYCTRL ioctl for querying controls */
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 181 bytes
Desc: This is a digitally signed message part
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160126/7434f8f5/attachment.sig>

From caesar.upstream at gmail.com  Tue Jan 26 18:12:15 2016
From: caesar.upstream at gmail.com (Caesar Wang)
Date: Wed, 27 Jan 2016 10:12:15 +0800
Subject: [Bug] usb: dwc2: Add functions to set and clear force mode
In-Reply-To: <CAD=FV=Xk4v=1QMYG9XmXh6hJLKU6i_wVakOArF1du-Z_4_+Neg@mail.gmail.com>
References: <56A7603C.1000409@rock-chips.com>
 <CAD=FV=Xk4v=1QMYG9XmXh6hJLKU6i_wVakOArF1du-Z_4_+Neg@mail.gmail.com>
Message-ID: <56A8277F.9@gmail.com>

Thanks Doug's reply.

Cc: Wulf
? 2016?01?27? 00:05, Doug Anderson ??:
> Hi,
>
> On Tue, Jan 26, 2016 at 4:02 AM, Caesar Wang <wxt at rock-chips.com> wrote:
>> Hi  John, Felipe
>>
>>
>> I'm no familiar with usb stuff.
>> then I found this patch will break usb working for rk3036 SoCs, maybe more
>> SoCs.
>> Says, U disk can't work on usb host.
>>
>> The failure log:
>>
>>     32.645481] usb usb2-port1: connect-debounce failed
>>
>>
>> Tested by following branch:
>> https://github.com/Caesar-github/rockchip/tree/kylin/next   (kernel:
>> 4.5-rc1)
>>
>> Revert "usb: dwc2: Add functions to set and clear force mode" will work for
>> it.
>>
>>
>> Maybe, someone have some suggestions or ideas?
> Can you check if this series helps you?
>
> http://marc.info/?l=linux-usb&m=145255851516121&w=2

Unluckily, this series patches can't fix it on rk3036 SoCs.
I revert this CL ("usb: dwc2: Add functions to set and clear force 
mode") to work firstly.


> I know USB on mainline was failing pretty badly for me for rk3288
> until I applied those patches, though I didn't debug it further than
> just applying those patches.
>
> -Doug
>
>
>
>
> -- 
> Thanks,
> Caesar


From wxt at rock-chips.com  Tue Jan 26 21:03:09 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Wed, 27 Jan 2016 13:03:09 +0800
Subject: [PATCH] ARM: dts: rockchip: add the leds control for kylin board
Message-ID: <1453870989-6999-1-git-send-email-wxt@rock-chips.com>

As the kylin schematic drawing, add the needed work led for
kylin board.

Run:
ucho 0 > /sys/class/leds/kylin:red:led/brightness
echo 1 > /sys/class/leds/kylin:red:led/brightness

The led can be normall on/off on kylin board.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Hi Heiko,

Describing the content:
- This patch is based on your v4.6-clk/next branch.
- Verified on
  https://github.com/Caesar-github/rockchip/commits/kylin/next.

 arch/arm/boot/dts/rk3036-kylin.dts | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/arch/arm/boot/dts/rk3036-kylin.dts b/arch/arm/boot/dts/rk3036-kylin.dts
index 190f22c..3332a7f 100644
--- a/arch/arm/boot/dts/rk3036-kylin.dts
+++ b/arch/arm/boot/dts/rk3036-kylin.dts
@@ -46,6 +46,17 @@
 	model = "Rockchip RK3036 KylinBoard";
 	compatible = "rockchip,rk3036-kylin", "rockchip,rk3036";
 
+	leds: gpio-leds {
+		compatible = "gpio-leds";
+
+		work {
+			gpios = <&gpio2 30 GPIO_ACTIVE_HIGH>;
+			label = "kylin:red:led";
+			pinctrl-names = "default";
+			pinctrl-0 = <&led_ctl>;
+		};
+	};
+
 	sdio_pwrseq: sdio-pwrseq {
 		compatible = "mmc-pwrseq-simple";
 		pinctrl-names = "default";
@@ -359,6 +370,12 @@
 };
 
 &pinctrl {
+	leds {
+		led_ctl: led-ctl {
+			rockchip,pins = <2 30 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+
 	pmic {
 		pmic_int: pmic-int {
 			rockchip,pins = <2 2 RK_FUNC_GPIO &pcfg_pull_default>;
-- 
1.9.1



From wxt at rock-chips.com  Tue Jan 26 21:06:44 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Wed, 27 Jan 2016 13:06:44 +0800
Subject: [RESEND PATCH] ARM: dts: rockchip: add the leds control for kylin
 board
Message-ID: <1453871204-7312-1-git-send-email-wxt@rock-chips.com>

As the kylin schematic drawing, add the needed work led for
kylin board.

Run:
echo 0 > /sys/class/leds/kylin:red:led/brightness
echo 1 > /sys/class/leds/kylin:red:led/brightness

The led can be normall on/off on kylin board.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Hi Heiko,

Describing the content:
- This patch is based on your for-next branch.
- Verified on
  https://github.com/Caesar-github/rockchip/commits/kylin/next.

 arch/arm/boot/dts/rk3036-kylin.dts | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/arch/arm/boot/dts/rk3036-kylin.dts b/arch/arm/boot/dts/rk3036-kylin.dts
index 190f22c..3332a7f 100644
--- a/arch/arm/boot/dts/rk3036-kylin.dts
+++ b/arch/arm/boot/dts/rk3036-kylin.dts
@@ -46,6 +46,17 @@
 	model = "Rockchip RK3036 KylinBoard";
 	compatible = "rockchip,rk3036-kylin", "rockchip,rk3036";
 
+	leds: gpio-leds {
+		compatible = "gpio-leds";
+
+		work {
+			gpios = <&gpio2 30 GPIO_ACTIVE_HIGH>;
+			label = "kylin:red:led";
+			pinctrl-names = "default";
+			pinctrl-0 = <&led_ctl>;
+		};
+	};
+
 	sdio_pwrseq: sdio-pwrseq {
 		compatible = "mmc-pwrseq-simple";
 		pinctrl-names = "default";
@@ -359,6 +370,12 @@
 };
 
 &pinctrl {
+	leds {
+		led_ctl: led-ctl {
+			rockchip,pins = <2 30 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+
 	pmic {
 		pmic_int: pmic-int {
 			rockchip,pins = <2 2 RK_FUNC_GPIO &pcfg_pull_default>;
-- 
1.9.1



From hansverk at cisco.com  Wed Jan 27 00:48:11 2016
From: hansverk at cisco.com (Hans Verkuil)
Date: Wed, 27 Jan 2016 09:48:11 +0100
Subject: [PATCH v1 0/3] Add VP8 deocder for rk3229 & rk3288
In-Reply-To: <1986575881.543376.1453883667103.JavaMail.xmail@wmthree-7>
References: <1986575881.543376.1453883667103.JavaMail.xmail@wmthree-7>
Message-ID: <56A8844B.9070700@cisco.com>

Please be aware that the request API was RFC code and that 1) it *will*
change (Laurent Pinchart is working on it now) and 2) it may take quite
some time before it is merged. Second half of this year, and that only
if nothing goes wrong.

I can't tell if it is possible or not, but it might be a good idea if you
can make a version with reduced functionality that does not rely on the
request API, try to get that merged and add the functionality that
depends on the request API later once that part is merged.

For the record: while the request API will change the impact on your
driver is likely to be limited, at least as things stand today.

Regards,

	Hans

On 01/27/16 09:34, ?? wrote:
> Hi,
> These patches are based on Request API[1] and RK IOMMU[2].
> 
> [1]http://www.spinics.net/lists/linux-media/msg95733.html
> [2]http://www.gossamer-threads.com/lists/linux/kernel/2347458
> 
> The part for VP8 headers and controls for the V4L2 API and framework and the changes to videobuf2 were authored and written by Pawel Osciakand Tomasz Figa
> 
> Thank you for all suggestion. I will fix all the incorrect message and resend a new version.
> 
> ----- Original Message -----
> From:Enric Balletbo Serra <eballetbo at gmail.com>
> To: Shawn Lin <shawn.lin at rock-chips.com> 
> CC: Jung Zhao <jung.zhao at rock-chips.com> pawel <pawel at osciak.com> m.szyprowski <m.szyprowski at samsung.com> kyungmin.park <kyungmin.park at samsung.com> Mauro Carvalho Chehab <mchehab at osg.samsung.com> Heiko St?bner <heiko at sntech.de> Sakari Ailus <sakari.ailus at linux.intel.com> linux-api <linux-api at vger.kernel.org> linux-kernel <linux-kernel at vger.kernel.org> Benoit Parrot <bparrot at ti.com> linux-rockchip <linux-rockchip at lists.infradead.org> Antti Palosaari <crope at iki.fi> Hans Verkuil <hans.verkuil at cisco.com> alpha.lin <alpha.lin at rock-chips.com> Philipp Zabel <p.zabel at pengutronix.de> Ricardo Ribalda Delgado <ricardo.ribalda at gmail.com> herman.chen <herman.chen at rock-chips.com> linux-arm-kernel at lists.infradead.org <linux-arm-kernel at lists.infradead.org> linux-media <linux-media at vger.kernel.org> 
> Sent: 2016-01-26 22:00
> Subject: Re:Re: [PATCH v1 0/3] Add VP8 deocder for rk3229 & rk3288
> 
> Hi Jung,
> 
> 2016-01-26 10:30 GMT+01:00 Shawn Lin <shawn.lin at rock-chips.com>:
>> Hi jun,
>>
>> Where is the dt-bingding documentation about your VP8 controller?
>>
>> And would you please share some info about rk3229? I can just find
>> rk3228 in mainline, otherwise may someone think it's a misspell.
>>
>> Thanks.
>>
>>
>> On 2016/1/26 17:04, Jung Zhao wrote:
>>>
>>> From: zhaojun <jung.zhao at rock-chips.com>
>>>
>>>
>>> ====================
>>> Introduction
>>> ====================
>>>
>>> The purpose of this series is to add the driver for vp8
>>> decoder on rk3229 & rk3288 platform, and will support
>>> more formats in the future.
>>>
>>> The driver uses v4l2 framework and RK IOMMU.
>>> RK IOMMU has not yet been merged.
>>>
> 
> Can you share or specify what patches are needed, are they already
> send to upstream ? So people that want to test your series knows what
> they need to apply
> 
> I think that, at least, this patch is required:
> 
> iommu/rockchip: reconstruct to support multi slaves [1]
> 
> If this is not already accepted, maybe is a good idea include this
> patch in the patch series
> 
> [1] http://www.gossamer-threads.com/lists/linux/kernel/2347458
> 
>>>
>>>
>>> zhaojun (3):
>>>    media: v4l: Add VP8 format support in V4L2 framework
>>>    media: VPU: support Rockchip VPU
>>>    media: vcodec: rockchip: Add Rockchip VP8 decoder driver
>>>
>>>   drivers/media/platform/rockchip-vpu/Makefile       |    7 +
>>>   .../media/platform/rockchip-vpu/rkvpu_hw_vp8d.c    |  798 ++++++++++
>>>   .../platform/rockchip-vpu/rockchip_vp8d_regs.h     | 1594
>>> ++++++++++++++++++++
>>>   drivers/media/platform/rockchip-vpu/rockchip_vpu.c |  799 ++++++++++
>>>   .../platform/rockchip-vpu/rockchip_vpu_common.h    |  439 ++++++
>>>   .../media/platform/rockchip-vpu/rockchip_vpu_dec.c | 1007 +++++++++++++
>>>   .../media/platform/rockchip-vpu/rockchip_vpu_dec.h |   33 +
>>>   .../media/platform/rockchip-vpu/rockchip_vpu_hw.c  |  295 ++++
>>>   .../media/platform/rockchip-vpu/rockchip_vpu_hw.h  |  100 ++
>>>   drivers/media/v4l2-core/v4l2-ctrls.c               |   17 +-
>>>   drivers/media/v4l2-core/v4l2-ioctl.c               |    3 +
>>>   drivers/media/v4l2-core/videobuf2-dma-contig.c     |   51 +-
>>>   include/media/v4l2-ctrls.h                         |    2 +
>>>   include/media/videobuf2-dma-contig.h               |   11 +-
>>>   include/uapi/linux/v4l2-controls.h                 |   98 ++
>>>   include/uapi/linux/videodev2.h                     |    5 +
>>>   16 files changed, 5238 insertions(+), 21 deletions(-)
>>>   create mode 100644 drivers/media/platform/rockchip-vpu/Makefile
>>>   create mode 100644 drivers/media/platform/rockchip-vpu/rkvpu_hw_vp8d.c
>>>   create mode 100644
>>> drivers/media/platform/rockchip-vpu/rockchip_vp8d_regs.h
>>>   create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu.c
>>>   create mode 100644
>>> drivers/media/platform/rockchip-vpu/rockchip_vpu_common.h
>>>   create mode 100644
>>> drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.c
>>>   create mode 100644
>>> drivers/media/platform/rockchip-vpu/rockchip_vpu_dec.h
>>>   create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.c
>>>   create mode 100644 drivers/media/platform/rockchip-vpu/rockchip_vpu_hw.h
>>>
>>
>>
>> --
>> Best Regards
>> Shawn Lin
>>
> 
> Best Regards,
> Enric
> 


From zhengxing at rock-chips.com  Wed Jan 27 01:56:39 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Wed, 27 Jan 2016 17:56:39 +0800
Subject: [PATCH] clk: rockchip: fix wrong mmc phase shift for rk3228
In-Reply-To: <1453779018-30666-1-git-send-email-shawn.lin@rock-chips.com>
References: <1453779018-30666-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <56A89457.2050501@rock-chips.com>

Hi Shawn,
Reviewed-by: Xing Zheng <zhengxing at rock-chips.com>

Thanks.

On 2016?01?26? 11:30, Shawn Lin wrote:
> mmc sample shift is 0 for rk3228 refer to user manaul.
> So it's broken if we enable mmc tuning for rk3228.
>
> Fixes: 307a2e9ac ("clk: rockchip: add clock controller for rk3228")
> Cc: Xing Zheng<zhengxing at rock-chips.com>
> Cc: Jeffy Chen<jeffy.chen at rock-chips.com>
> Signed-off-by: Shawn Lin<shawn.lin at rock-chips.com>
> ---
>
>   drivers/clk/rockchip/clk-rk3228.c | 6 +++---
>   1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/clk/rockchip/clk-rk3228.c b/drivers/clk/rockchip/clk-rk3228.c
> index 981a502..97f49aa 100644
> --- a/drivers/clk/rockchip/clk-rk3228.c
> +++ b/drivers/clk/rockchip/clk-rk3228.c
> @@ -605,13 +605,13 @@ static struct rockchip_clk_branch rk3228_clk_branches[] __initdata = {
>
>   	/* PD_MMC */
>   	MMC(SCLK_SDMMC_DRV,    "sdmmc_drv",    "sclk_sdmmc", RK3228_SDMMC_CON0, 1),
> -	MMC(SCLK_SDMMC_SAMPLE, "sdmmc_sample", "sclk_sdmmc", RK3228_SDMMC_CON1, 1),
> +	MMC(SCLK_SDMMC_SAMPLE, "sdmmc_sample", "sclk_sdmmc", RK3228_SDMMC_CON1, 0),
>
>   	MMC(SCLK_SDIO_DRV,     "sdio_drv",     "sclk_sdio",  RK3228_SDIO_CON0,  1),
> -	MMC(SCLK_SDIO_SAMPLE,  "sdio_sample",  "sclk_sdio",  RK3228_SDIO_CON1,  1),
> +	MMC(SCLK_SDIO_SAMPLE,  "sdio_sample",  "sclk_sdio",  RK3228_SDIO_CON1,  0),
>
>   	MMC(SCLK_EMMC_DRV,     "emmc_drv",     "sclk_emmc",  RK3228_EMMC_CON0,  1),
> -	MMC(SCLK_EMMC_SAMPLE,  "emmc_sample",  "sclk_emmc",  RK3228_EMMC_CON1,  1),
> +	MMC(SCLK_EMMC_SAMPLE,  "emmc_sample",  "sclk_emmc",  RK3228_EMMC_CON1,  0),
>   };
>
>   static const char *const rk3228_critical_clocks[] __initconst = {




From kever.yang at rock-chips.com  Wed Jan 27 02:13:12 2016
From: kever.yang at rock-chips.com (Kever Yang)
Date: Wed, 27 Jan 2016 18:13:12 +0800
Subject: [PATCH v5 03/21] usb: dwc2: host: Set host_rx_fifo_size to 528
 for rk3066
In-Reply-To: <1453486736-15358-4-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
 <1453486736-15358-4-git-send-email-dianders@chromium.org>
Message-ID: <56A89838.3040202@rock-chips.com>

Hi Doug,

On 01/23/2016 02:18 AM, Douglas Anderson wrote:
> As documented in dwc2_calculate_dynamic_fifo(), host_rx_fifo_size should
> really be:
>   2 * ((Largest Packet size / 4) + 1 + 1) + n
>   with n = number of host channel.
>
> We have 9 host channels, so
>   2 * ((1024/4) + 2) + 9 = 516 + 9 = 525
>
> We've got 960 / 972 total_fifo_size on rk3288 (and presumably on
> rk3066) and 525 + 128 + 256 = 909 so we're still under on both ports
> even when we increment by 5.
>
> Since we have space, Kever Yang suggests bumping by 8.  He says this
> will meet INCR16 access and next fifo type can start with a aligned
> address.
I have double check this feature, the INCR16 is actually a burst 16
on to the bus, the aligned problem only happen on the DRAM
access, and it's OK for internal RAM access via internal DMA.

Bump to 8 looks much better when we check the FIFO setting if we
have enough memory, right? :)

rk3066/rk3188/rk3288 have the same hw config for dwc2 controller,
so this patch should works on those files.

Reviewed-by: Kever Yang <kever.yang at rock-chips.com>
>
> ...so let's bump up by 8.  In the future, it would be nice if
> dwc2_calculate_dynamic_fifo() could handle the "too small" FIFO case and
> come up with something more dynamically.  When we do that we can figure
> out how to allocate the extra 48 / 60 bytes of FIFO that we're currently
> wasting.
>
> NOTE: no known bugs are fixed by this patch, but it seems like a simple
> fix and ought to fix someone.
>
> Signed-off-by: Douglas Anderson <dianders at chromium.org>
> ---
> Changes in v5: None
> Changes in v4:
> - Set host_rx_fifo_size to 528 for rk3066 new for v4.
>
> Changes in v3: None
> Changes in v2: None
>
>   drivers/usb/dwc2/platform.c | 2 +-
>   1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
> index 5008a467ce06..b6d7666e715c 100644
> --- a/drivers/usb/dwc2/platform.c
> +++ b/drivers/usb/dwc2/platform.c
> @@ -126,7 +126,7 @@ static const struct dwc2_core_params params_rk3066 = {
>   	.speed				= -1,
>   	.enable_dynamic_fifo		= 1,
>   	.en_multiple_tx_fifo		= -1,
> -	.host_rx_fifo_size		= 520,	/* 520 DWORDs */
> +	.host_rx_fifo_size		= 528,	/* 528 DWORDs */
>   	.host_nperio_tx_fifo_size	= 128,	/* 128 DWORDs */
>   	.host_perio_tx_fifo_size	= 256,	/* 256 DWORDs */
>   	.max_transfer_size		= -1,




From tomeu at tomeuvizoso.net  Wed Jan 27 02:20:35 2016
From: tomeu at tomeuvizoso.net (Tomeu Vizoso)
Date: Wed, 27 Jan 2016 11:20:35 +0100
Subject: [PATCH 0/5] clk: rockchip: add full support for HDMI clock on
 rk3288
In-Reply-To: <CAD=FV=Une-DJ+0zV6Hmx5EmKXCEApb2Zx1FuJaUQf7MT9OPsug@mail.gmail.com>
References: <1415087559-19444-1-git-send-email-kever.yang@rock-chips.com>
 <48806799.thBSifdUp8@diego> <54647142.4080800@rock-chips.com>
 <CAD=FV=Vzec3Qm-kc6zZ4VxSREoZbzVkWbzp-noHYdcEVrLYYSw@mail.gmail.com>
 <CAAObsKChYN5nmcm5OWnAzNRGDi_QGSoKvN+0FizhjeT-3mmeXQ@mail.gmail.com>
 <CAD=FV=Wgu=xQ9PcVqfzcedXVU3Rh-HKbBVxHuqgV9OZJEYhYvg@mail.gmail.com>
 <CAAObsKC1v--mZwkiG-c7P2Bwi2-_txbeqaW4Vd5h-noAVNbhjA@mail.gmail.com>
 <CAD=FV=VB93wYigRJ8wa8YL6P+Q+-fRokPiCB-Fo1pC_zAdh2oA@mail.gmail.com>
 <CAAObsKCMh3Bk_oNJdLbU2F9b7jrnFZOp2uFR_oPqUoJ3W1gfaQ@mail.gmail.com>
 <CAD=FV=W=LCJk4m049hBnm7BOYudnVe=oa3jdcSrrZZUU9rFtog@mail.gmail.com>
 <CAAObsKBS45kbvdmdvFxwSo=B9GXRLoX2g7WQYVmZJ0KFnwSejw@mail.gmail.com>
 <CAD=FV=Une-DJ+0zV6Hmx5EmKXCEApb2Zx1FuJaUQf7MT9OPsug@mail.gmail.com>
Message-ID: <CAAObsKDsJgUA9dL+1Fdc616ofLdMqaUOwHhDkBWz7MV5_LJu9Q@mail.gmail.com>

On 26 January 2016 at 17:32, Doug Anderson <dianders at chromium.org> wrote:
> Tomeu,
>
> On Tue, Jan 26, 2016 at 12:28 AM, Tomeu Vizoso <tomeu at tomeuvizoso.net> wrote:
>> On 22 January 2016 at 18:07, Doug Anderson <dianders at chromium.org> wrote:
>>> Tomeu,
>>>
>>> On Fri, Jan 22, 2016 at 6:00 AM, Tomeu Vizoso <tomeu at tomeuvizoso.net> wrote:
>>>> On 21 January 2016 at 21:11, Doug Anderson <dianders at chromium.org> wrote:
>>>>> Hi,
>>>>>
>>>>> On Thu, Jan 21, 2016 at 1:03 AM, Tomeu Vizoso <tomeu at tomeuvizoso.net> wrote:
>>>>>> So we have a mechanism for detecting a conflict in the clock
>>>>>> hierarchy, and a mechanism to solve it, but we are missing a way for
>>>>>> userspace to communicate policy regarding which clocks should be given
>>>>>> priority when solving such a conflict?
>>>>>
>>>>> Hrmmm, I guess it could be userspace that makes the decision.  It does
>>>>> seem a little odd to force it to userspace in all cases, though.  For
>>>>> a particular laptop that is designed with a specific panel connected
>>>>> up eDP it seems less than ideal to push this into userspace.  If the
>>>>> kernel could just work in the expected sane way (or at least work that
>>>>> way by default) it would be ideal.
>>>>
>>>> Ah, I was wrongly assuming that the kernel didn't have enough
>>>> information to make an informed decision in this case, sorry.
>>>>
>>>> Guess the per-user rate limits don't help here because the consumer
>>>> with higher priority could work with frequencies other than the ideal.
>>>>
>>>> And we cannot have a consumer listening for PRE_RATE_CHANGE and
>>>> aborting unwanted changes or rerouting the ancestors of the clocks of
>>>> other consumers because that would be a massive violation of
>>>> separation of concerns.
>>>>
>>>> If we were to rearrange the clock topology from within the CCF, then
>>>> consumers need to have a way to communicate to the core that they are
>>>> more important than other consumers. clk_set_important(clk, true)
>>>> could be enough in this case, but would be insufficient in more
>>>> complex cases where more than two clocks could use the same PLL.
>>>
>>> With something like the above I'd still expect some complexity
>>> depending on the probe order.  If a less important device grabs the
>>> clock first, it might be forced to re-think its clocks later.  That
>>> might be disconcerting.
>>
>> How much disconcerting do you think this could be? Hopefully those
>> devices should probe quite close to each other, right?
>
> Probe: probably, though with defers it could be several seconds.
>
> ...but remember that display interfaces tend to be hotplug.  That
> might mean that the HDMI interface won't try to set the clock until
> much, much later.

I'm still having trouble grasping what's the impact to users. Is it
that if HDMI gets the contended clock first and the internal panel
device only probes after a second or so, then the user may notice a
change in frequency in the HDMI screen when the panel lights up?

Though in that particular case I'm not sure if the impact is that big
for the user, wonder if such rearranging of the clock hierarchy can
cause bigger problems in other situations.

The on-demand probing series could help here because a downstream that
cared about these issues could just rearrange the contents of the DT
(maybe with a script as part of the build process) so that the panel
is always probed first, but well, that one collided with an iceberg.

>>> OK, so I was just involved in a change recently that made me realize
>>> that maybe our original problems were tied to the fact that our
>>> builtin panels were trying to specify a clock that was impossible to
>>> achieve with CPLL / GPLL.  It was a known problem that the request
>>> would be denied and the CCF would just pick the closest rate it could.
>>> Probably the right thing is to solve _that_ problem first.  If using
>>> simple panel you could do a change like
>>> <https://chromium-review.googlesource.com/#/c/323211/> (though
>>> presumably you'd have to handle people using the same panel in other
>>> laptops).  You might also be able to do funny things to fixup the mode
>>> like dbehr tried to do in
>>> <https://chromium-review.googlesource.com/#/c/270017/>.  By doing this
>>> and making sure that
>>
>> Are we missing something here?
>
> Eh?

The sentence above seemed to have been cut in the middle and I was
wondering if there's something relevant I'm missing because of it.

Thanks,

Tomeu


From eballetbo at gmail.com  Wed Jan 27 02:21:43 2016
From: eballetbo at gmail.com (Enric Balletbo Serra)
Date: Wed, 27 Jan 2016 11:21:43 +0100
Subject: [PATCH v1 1/3] media: v4l: Add VP8 format support in V4L2
 framework
In-Reply-To: <1453852413.5584.15.camel@collabora.com>
References: <1453799046-307-1-git-send-email-jung.zhao@rock-chips.com>
 <1453799046-307-2-git-send-email-jung.zhao@rock-chips.com>
 <1453852413.5584.15.camel@collabora.com>
Message-ID: <CAFqH_5233Ge2eNk1Fqr1sy8Npp7mixL3fPWAdpBJ4NcZfFhjSQ@mail.gmail.com>

Hi Jung,

Also, if I'm not wrong these changes are a mix of these three patches
available in the chromium tree[1], it's a good practice cherry-pick
the patches (removing the CHROMIUM tags, rebase against mainline...)
cc the authors and maintain the author and the signed-off chain. Note
also that the patches have a good explanation so better use the
original ones.

Best regards,
Enric

[1]

Author: Tomasz Figa <tfiga at chromium.org>
Date:   Mon Apr 13 14:34:06 2015 +0900

    CHROMIUM: [media] videobuf2-dc: Let drivers specify DMA attrs

    DMA allocations might be subject to certain reqiurements specific to the
    hardware using the buffers, such as availability of kernel mapping (for
    contents fix-ups in the driver). The only entity that knows them is the
    driver, so it must share this knowledge with vb2-dc.

    This patch extends the alloc_ctx initialization interface to let the
    driver specify DMA attrs, which are then stored inside the allocation
    context and will be used for all allocations with that context.

    As a side effect, all dma_*_coherent() calls are turned into
    dma_*_attrs() calls, because the attributes need to be carried over
    through all DMA operations.

Author: Pawel Osciak <posciak at chromium.org>
Date:   Fri Dec 26 10:37:33 2014 +0900

    CHROMIUM: v4l: Add VP8 low-level decoder API controls.

    These controls are to be used with the new low-level decoder API for VP8
    to provide additional parameters for the hardware that cannot parse the
    input stream.

Author: Pawel Osciak <posciak at chromium.org>
Date:   Tue Nov 11 20:44:28 2014 +0900

    CHROMIUM: [media] v4l: Add private compound control type.

    V4L2_CTRL_TYPE_PRIVATE is to be used for private driver compound
    controls that use the "ptr" member of struct v4l2_ext_control.


2016-01-27 0:53 GMT+01:00 Nicolas Dufresne <nicolas.dufresne at collabora.com>:
> Hi Jung,
>
> this patch adds new public API to videodev2.h. It would be appropriate
> to also add the associated documentation for it (see Documentation/DocBook/media). I also believe there is multiple way VP8 support could have been added. A proper commit message that explains the approach would also be appropriate.
>
> cheers,
> Nicolas
>
> Le mardi 26 janvier 2016 ? 17:04 +0800, Jung Zhao a ?crit :
>> From: zhaojun <jung.zhao at rock-chips.com>
>>
>> Signed-off-by: zhaojun <jung.zhao at rock-chips.com>
>> ---
>>
>>  drivers/media/v4l2-core/v4l2-ctrls.c           | 17 ++++-
>>  drivers/media/v4l2-core/v4l2-ioctl.c           |  3 +
>>  drivers/media/v4l2-core/videobuf2-dma-contig.c | 51 +++++++++-----
>>  include/media/v4l2-ctrls.h                     |  2 +
>>  include/media/videobuf2-dma-contig.h           | 11 ++-
>>  include/uapi/linux/v4l2-controls.h             | 98
>> ++++++++++++++++++++++++++
>>  include/uapi/linux/videodev2.h                 |  5 ++
>>  7 files changed, 166 insertions(+), 21 deletions(-)
>>
>> diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c
>> b/drivers/media/v4l2-core/v4l2-ctrls.c
>> index 890520d..22821e94 100644
>> --- a/drivers/media/v4l2-core/v4l2-ctrls.c
>> +++ b/drivers/media/v4l2-core/v4l2-ctrls.c
>> @@ -761,7 +761,7 @@ const char *v4l2_ctrl_get_name(u32 id)
>>       case V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP:                retu
>> rn "VPX I-Frame QP Value";
>>       case V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP:                retu
>> rn "VPX P-Frame QP Value";
>>       case V4L2_CID_MPEG_VIDEO_VPX_PROFILE:
>> return "VPX Profile";
>> -
>> +     case V4L2_CID_MPEG_VIDEO_VP8_FRAME_HDR:
>> return "VP8 Frame Header";
>>       /* CAMERA controls */
>>       /* Keep the order of the 'case's the same as in v4l2-
>> controls.h! */
>>       case V4L2_CID_CAMERA_CLASS:             return "Camera
>> Controls";
>> @@ -1126,6 +1126,9 @@ void v4l2_ctrl_fill(u32 id, const char **name,
>> enum v4l2_ctrl_type *type,
>>       case V4L2_CID_RDS_TX_ALT_FREQS:
>>               *type = V4L2_CTRL_TYPE_U32;
>>               break;
>> +     case V4L2_CID_MPEG_VIDEO_VP8_FRAME_HDR:
>> +             *type = V4L2_CTRL_TYPE_VP8_FRAME_HDR;
>> +             break;
>>       default:
>>               *type = V4L2_CTRL_TYPE_INTEGER;
>>               break;
>> @@ -1525,6 +1528,13 @@ static int std_validate(const struct v4l2_ctrl
>> *ctrl, u32 idx,
>>                       return -ERANGE;
>>               return 0;
>>
>> +     /* FIXME:just return 0 for now */
>> +     case V4L2_CTRL_TYPE_PRIVATE:
>> +             return 0;
>> +
>> +     case V4L2_CTRL_TYPE_VP8_FRAME_HDR:
>> +             return 0;
>> +
>>       default:
>>               return -EINVAL;
>>       }
>> @@ -2074,6 +2084,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct
>> v4l2_ctrl_handler *hdl,
>>       case V4L2_CTRL_TYPE_U32:
>>               elem_size = sizeof(u32);
>>               break;
>> +     case V4L2_CTRL_TYPE_VP8_FRAME_HDR:
>> +             elem_size = sizeof(struct v4l2_ctrl_vp8_frame_hdr);
>> +             break;
>>       default:
>>               if (type < V4L2_CTRL_COMPOUND_TYPES)
>>                       elem_size = sizeof(s32);
>> @@ -2098,7 +2111,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct
>> v4l2_ctrl_handler *hdl,
>>               handler_set_err(hdl, -ERANGE);
>>               return NULL;
>>       }
>> -     if (is_array &&
>> +     if ((is_array || (flags & V4L2_CTRL_FLAG_REQ_KEEP)) &&
>>           (type == V4L2_CTRL_TYPE_BUTTON ||
>>            type == V4L2_CTRL_TYPE_CTRL_CLASS)) {
>>               handler_set_err(hdl, -EINVAL);
>> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c
>> b/drivers/media/v4l2-core/v4l2-ioctl.c
>> index 7d028d1..8aa5812 100644
>> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
>> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
>> @@ -1259,6 +1259,9 @@ static void v4l_fill_fmtdesc(struct
>> v4l2_fmtdesc *fmt)
>>               case V4L2_PIX_FMT_VC1_ANNEX_G:  descr = "VC-1
>> (SMPTE 412M Annex G)"; break;
>>               case V4L2_PIX_FMT_VC1_ANNEX_L:  descr = "VC-1
>> (SMPTE 412M Annex L)"; break;
>>               case V4L2_PIX_FMT_VP8:          descr = "VP8";
>> break;
>> +             case V4L2_PIX_FMT_VP8_FRAME:
>> +                     descr = "VP8 FRAME";
>> +                     break;
>>               case V4L2_PIX_FMT_CPIA1:        descr = "GSPCA CPiA
>> YUV"; break;
>>               case V4L2_PIX_FMT_WNVA:         descr =
>> "WNVA"; break;
>>               case V4L2_PIX_FMT_SN9C10X:      descr = "GSPCA
>> SN9C10X"; break;
>> diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c
>> b/drivers/media/v4l2-core/videobuf2-dma-contig.c
>> index c331272..aebcc7f 100644
>> --- a/drivers/media/v4l2-core/videobuf2-dma-contig.c
>> +++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c
>> @@ -23,13 +23,16 @@
>>
>>  struct vb2_dc_conf {
>>       struct device           *dev;
>> +     struct dma_attrs        attrs;
>>  };
>>
>>  struct vb2_dc_buf {
>>       struct device                   *dev;
>>       void                            *vaddr;
>>       unsigned long                   size;
>> +     void                            *cookie;
>>       dma_addr_t                      dma_addr;
>> +     struct dma_attrs                attrs;
>>       enum dma_data_direction         dma_dir;
>>       struct sg_table                 *dma_sgt;
>>       struct frame_vector             *vec;
>> @@ -131,7 +134,8 @@ static void vb2_dc_put(void *buf_priv)
>>               sg_free_table(buf->sgt_base);
>>               kfree(buf->sgt_base);
>>       }
>> -     dma_free_coherent(buf->dev, buf->size, buf->vaddr, buf-
>> >dma_addr);
>> +     dma_free_attrs(buf->dev, buf->size, buf->cookie, buf-
>> >dma_addr,
>> +                     &buf->attrs);
>>       put_device(buf->dev);
>>       kfree(buf);
>>  }
>> @@ -143,18 +147,22 @@ static void *vb2_dc_alloc(void *alloc_ctx,
>> unsigned long size,
>>       struct device *dev = conf->dev;
>>       struct vb2_dc_buf *buf;
>>
>> -     buf = kzalloc(sizeof *buf, GFP_KERNEL);
>> +     buf = kzalloc(sizeof(*buf), GFP_KERNEL);
>>       if (!buf)
>>               return ERR_PTR(-ENOMEM);
>>
>> -     buf->vaddr = dma_alloc_coherent(dev, size, &buf->dma_addr,
>> -                                             GFP_KERNEL |
>> gfp_flags);
>> -     if (!buf->vaddr) {
>> +     buf->attrs = conf->attrs;
>> +     buf->cookie = dma_alloc_attrs(dev, size, &buf->dma_addr,
>> +                                     GFP_KERNEL | gfp_flags,
>> &buf->attrs);
>> +     if (!buf->cookie) {
>>               dev_err(dev, "dma_alloc_coherent of size %ld
>> failed\n", size);
>>               kfree(buf);
>>               return ERR_PTR(-ENOMEM);
>>       }
>>
>> +     if (!dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, &buf->attrs))
>> +             buf->vaddr = buf->cookie;
>> +
>>       /* Prevent the device from being released while the buffer
>> is used */
>>       buf->dev = get_device(dev);
>>       buf->size = size;
>> @@ -185,8 +193,8 @@ static int vb2_dc_mmap(void *buf_priv, struct
>> vm_area_struct *vma)
>>        */
>>       vma->vm_pgoff = 0;
>>
>> -     ret = dma_mmap_coherent(buf->dev, vma, buf->vaddr,
>> -             buf->dma_addr, buf->size);
>> +     ret = dma_mmap_attrs(buf->dev, vma, buf->cookie,
>> +             buf->dma_addr, buf->size, &buf->attrs);
>>
>>       if (ret) {
>>               pr_err("Remapping memory failed, error: %d\n", ret);
>> @@ -329,7 +337,7 @@ static void *vb2_dc_dmabuf_ops_kmap(struct
>> dma_buf *dbuf, unsigned long pgnum)
>>  {
>>       struct vb2_dc_buf *buf = dbuf->priv;
>>
>> -     return buf->vaddr + pgnum * PAGE_SIZE;
>> +     return buf->vaddr ? buf->vaddr + pgnum * PAGE_SIZE : NULL;
>>  }
>>
>>  static void *vb2_dc_dmabuf_ops_vmap(struct dma_buf *dbuf)
>> @@ -368,8 +376,8 @@ static struct sg_table
>> *vb2_dc_get_base_sgt(struct vb2_dc_buf *buf)
>>               return NULL;
>>       }
>>
>> -     ret = dma_get_sgtable(buf->dev, sgt, buf->vaddr, buf-
>> >dma_addr,
>> -             buf->size);
>> +     ret = dma_get_sgtable_attrs(buf->dev, sgt, buf->cookie, buf-
>> >dma_addr,
>> +             buf->size, &buf->attrs);
>>       if (ret < 0) {
>>               dev_err(buf->dev, "failed to get scatterlist from
>> DMA API\n");
>>               kfree(sgt);
>> @@ -448,22 +456,26 @@ static void vb2_dc_put_userptr(void *buf_priv)
>>   */
>>
>>  #ifdef __arch_pfn_to_dma
>> -static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev,
>> unsigned long pfn)
>> +static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev,
>> +                                        unsigned long pfn)
>>  {
>>       return (dma_addr_t)__arch_pfn_to_dma(dev, pfn);
>>  }
>>  #elif defined(__pfn_to_bus)
>> -static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev,
>> unsigned long pfn)
>> +static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev,
>> +                                        unsigned long pfn)
>>  {
>>       return (dma_addr_t)__pfn_to_bus(pfn);
>>  }
>>  #elif defined(__pfn_to_phys)
>> -static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev,
>> unsigned long pfn)
>> +static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev,
>> +                                        unsigned long pfn)
>>  {
>>       return (dma_addr_t)__pfn_to_phys(pfn);
>>  }
>>  #else
>> -static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev,
>> unsigned long pfn)
>> +static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev,
>> +                                        unsigned long pfn)
>>  {
>>       /* really, we cannot do anything better at this point */
>>       return (dma_addr_t)(pfn) << PAGE_SHIFT;
>> @@ -497,7 +509,7 @@ static void *vb2_dc_get_userptr(void *alloc_ctx,
>> unsigned long vaddr,
>>               return ERR_PTR(-EINVAL);
>>       }
>>
>> -     buf = kzalloc(sizeof *buf, GFP_KERNEL);
>> +     buf = kzalloc(sizeof(*buf), GFP_KERNEL);
>>       if (!buf)
>>               return ERR_PTR(-ENOMEM);
>>
>> @@ -721,19 +733,22 @@ const struct vb2_mem_ops vb2_dma_contig_memops
>> = {
>>  };
>>  EXPORT_SYMBOL_GPL(vb2_dma_contig_memops);
>>
>> -void *vb2_dma_contig_init_ctx(struct device *dev)
>> +void *vb2_dma_contig_init_ctx_attrs(struct device *dev,
>> +                                 struct dma_attrs *attrs)
>>  {
>>       struct vb2_dc_conf *conf;
>>
>> -     conf = kzalloc(sizeof *conf, GFP_KERNEL);
>> +     conf = kzalloc(sizeof(*conf), GFP_KERNEL);
>>       if (!conf)
>>               return ERR_PTR(-ENOMEM);
>>
>>       conf->dev = dev;
>> +     if (attrs)
>> +             conf->attrs = *attrs;
>>
>>       return conf;
>>  }
>> -EXPORT_SYMBOL_GPL(vb2_dma_contig_init_ctx);
>> +EXPORT_SYMBOL_GPL(vb2_dma_contig_init_ctx_attrs);
>>
>>  void vb2_dma_contig_cleanup_ctx(void *alloc_ctx)
>>  {
>> diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
>> index 5f9526f..0424cdc 100644
>> --- a/include/media/v4l2-ctrls.h
>> +++ b/include/media/v4l2-ctrls.h
>> @@ -46,6 +46,7 @@ struct poll_table_struct;
>>   * @p_u16:   Pointer to a 16-bit unsigned value.
>>   * @p_u32:   Pointer to a 32-bit unsigned value.
>>   * @p_char:  Pointer to a string.
>> + * @p_vp8_frame_hdr: Pointer to a struct
>> v4l2_ctrl_vp8_frame_hdr.
>>   * @p:               Pointer to a compound value.
>>   */
>>  union v4l2_ctrl_ptr {
>> @@ -55,6 +56,7 @@ union v4l2_ctrl_ptr {
>>       u16 *p_u16;
>>       u32 *p_u32;
>>       char *p_char;
>> +     struct v4l2_ctrl_vp8_frame_hdr *p_vp8_frame_hdr;
>>       void *p;
>>  };
>>
>> diff --git a/include/media/videobuf2-dma-contig.h
>> b/include/media/videobuf2-dma-contig.h
>> index c33dfa6..2087c9a 100644
>> --- a/include/media/videobuf2-dma-contig.h
>> +++ b/include/media/videobuf2-dma-contig.h
>> @@ -16,6 +16,8 @@
>>  #include <media/videobuf2-v4l2.h>
>>  #include <linux/dma-mapping.h>
>>
>> +struct dma_attrs;
>> +
>>  static inline dma_addr_t
>>  vb2_dma_contig_plane_dma_addr(struct vb2_buffer *vb, unsigned int
>> plane_no)
>>  {
>> @@ -24,7 +26,14 @@ vb2_dma_contig_plane_dma_addr(struct vb2_buffer
>> *vb, unsigned int plane_no)
>>       return *addr;
>>  }
>>
>> -void *vb2_dma_contig_init_ctx(struct device *dev);
>> +void *vb2_dma_contig_init_ctx_attrs(struct device *dev,
>> +                                 struct dma_attrs *attrs);
>> +
>> +static inline void *vb2_dma_contig_init_ctx(struct device *dev)
>> +{
>> +     return vb2_dma_contig_init_ctx_attrs(dev, NULL);
>> +}
>> +
>>  void vb2_dma_contig_cleanup_ctx(void *alloc_ctx);
>>
>>  extern const struct vb2_mem_ops vb2_dma_contig_memops;
>> diff --git a/include/uapi/linux/v4l2-controls.h
>> b/include/uapi/linux/v4l2-controls.h
>> index 2d225bc..63c65d9 100644
>> --- a/include/uapi/linux/v4l2-controls.h
>> +++ b/include/uapi/linux/v4l2-controls.h
>> @@ -520,6 +520,7 @@ enum
>> v4l2_mpeg_video_h264_hierarchical_coding_type {
>>  };
>>  #define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER   (V
>> 4L2_CID_MPEG_BASE+381)
>>  #define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_QP
>> (V4L2_CID_MPEG_BASE+382)
>> +
>>  #define V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP (V4L2_CID_MPEG_B
>> ASE+400)
>>  #define V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP (V4L2_CID_MPEG_B
>> ASE+401)
>>  #define V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP (V4L2_CID_MPEG_B
>> ASE+402)
>> @@ -578,6 +579,8 @@ enum v4l2_vp8_golden_frame_sel {
>>  #define V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP           (V4L2_CID_
>> MPEG_BASE+510)
>>  #define V4L2_CID_MPEG_VIDEO_VPX_PROFILE                      (V4L2
>> _CID_MPEG_BASE+511)
>>
>> +#define V4L2_CID_MPEG_VIDEO_VP8_FRAME_HDR            (V4L2_CID_M
>> PEG_BASE+512)
>> +
>>  /*  MPEG-class control IDs specific to the CX2341x driver as defined
>> by V4L2 */
>>  #define V4L2_CID_MPEG_CX2341X_BASE                           (
>> V4L2_CTRL_CLASS_MPEG | 0x1000)
>>  #define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE      (V4L
>> 2_CID_MPEG_CX2341X_BASE+0)
>> @@ -963,4 +966,99 @@ enum v4l2_detect_md_mode {
>>  #define V4L2_CID_DETECT_MD_THRESHOLD_GRID    (V4L2_CID_DETECT_CL
>> ASS_BASE + 3)
>>  #define V4L2_CID_DETECT_MD_REGION_GRID               (V4L2_CID_DETE
>> CT_CLASS_BASE + 4)
>>
>> +
>> +/* Complex controls */
>> +
>> +#define V4L2_VP8_SEGMNT_HDR_FLAG_ENABLED              0x01
>> +#define V4L2_VP8_SEGMNT_HDR_FLAG_UPDATE_MAP           0x02
>> +#define V4L2_VP8_SEGMNT_HDR_FLAG_UPDATE_FEATURE_DATA  0x04
>> +struct v4l2_vp8_sgmnt_hdr {
>> +     __u8 segment_feature_mode;
>> +
>> +     __s8 quant_update[4];
>> +     __s8 lf_update[4];
>> +     __u8 segment_probs[3];
>> +
>> +     __u8 flags;
>> +};
>> +
>> +#define V4L2_VP8_LF_HDR_ADJ_ENABLE   0x01
>> +#define V4L2_VP8_LF_HDR_DELTA_UPDATE 0x02
>> +struct v4l2_vp8_loopfilter_hdr {
>> +     __u8 type;
>> +     __u8 level;
>> +     __u8 sharpness_level;
>> +     __s8 ref_frm_delta_magnitude[4];
>> +     __s8 mb_mode_delta_magnitude[4];
>> +
>> +     __u8 flags;
>> +};
>> +
>> +struct v4l2_vp8_quantization_hdr {
>> +     __u8 y_ac_qi;
>> +     __s8 y_dc_delta;
>> +     __s8 y2_dc_delta;
>> +     __s8 y2_ac_delta;
>> +     __s8 uv_dc_delta;
>> +     __s8 uv_ac_delta;
>> +     __u16 dequant_factors[4][3][2];
>> +};
>> +
>> +struct v4l2_vp8_entropy_hdr {
>> +     __u8 coeff_probs[4][8][3][11];
>> +     __u8 y_mode_probs[4];
>> +     __u8 uv_mode_probs[3];
>> +     __u8 mv_probs[2][19];
>> +};
>> +
>> +#define V4L2_VP8_FRAME_HDR_FLAG_EXPERIMENTAL         0x01
>> +#define V4L2_VP8_FRAME_HDR_FLAG_SHOW_FRAME           0x02
>> +#define V4L2_VP8_FRAME_HDR_FLAG_MB_NO_SKIP_COEFF     0x04
>> +struct v4l2_ctrl_vp8_frame_hdr {
>> +     /* 0: keyframe, 1: not a keyframe */
>> +     __u8 key_frame;
>> +     __u8 version;
>> +
>> +     /* Populated also if not a key frame */
>> +     __u16 width;
>> +     __u8 horizontal_scale;
>> +     __u16 height;
>> +     __u8 vertical_scale;
>> +
>> +     struct v4l2_vp8_sgmnt_hdr sgmnt_hdr;
>> +     struct v4l2_vp8_loopfilter_hdr lf_hdr;
>> +     struct v4l2_vp8_quantization_hdr quant_hdr;
>> +     struct v4l2_vp8_entropy_hdr entropy_hdr;
>> +
>> +     __u8 sign_bias_golden;
>> +     __u8 sign_bias_alternate;
>> +
>> +     __u8 prob_skip_false;
>> +     __u8 prob_intra;
>> +     __u8 prob_last;
>> +     __u8 prob_gf;
>> +
>> +     __u32 first_part_size;
>> +     __u32 first_part_offset;
>> +     /*
>> +      * Offset in bits of MB data in first partition,
>> +      * i.e. bit offset starting from first_part_offset.
>> +      */
>> +     __u32 macroblock_bit_offset;
>> +
>> +     __u8 num_dct_parts;
>> +     __u32 dct_part_sizes[8];
>> +
>> +     __u8 bool_dec_range;
>> +     __u8 bool_dec_value;
>> +     __u8 bool_dec_count;
>> +
>> +     /* v4l2_buffer indices of reference frames */
>> +     __u32 last_frame;
>> +     __u32 golden_frame;
>> +     __u32 alt_frame;
>> +
>> +     __u8 flags;
>> +};
>> +
>>  #endif
>> diff --git a/include/uapi/linux/videodev2.h
>> b/include/uapi/linux/videodev2.h
>> index 29a6b78..191ca19 100644
>> --- a/include/uapi/linux/videodev2.h
>> +++ b/include/uapi/linux/videodev2.h
>> @@ -593,6 +593,7 @@ struct v4l2_pix_format {
>>  #define V4L2_PIX_FMT_VC1_ANNEX_G v4l2_fourcc('V', 'C', '1', 'G') /*
>> SMPTE 421M Annex G compliant stream */
>>  #define V4L2_PIX_FMT_VC1_ANNEX_L v4l2_fourcc('V', 'C', '1', 'L') /*
>> SMPTE 421M Annex L compliant stream */
>>  #define V4L2_PIX_FMT_VP8      v4l2_fourcc('V', 'P', '8', '0') /* VP8
>> */
>> +#define V4L2_PIX_FMT_VP8_FRAME       v4l2_fourcc('V', 'P', '8',
>> 'F') /* VP8 parsed frames */
>>
>>  /*  Vendor-specific formats   */
>>  #define V4L2_PIX_FMT_CPIA1    v4l2_fourcc('C', 'P', 'I', 'A') /*
>> cpia1 YUV */
>> @@ -1473,6 +1474,7 @@ struct v4l2_ext_control {
>>               __u8 __user *p_u8;
>>               __u16 __user *p_u16;
>>               __u32 __user *p_u32;
>> +             struct v4l2_ctrl_vp8_frame_hdr __user
>> *p_vp8_frame_hdr;
>>               void __user *ptr;
>>       };
>>  } __attribute__ ((packed));
>> @@ -1517,6 +1519,9 @@ enum v4l2_ctrl_type {
>>       V4L2_CTRL_TYPE_U8            = 0x0100,
>>       V4L2_CTRL_TYPE_U16           = 0x0101,
>>       V4L2_CTRL_TYPE_U32           = 0x0102,
>> +     V4L2_CTRL_TYPE_VP8_FRAME_HDR    = 0x108,
>> +
>> +     V4L2_CTRL_TYPE_PRIVATE       = 0xffff,
>>  };
>>
>>  /*  Used in the VIDIOC_QUERYCTRL ioctl for querying controls */


From kever.yang at rock-chips.com  Wed Jan 27 02:23:54 2016
From: kever.yang at rock-chips.com (Kever Yang)
Date: Wed, 27 Jan 2016 18:23:54 +0800
Subject: [PATCH v5 06/21] usb: dwc2: host: Always add to the tail of queues
In-Reply-To: <1453486736-15358-7-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
 <1453486736-15358-7-git-send-email-dianders@chromium.org>
Message-ID: <56A89ABA.7000809@rock-chips.com>

Hi Doug,

This is obviously a bug in dwc2 driver which not meet the usb 2.0
spec, and this patch can fix it.

Reviewed-by: Kever Yang <kever.yang at rock-chips.com>

Thanks,
- Kever
On 01/23/2016 02:18 AM, Douglas Anderson wrote:
> The queues the the dwc2 host controller used are truly queues.  That
> means FIFO or first in first out.
>
> Unfortunately though the code was iterating through these queues
> starting from the head, some places in the code was adding things to the
> queue by adding at the head instead of the tail.  That means last in
> first out.  Doh.
>
> Go through and just always add to the tail.
>
> Doing this makes things much happier when I've got:
> * 7-port USB 2.0 Single-TT hub
> * - Microsoft 2.4 GHz Transceiver v7.0 dongle
> * - Jabra speakerphone playing music
>
> Signed-off-by: Douglas Anderson <dianders at chromium.org>
> ---
> Changes in v5: None
> Changes in v4:
> - Always add to the tail of queues new for v4.
>
> Changes in v3: None
> Changes in v2: None
>
>   drivers/usb/dwc2/hcd.c       | 11 ++++++-----
>   drivers/usb/dwc2/hcd_ddma.c  |  4 ++--
>   drivers/usb/dwc2/hcd_intr.c  |  4 ++--
>   drivers/usb/dwc2/hcd_queue.c |  6 ++++--
>   4 files changed, 14 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
> index 7783c8ba0173..d2daaea88d91 100644
> --- a/drivers/usb/dwc2/hcd.c
> +++ b/drivers/usb/dwc2/hcd.c
> @@ -969,7 +969,8 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions(
>   		 * periodic assigned schedule
>   		 */
>   		qh_ptr = qh_ptr->next;
> -		list_move(&qh->qh_list_entry, &hsotg->periodic_sched_assigned);
> +		list_move_tail(&qh->qh_list_entry,
> +			       &hsotg->periodic_sched_assigned);
>   		ret_val = DWC2_TRANSACTION_PERIODIC;
>   	}
>   
> @@ -1002,8 +1003,8 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions(
>   		 * non-periodic active schedule
>   		 */
>   		qh_ptr = qh_ptr->next;
> -		list_move(&qh->qh_list_entry,
> -			  &hsotg->non_periodic_sched_active);
> +		list_move_tail(&qh->qh_list_entry,
> +			       &hsotg->non_periodic_sched_active);
>   
>   		if (ret_val == DWC2_TRANSACTION_NONE)
>   			ret_val = DWC2_TRANSACTION_NON_PERIODIC;
> @@ -1176,8 +1177,8 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg)
>   			 * Move the QH from the periodic assigned schedule to
>   			 * the periodic queued schedule
>   			 */
> -			list_move(&qh->qh_list_entry,
> -				  &hsotg->periodic_sched_queued);
> +			list_move_tail(&qh->qh_list_entry,
> +				       &hsotg->periodic_sched_queued);
>   
>   			/* done queuing high bandwidth */
>   			hsotg->queuing_high_bandwidth = 0;
> diff --git a/drivers/usb/dwc2/hcd_ddma.c b/drivers/usb/dwc2/hcd_ddma.c
> index 36606fc33c0d..16b261cfa92d 100644
> --- a/drivers/usb/dwc2/hcd_ddma.c
> +++ b/drivers/usb/dwc2/hcd_ddma.c
> @@ -1327,8 +1327,8 @@ void dwc2_hcd_complete_xfer_ddma(struct dwc2_hsotg *hsotg,
>   			dwc2_hcd_qh_unlink(hsotg, qh);
>   		} else {
>   			/* Keep in assigned schedule to continue transfer */
> -			list_move(&qh->qh_list_entry,
> -				  &hsotg->periodic_sched_assigned);
> +			list_move_tail(&qh->qh_list_entry,
> +				       &hsotg->periodic_sched_assigned);
>   			/*
>   			 * If channel has been halted during giveback of urb
>   			 * then prevent any new scheduling.
> diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
> index 99efc2bd1617..2c521c00e5e0 100644
> --- a/drivers/usb/dwc2/hcd_intr.c
> +++ b/drivers/usb/dwc2/hcd_intr.c
> @@ -143,7 +143,7 @@ static void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
>   			 * Move QH to the ready list to be executed next
>   			 * (micro)frame
>   			 */
> -			list_move(&qh->qh_list_entry,
> +			list_move_tail(&qh->qh_list_entry,
>   				  &hsotg->periodic_sched_ready);
>   	}
>   	tr_type = dwc2_hcd_select_transactions(hsotg);
> @@ -794,7 +794,7 @@ static void dwc2_halt_channel(struct dwc2_hsotg *hsotg,
>   			 * halt to be queued when the periodic schedule is
>   			 * processed.
>   			 */
> -			list_move(&chan->qh->qh_list_entry,
> +			list_move_tail(&chan->qh->qh_list_entry,
>   				  &hsotg->periodic_sched_assigned);
>   
>   			/*
> diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
> index e0933a9dfad7..bc632a72f611 100644
> --- a/drivers/usb/dwc2/hcd_queue.c
> +++ b/drivers/usb/dwc2/hcd_queue.c
> @@ -732,9 +732,11 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
>   	     dwc2_frame_num_le(qh->sched_frame, frame_number)) ||
>   	    (hsotg->core_params->uframe_sched <= 0 &&
>   	     qh->sched_frame == frame_number))
> -		list_move(&qh->qh_list_entry, &hsotg->periodic_sched_ready);
> +		list_move_tail(&qh->qh_list_entry,
> +			       &hsotg->periodic_sched_ready);
>   	else
> -		list_move(&qh->qh_list_entry, &hsotg->periodic_sched_inactive);
> +		list_move_tail(&qh->qh_list_entry,
> +			       &hsotg->periodic_sched_inactive);
>   }
>   
>   /**




From moritz.fischer at ettus.com  Wed Jan 27 02:53:19 2016
From: moritz.fischer at ettus.com (Moritz Fischer)
Date: Wed, 27 Jan 2016 11:53:19 +0100
Subject: [PATCH v2 2/4] power: reset: add reboot mode driver
In-Reply-To: <1452598319-8324-1-git-send-email-andy.yan@rock-chips.com>
References: <1452598029-8222-1-git-send-email-andy.yan@rock-chips.com>
 <1452598319-8324-1-git-send-email-andy.yan@rock-chips.com>
Message-ID: <CAAtXAHd-m5GAUnvx9At9_UOoi1M4g25P0M-vwoC+Xc-PXtdwqw@mail.gmail.com>

Hi Andy,

On Tue, Jan 12, 2016 at 12:31 PM, Andy Yan <andy.yan at rock-chips.com> wrote:
> This driver parse the reboot commands like "reboot loader"
> and "reboot recovery" to get a boot mode described in the
> device tree , then call the write interfae to store the boot
> mode in some persistent storage  like special register or ram,
> which can be read by the bootloader after system reboot, then
> the bootloader can take different action according to the mode
> stored.
>
> This is commonly used on Android based devices, which in order
> to reboot the device into fastboot or recovery mode.
>
> Signed-off-by: Andy Yan <andy.yan at rock-chips.com>
>
> ---
>
> Changes in v2:
> - move to dir drivers/power/reset/
> - make syscon-reboot-mode a generic driver
>
> Changes in v1:
> - fix the embarrassed compile warning
> - correct the maskrom magic number
> - check for the normal reboot
>
>  drivers/power/reset/Kconfig              |  16 +++++
>  drivers/power/reset/Makefile             |   2 +
>  drivers/power/reset/reboot-mode.c        | 100 +++++++++++++++++++++++++++++++
>  drivers/power/reset/reboot-mode.h        |   6 ++
>  drivers/power/reset/syscon-reboot-mode.c |  62 +++++++++++++++++++
>  5 files changed, 186 insertions(+)
>  create mode 100644 drivers/power/reset/reboot-mode.c
>  create mode 100644 drivers/power/reset/reboot-mode.h
>  create mode 100644 drivers/power/reset/syscon-reboot-mode.c
>
> diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
> index 1131cf7..2e6d873 100644
> --- a/drivers/power/reset/Kconfig
> +++ b/drivers/power/reset/Kconfig
> @@ -173,5 +173,21 @@ config POWER_RESET_ZX
>         help
>           Reboot support for ZTE SoCs.
>
> +config REBOOT_MODE
> +       tristate
> +       depends on OF
> +       help
> +        This driver will help to pass the system reboot mode
> +        to bootloader
> +
> +config SYSCON_REBOOT_MODE
> +       bool "Generic SYSCON regmap reboot mode driver"
> +       select REBOOT_MODE
> +       help
> +        Say y here will enable reboot mode driver. This will
> +        get reboot mode arguments and store it in SYSCON mapped
> +        register, then the bootloader can read it to take different
> +        action according to the mode.
> +
>  endif
>
> diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile
> index 096fa67..a63865b 100644
> --- a/drivers/power/reset/Makefile
> +++ b/drivers/power/reset/Makefile
> @@ -20,3 +20,5 @@ obj-$(CONFIG_POWER_RESET_SYSCON) += syscon-reboot.o
>  obj-$(CONFIG_POWER_RESET_SYSCON_POWEROFF) += syscon-poweroff.o
>  obj-$(CONFIG_POWER_RESET_RMOBILE) += rmobile-reset.o
>  obj-$(CONFIG_POWER_RESET_ZX) += zx-reboot.o
> +obj-$(CONFIG_REBOOT_MODE) += reboot-mode.o
> +obj-$(CONFIG_SYSCON_REBOOT_MODE) += syscon-reboot-mode.o
> diff --git a/drivers/power/reset/reboot-mode.c b/drivers/power/reset/reboot-mode.c
> new file mode 100644
> index 0000000..92b7b4d
> --- /dev/null
> +++ b/drivers/power/reset/reboot-mode.c
> @@ -0,0 +1,100 @@
> +/*
> + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +#include <linux/device.h>
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/reboot.h>
> +#include "reboot-mode.h"
> +
> +struct mode_info {
> +       const char *mode;
> +       int magic;
> +       struct list_head list;
> +};
> +
> +struct reboot_mode_driver {
> +       struct list_head head;
> +       int (*write)(int magic);
> +       struct notifier_block reboot_notifier;
> +};
> +
> +static int get_reboot_mode_magic(struct reboot_mode_driver *reboot,
> +                                const char *cmd)
> +{
> +       const char *normal = "normal";
> +       int magic = 0;
> +       struct mode_info *info;
> +
> +       if (!cmd)
> +               cmd = normal;
> +
> +       list_for_each_entry(info, &reboot->head, list) {
> +               if (!strcmp(info->mode, cmd)) {
> +                       magic = info->magic;
> +                       break;
> +               }
> +       }
> +
> +       return magic;
> +}
> +
> +static int reboot_mode_notify(struct notifier_block *this,
> +                             unsigned long mode, void *cmd)
> +{
> +       struct reboot_mode_driver *reboot;
> +       int magic;
> +
> +       reboot = container_of(this, struct reboot_mode_driver, reboot_notifier);
> +       magic = get_reboot_mode_magic(reboot, cmd);
> +       if (magic)
> +               reboot->write(magic);
> +
> +       return NOTIFY_DONE;
> +}
> +
> +int reboot_mode_register(struct device *dev, int (*write)(int))
> +{
> +       struct reboot_mode_driver *reboot;
> +       struct mode_info *info;
> +       struct device_node *child;
> +       int ret;
> +
> +       reboot = devm_kzalloc(dev, sizeof(*reboot), GFP_KERNEL);
> +       if (!reboot)
> +               return -ENOMEM;
> +
> +       reboot->write = write;
> +       INIT_LIST_HEAD(&reboot->head);
> +       for_each_child_of_node(dev->of_node, child) {
> +               info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
> +               if (!info)
> +                       return -ENOMEM;
> +               info->mode = of_get_property(child, "linux,mode", NULL);
> +               if (of_property_read_u32(child, "loader,magic", &info->magic)) {
> +                       dev_err(dev, "reboot mode %s without magic number\n",
> +                               info->mode);
> +                       devm_kfree(dev, info);
> +                       continue;
> +               }
> +               list_add_tail(&info->list, &reboot->head);
> +       }
> +       reboot->reboot_notifier.notifier_call = reboot_mode_notify;
> +       ret = register_reboot_notifier(&reboot->reboot_notifier);
> +       if (ret)
> +               dev_err(dev, "can't register reboot notifier\n");
> +
> +       return ret;
> +}
> +EXPORT_SYMBOL_GPL(reboot_mode_register);
> +
> +MODULE_AUTHOR("Andy Yan <andy.yan at rock-chips.com");
> +MODULE_DESCRIPTION("System reboot mode driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/power/reset/reboot-mode.h b/drivers/power/reset/reboot-mode.h
> new file mode 100644
> index 0000000..76646e7
> --- /dev/null
> +++ b/drivers/power/reset/reboot-mode.h
> @@ -0,0 +1,6 @@
> +#ifndef __REBOOT_MODE_H__
> +#define __REBOOT_MODE_H__
> +
> +extern int reboot_mode_register(struct device *dev, int (*write)(int));
> +
> +#endif
> diff --git a/drivers/power/reset/syscon-reboot-mode.c b/drivers/power/reset/syscon-reboot-mode.c
> new file mode 100644
> index 0000000..b9370f3
> --- /dev/null
> +++ b/drivers/power/reset/syscon-reboot-mode.c
> @@ -0,0 +1,62 @@
> +/*
> + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/platform_device.h>
> +#include <linux/reboot.h>
> +#include <linux/regmap.h>
> +#include <linux/mfd/syscon.h>
> +#include "reboot-mode.h"
> +
> +static struct regmap *map;
> +static u32 offset;
> +
> +static int syscon_reboot_mode_write(int magic)
> +{
> +       regmap_write(map, offset, magic);

See suggestion below. I think using regmap_update_bits() would be even
more useful for cases where you have other stuff in that register, too.

> +
> +       return 0;
> +}
> +
> +static int syscon_reboot_mode_probe(struct platform_device *pdev)
> +{
> +       int ret;
> +
> +       map = syscon_node_to_regmap(pdev->dev.parent->of_node);
> +       if (IS_ERR(map))
> +               return PTR_ERR(map);
> +       if (of_property_read_u32(pdev->dev.of_node, "offset", &offset))
> +               return -EINVAL;

I think it would be worthwhile to have a mask value here, that you can use
with regmap_update_bits()

+           if (of_property_read_u32(pdev->dev.of_node, "mask", &mask))
+                   return -EINVAL;


Cheers,

Moritz


From al.kochet at gmail.com  Wed Jan 27 04:24:29 2016
From: al.kochet at gmail.com (Alexander Kochetkov)
Date: Wed, 27 Jan 2016 15:24:29 +0300
Subject: [PATCH] clk: rockchip: Allow sclk_i2s0 and i2s0_frac to change their
 parents rate on rk3188
Message-ID: <1453897469-2134-1-git-send-email-al.kochet@gmail.com>

Allow sclk_i2s0 and i2s0_frac to change their parents rate as
that the upstream dividers are purely there to feed sclk_i2s0

Tested on radxarock-lite.

Signed-off-by: Alexander Kochetkov <al.kochet at gmail.com>
---
 drivers/clk/rockchip/clk-rk3188.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c
index fe728f8..50e3eee 100644
--- a/drivers/clk/rockchip/clk-rk3188.c
+++ b/drivers/clk/rockchip/clk-rk3188.c
@@ -691,10 +691,10 @@ static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = {
 	COMPOSITE_NOMUX(0, "i2s0_pre", "i2s_src", 0,
 			RK2928_CLKSEL_CON(3), 0, 7, DFLAGS,
 			RK2928_CLKGATE_CON(0), 9, GFLAGS),
-	COMPOSITE_FRAC(0, "i2s0_frac", "i2s0_pre", 0,
+	COMPOSITE_FRAC(0, "i2s0_frac", "i2s0_pre", CLK_SET_RATE_PARENT,
 			RK2928_CLKSEL_CON(7), 0,
 			RK2928_CLKGATE_CON(0), 10, GFLAGS),
-	MUX(SCLK_I2S0, "sclk_i2s0", mux_sclk_i2s0_p, 0,
+	MUX(SCLK_I2S0, "sclk_i2s0", mux_sclk_i2s0_p, CLK_SET_RATE_PARENT,
 			RK2928_CLKSEL_CON(3), 8, 2, MFLAGS),
 
 	GATE(0, "hclk_imem0", "hclk_cpu", 0, RK2928_CLKGATE_CON(4), 14, GFLAGS),
-- 
1.7.9.5



From broonie at kernel.org  Wed Jan 27 04:47:00 2016
From: broonie at kernel.org (Mark Brown)
Date: Wed, 27 Jan 2016 12:47:00 +0000
Subject: [PATCH v3 3/9] ASoC: rockchip: i2s: add support for grabbing
 output clock to codec
In-Reply-To: <2073307.S3XLZp3hP0@diego>
References: <1452865796-23527-1-git-send-email-wxt@rock-chips.com>
 <20160122171815.GD6588@sirena.org.uk>
 <56A57733.8020200@rock-chips.com> <2073307.S3XLZp3hP0@diego>
Message-ID: <20160127124700.GL6042@sirena.org.uk>

On Tue, Jan 26, 2016 at 10:52:25AM +0100, Heiko St?bner wrote:
> Am Montag, 25. Januar 2016, 09:15:31 schrieb Jianqun Xu:
> > ? 23/01/2016 01:18, Mark Brown ??:

> > > controller driver that ensures that the clock is enabled.  The driver
> > > that manages the clock should be the one that uses it, like I say this
> > > means you should add the code to enable the clock to the CODEC driver if
> > > the CODEC driver needs the clock enabled.

> > But the property responsible to the factor, not care if the mclk source
> > clock is enabled or not. So does the simple-card driver can add support
> > to enable/disable mclk ?

> The mclk-input is part of the codec I'd think. So you'd want the clocks-
> property in the i2c entry describing the codec itself and implement the clk 
> operations in the codec driver as well.

> See codec-drivers for da7213, da7218,max98090 and many more for reference.

Yes, as I have said repeatedly including in the mail quoted above the
CODEC is using the clock so should be managing it.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160127/aa1b82ef/attachment.sig>

From linus.walleij at linaro.org  Wed Jan 27 06:02:18 2016
From: linus.walleij at linaro.org (Linus Walleij)
Date: Wed, 27 Jan 2016 15:02:18 +0100
Subject: [PATCH v2] pinctrl: rockchip: add support for the rk3399
In-Reply-To: <1452152498-21780-1-git-send-email-wdc@rock-chips.com>
References: <1451997055-47655-1-git-send-email-wdc@rock-chips.com>
 <1452152498-21780-1-git-send-email-wdc@rock-chips.com>
Message-ID: <CACRpkdb6Cooo-_eBKJn-SdFkGPg2hR9nud7Tq6aa+bmkDuOEmg@mail.gmail.com>

On Thu, Jan 7, 2016 at 8:41 AM, David Wu <wdc at rock-chips.com> wrote:

> From: David Wu <david.wu at rock-chips.com>
>
> The pinctrl of rk3399 is much different from other's,
> especially the 3bits of drive strength.
>
> Signed-off-by: David Wu <david.wu at rock-chips.com>
> ---
> change from v1:
> - need spin_unlock_irqrestore for set drive default case

Heiko can you please look at this patch?

> +       { .compatible = "rockchip,rk3399-pinctrl",
> +               .data = (void *)&rk3399_pin_ctrl },

I guess this requires a small patch to some binding file.
It's OK to fold that into this patch.

Yours,
Linus Walleij


From jung.zhao at rock-chips.com  Wed Jan 27 06:06:59 2016
From: jung.zhao at rock-chips.com (Jung Zhao)
Date: Wed, 27 Jan 2016 22:06:59 +0800
Subject: =?UTF-8?Q?=E7=AD=94=E5=A4=8D:_=5BPATCH_v1_1/3=5D_media:_v4l:_A?=
 =?UTF-8?Q?dd_VP8_format_support_in_V4L2_fr?= =?UTF-8?Q?amework?=
In-Reply-To: <CAFqH_5233Ge2eNk1Fqr1sy8Npp7mixL3fPWAdpBJ4NcZfFhjSQ@mail.gmail.com>
References: <1453799046-307-1-git-send-email-jung.zhao@rock-chips.com>	<1453799046-307-2-git-send-email-jung.zhao@rock-chips.com>	<1453852413.5584.15.camel@collabora.com>
 <CAFqH_5233Ge2eNk1Fqr1sy8Npp7mixL3fPWAdpBJ4NcZfFhjSQ@mail.gmail.com>
Message-ID: <019701d1590b$fe568390$fb038ab0$@rock-chips.com>

Yes, These patches are based on Pawel Osciak and Tomasz Figa's patches and others patches I mentioned above. I have expained the situation in [PATCH v1 0/3]. I will fix all the incorrect message in next version.

Thank you for your suggestion again.

-----????-----
???: Enric Balletbo Serra [mailto:eballetbo at gmail.com] 
????: 2016?1?27? 18:22
???: Nicolas Dufresne
??: Jung Zhao; pawel at osciak.com; Marek Szyprowski; Kyungmin Park; Mauro Carvalho Chehab; Heiko St?bner; linux-rockchip at lists.infradead.org; Chen Herman; Alpha Lin; Antti Palosaari; linux-api at vger.kernel.org; Benoit Parrot; Philipp Zabel; linux-kernel at vger.kernel.org; Sakari Ailus; Ricardo Ribalda Delgado; Hans Verkuil; linux-media at vger.kernel.org
??: Re: [PATCH v1 1/3] media: v4l: Add VP8 format support in V4L2 framework

Hi Jung,

Also, if I'm not wrong these changes are a mix of these three patches available in the chromium tree[1], it's a good practice cherry-pick the patches (removing the CHROMIUM tags, rebase against mainline...) cc the authors and maintain the author and the signed-off chain. Note also that the patches have a good explanation so better use the original ones.

Best regards,
Enric

[1]

Author: Tomasz Figa <tfiga at chromium.org>
Date:   Mon Apr 13 14:34:06 2015 +0900

    CHROMIUM: [media] videobuf2-dc: Let drivers specify DMA attrs

    DMA allocations might be subject to certain reqiurements specific to the
    hardware using the buffers, such as availability of kernel mapping (for
    contents fix-ups in the driver). The only entity that knows them is the
    driver, so it must share this knowledge with vb2-dc.

    This patch extends the alloc_ctx initialization interface to let the
    driver specify DMA attrs, which are then stored inside the allocation
    context and will be used for all allocations with that context.

    As a side effect, all dma_*_coherent() calls are turned into
    dma_*_attrs() calls, because the attributes need to be carried over
    through all DMA operations.

Author: Pawel Osciak <posciak at chromium.org>
Date:   Fri Dec 26 10:37:33 2014 +0900

    CHROMIUM: v4l: Add VP8 low-level decoder API controls.

    These controls are to be used with the new low-level decoder API for VP8
    to provide additional parameters for the hardware that cannot parse the
    input stream.

Author: Pawel Osciak <posciak at chromium.org>
Date:   Tue Nov 11 20:44:28 2014 +0900

    CHROMIUM: [media] v4l: Add private compound control type.

    V4L2_CTRL_TYPE_PRIVATE is to be used for private driver compound
    controls that use the "ptr" member of struct v4l2_ext_control.


2016-01-27 0:53 GMT+01:00 Nicolas Dufresne <nicolas.dufresne at collabora.com>:
> Hi Jung,
>
> this patch adds new public API to videodev2.h. It would be appropriate 
> to also add the associated documentation for it (see Documentation/DocBook/media). I also believe there is multiple way VP8 support could have been added. A proper commit message that explains the approach would also be appropriate.
>
> cheers,
> Nicolas
>
> Le mardi 26 janvier 2016 ? 17:04 +0800, Jung Zhao a ?crit :
>> From: zhaojun <jung.zhao at rock-chips.com>
>>
>> Signed-off-by: zhaojun <jung.zhao at rock-chips.com>
>> ---
>>
>>  drivers/media/v4l2-core/v4l2-ctrls.c           | 17 ++++-
>>  drivers/media/v4l2-core/v4l2-ioctl.c           |  3 +
>>  drivers/media/v4l2-core/videobuf2-dma-contig.c | 51 +++++++++-----
>>  include/media/v4l2-ctrls.h                     |  2 +
>>  include/media/videobuf2-dma-contig.h           | 11 ++-
>>  include/uapi/linux/v4l2-controls.h             | 98
>> ++++++++++++++++++++++++++
>>  include/uapi/linux/videodev2.h                 |  5 ++
>>  7 files changed, 166 insertions(+), 21 deletions(-)
>>
>> diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c
>> b/drivers/media/v4l2-core/v4l2-ctrls.c
>> index 890520d..22821e94 100644
>> --- a/drivers/media/v4l2-core/v4l2-ctrls.c
>> +++ b/drivers/media/v4l2-core/v4l2-ctrls.c
>> @@ -761,7 +761,7 @@ const char *v4l2_ctrl_get_name(u32 id)
>>       case V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP:                retu
>> rn "VPX I-Frame QP Value";
>>       case V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP:                retu
>> rn "VPX P-Frame QP Value";
>>       case V4L2_CID_MPEG_VIDEO_VPX_PROFILE:
>> return "VPX Profile";
>> -
>> +     case V4L2_CID_MPEG_VIDEO_VP8_FRAME_HDR:
>> return "VP8 Frame Header";
>>       /* CAMERA controls */
>>       /* Keep the order of the 'case's the same as in v4l2- 
>> controls.h! */
>>       case V4L2_CID_CAMERA_CLASS:             return "Camera
>> Controls";
>> @@ -1126,6 +1126,9 @@ void v4l2_ctrl_fill(u32 id, const char **name, 
>> enum v4l2_ctrl_type *type,
>>       case V4L2_CID_RDS_TX_ALT_FREQS:
>>               *type = V4L2_CTRL_TYPE_U32;
>>               break;
>> +     case V4L2_CID_MPEG_VIDEO_VP8_FRAME_HDR:
>> +             *type = V4L2_CTRL_TYPE_VP8_FRAME_HDR;
>> +             break;
>>       default:
>>               *type = V4L2_CTRL_TYPE_INTEGER;
>>               break;
>> @@ -1525,6 +1528,13 @@ static int std_validate(const struct v4l2_ctrl 
>> *ctrl, u32 idx,
>>                       return -ERANGE;
>>               return 0;
>>
>> +     /* FIXME:just return 0 for now */
>> +     case V4L2_CTRL_TYPE_PRIVATE:
>> +             return 0;
>> +
>> +     case V4L2_CTRL_TYPE_VP8_FRAME_HDR:
>> +             return 0;
>> +
>>       default:
>>               return -EINVAL;
>>       }
>> @@ -2074,6 +2084,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct 
>> v4l2_ctrl_handler *hdl,
>>       case V4L2_CTRL_TYPE_U32:
>>               elem_size = sizeof(u32);
>>               break;
>> +     case V4L2_CTRL_TYPE_VP8_FRAME_HDR:
>> +             elem_size = sizeof(struct v4l2_ctrl_vp8_frame_hdr);
>> +             break;
>>       default:
>>               if (type < V4L2_CTRL_COMPOUND_TYPES)
>>                       elem_size = sizeof(s32); @@ -2098,7 +2111,7 @@ 
>> static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>>               handler_set_err(hdl, -ERANGE);
>>               return NULL;
>>       }
>> -     if (is_array &&
>> +     if ((is_array || (flags & V4L2_CTRL_FLAG_REQ_KEEP)) &&
>>           (type == V4L2_CTRL_TYPE_BUTTON ||
>>            type == V4L2_CTRL_TYPE_CTRL_CLASS)) {
>>               handler_set_err(hdl, -EINVAL); diff --git 
>> a/drivers/media/v4l2-core/v4l2-ioctl.c
>> b/drivers/media/v4l2-core/v4l2-ioctl.c
>> index 7d028d1..8aa5812 100644
>> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
>> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
>> @@ -1259,6 +1259,9 @@ static void v4l_fill_fmtdesc(struct 
>> v4l2_fmtdesc *fmt)
>>               case V4L2_PIX_FMT_VC1_ANNEX_G:  descr = "VC-1 (SMPTE 
>> 412M Annex G)"; break;
>>               case V4L2_PIX_FMT_VC1_ANNEX_L:  descr = "VC-1 (SMPTE 
>> 412M Annex L)"; break;
>>               case V4L2_PIX_FMT_VP8:          descr = "VP8";
>> break;
>> +             case V4L2_PIX_FMT_VP8_FRAME:
>> +                     descr = "VP8 FRAME";
>> +                     break;
>>               case V4L2_PIX_FMT_CPIA1:        descr = "GSPCA CPiA
>> YUV"; break;
>>               case V4L2_PIX_FMT_WNVA:         descr =
>> "WNVA"; break;
>>               case V4L2_PIX_FMT_SN9C10X:      descr = "GSPCA
>> SN9C10X"; break;
>> diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c
>> b/drivers/media/v4l2-core/videobuf2-dma-contig.c
>> index c331272..aebcc7f 100644
>> --- a/drivers/media/v4l2-core/videobuf2-dma-contig.c
>> +++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c
>> @@ -23,13 +23,16 @@
>>
>>  struct vb2_dc_conf {
>>       struct device           *dev;
>> +     struct dma_attrs        attrs;
>>  };
>>
>>  struct vb2_dc_buf {
>>       struct device                   *dev;
>>       void                            *vaddr;
>>       unsigned long                   size;
>> +     void                            *cookie;
>>       dma_addr_t                      dma_addr;
>> +     struct dma_attrs                attrs;
>>       enum dma_data_direction         dma_dir;
>>       struct sg_table                 *dma_sgt;
>>       struct frame_vector             *vec;
>> @@ -131,7 +134,8 @@ static void vb2_dc_put(void *buf_priv)
>>               sg_free_table(buf->sgt_base);
>>               kfree(buf->sgt_base);
>>       }
>> -     dma_free_coherent(buf->dev, buf->size, buf->vaddr, buf-
>> >dma_addr);
>> +     dma_free_attrs(buf->dev, buf->size, buf->cookie, buf-
>> >dma_addr,
>> +                     &buf->attrs);
>>       put_device(buf->dev);
>>       kfree(buf);
>>  }
>> @@ -143,18 +147,22 @@ static void *vb2_dc_alloc(void *alloc_ctx, 
>> unsigned long size,
>>       struct device *dev = conf->dev;
>>       struct vb2_dc_buf *buf;
>>
>> -     buf = kzalloc(sizeof *buf, GFP_KERNEL);
>> +     buf = kzalloc(sizeof(*buf), GFP_KERNEL);
>>       if (!buf)
>>               return ERR_PTR(-ENOMEM);
>>
>> -     buf->vaddr = dma_alloc_coherent(dev, size, &buf->dma_addr,
>> -                                             GFP_KERNEL |
>> gfp_flags);
>> -     if (!buf->vaddr) {
>> +     buf->attrs = conf->attrs;
>> +     buf->cookie = dma_alloc_attrs(dev, size, &buf->dma_addr,
>> +                                     GFP_KERNEL | gfp_flags,
>> &buf->attrs);
>> +     if (!buf->cookie) {
>>               dev_err(dev, "dma_alloc_coherent of size %ld failed\n", 
>> size);
>>               kfree(buf);
>>               return ERR_PTR(-ENOMEM);
>>       }
>>
>> +     if (!dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, &buf->attrs))
>> +             buf->vaddr = buf->cookie;
>> +
>>       /* Prevent the device from being released while the buffer is 
>> used */
>>       buf->dev = get_device(dev);
>>       buf->size = size;
>> @@ -185,8 +193,8 @@ static int vb2_dc_mmap(void *buf_priv, struct 
>> vm_area_struct *vma)
>>        */
>>       vma->vm_pgoff = 0;
>>
>> -     ret = dma_mmap_coherent(buf->dev, vma, buf->vaddr,
>> -             buf->dma_addr, buf->size);
>> +     ret = dma_mmap_attrs(buf->dev, vma, buf->cookie,
>> +             buf->dma_addr, buf->size, &buf->attrs);
>>
>>       if (ret) {
>>               pr_err("Remapping memory failed, error: %d\n", ret); @@ 
>> -329,7 +337,7 @@ static void *vb2_dc_dmabuf_ops_kmap(struct dma_buf 
>> *dbuf, unsigned long pgnum)  {
>>       struct vb2_dc_buf *buf = dbuf->priv;
>>
>> -     return buf->vaddr + pgnum * PAGE_SIZE;
>> +     return buf->vaddr ? buf->vaddr + pgnum * PAGE_SIZE : NULL;
>>  }
>>
>>  static void *vb2_dc_dmabuf_ops_vmap(struct dma_buf *dbuf) @@ -368,8 
>> +376,8 @@ static struct sg_table *vb2_dc_get_base_sgt(struct 
>> vb2_dc_buf *buf)
>>               return NULL;
>>       }
>>
>> -     ret = dma_get_sgtable(buf->dev, sgt, buf->vaddr, buf-
>> >dma_addr,
>> -             buf->size);
>> +     ret = dma_get_sgtable_attrs(buf->dev, sgt, buf->cookie, buf-
>> >dma_addr,
>> +             buf->size, &buf->attrs);
>>       if (ret < 0) {
>>               dev_err(buf->dev, "failed to get scatterlist from DMA 
>> API\n");
>>               kfree(sgt);
>> @@ -448,22 +456,26 @@ static void vb2_dc_put_userptr(void *buf_priv)
>>   */
>>
>>  #ifdef __arch_pfn_to_dma
>> -static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, 
>> unsigned long pfn)
>> +static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev,
>> +                                        unsigned long pfn)
>>  {
>>       return (dma_addr_t)__arch_pfn_to_dma(dev, pfn);  }  #elif 
>> defined(__pfn_to_bus) -static inline dma_addr_t 
>> vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn)
>> +static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev,
>> +                                        unsigned long pfn)
>>  {
>>       return (dma_addr_t)__pfn_to_bus(pfn);  }  #elif 
>> defined(__pfn_to_phys) -static inline dma_addr_t 
>> vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn)
>> +static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev,
>> +                                        unsigned long pfn)
>>  {
>>       return (dma_addr_t)__pfn_to_phys(pfn);  }  #else -static inline 
>> dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn)
>> +static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev,
>> +                                        unsigned long pfn)
>>  {
>>       /* really, we cannot do anything better at this point */
>>       return (dma_addr_t)(pfn) << PAGE_SHIFT; @@ -497,7 +509,7 @@ 
>> static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr,
>>               return ERR_PTR(-EINVAL);
>>       }
>>
>> -     buf = kzalloc(sizeof *buf, GFP_KERNEL);
>> +     buf = kzalloc(sizeof(*buf), GFP_KERNEL);
>>       if (!buf)
>>               return ERR_PTR(-ENOMEM);
>>
>> @@ -721,19 +733,22 @@ const struct vb2_mem_ops vb2_dma_contig_memops 
>> = {  };  EXPORT_SYMBOL_GPL(vb2_dma_contig_memops);
>>
>> -void *vb2_dma_contig_init_ctx(struct device *dev)
>> +void *vb2_dma_contig_init_ctx_attrs(struct device *dev,
>> +                                 struct dma_attrs *attrs)
>>  {
>>       struct vb2_dc_conf *conf;
>>
>> -     conf = kzalloc(sizeof *conf, GFP_KERNEL);
>> +     conf = kzalloc(sizeof(*conf), GFP_KERNEL);
>>       if (!conf)
>>               return ERR_PTR(-ENOMEM);
>>
>>       conf->dev = dev;
>> +     if (attrs)
>> +             conf->attrs = *attrs;
>>
>>       return conf;
>>  }
>> -EXPORT_SYMBOL_GPL(vb2_dma_contig_init_ctx);
>> +EXPORT_SYMBOL_GPL(vb2_dma_contig_init_ctx_attrs);
>>
>>  void vb2_dma_contig_cleanup_ctx(void *alloc_ctx)  { diff --git 
>> a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 
>> 5f9526f..0424cdc 100644
>> --- a/include/media/v4l2-ctrls.h
>> +++ b/include/media/v4l2-ctrls.h
>> @@ -46,6 +46,7 @@ struct poll_table_struct;
>>   * @p_u16:   Pointer to a 16-bit unsigned value.
>>   * @p_u32:   Pointer to a 32-bit unsigned value.
>>   * @p_char:  Pointer to a string.
>> + * @p_vp8_frame_hdr: Pointer to a struct
>> v4l2_ctrl_vp8_frame_hdr.
>>   * @p:               Pointer to a compound value.
>>   */
>>  union v4l2_ctrl_ptr {
>> @@ -55,6 +56,7 @@ union v4l2_ctrl_ptr {
>>       u16 *p_u16;
>>       u32 *p_u32;
>>       char *p_char;
>> +     struct v4l2_ctrl_vp8_frame_hdr *p_vp8_frame_hdr;
>>       void *p;
>>  };
>>
>> diff --git a/include/media/videobuf2-dma-contig.h
>> b/include/media/videobuf2-dma-contig.h
>> index c33dfa6..2087c9a 100644
>> --- a/include/media/videobuf2-dma-contig.h
>> +++ b/include/media/videobuf2-dma-contig.h
>> @@ -16,6 +16,8 @@
>>  #include <media/videobuf2-v4l2.h>
>>  #include <linux/dma-mapping.h>
>>
>> +struct dma_attrs;
>> +
>>  static inline dma_addr_t
>>  vb2_dma_contig_plane_dma_addr(struct vb2_buffer *vb, unsigned int
>> plane_no)
>>  {
>> @@ -24,7 +26,14 @@ vb2_dma_contig_plane_dma_addr(struct vb2_buffer 
>> *vb, unsigned int plane_no)
>>       return *addr;
>>  }
>>
>> -void *vb2_dma_contig_init_ctx(struct device *dev);
>> +void *vb2_dma_contig_init_ctx_attrs(struct device *dev,
>> +                                 struct dma_attrs *attrs);
>> +
>> +static inline void *vb2_dma_contig_init_ctx(struct device *dev) {
>> +     return vb2_dma_contig_init_ctx_attrs(dev, NULL); }
>> +
>>  void vb2_dma_contig_cleanup_ctx(void *alloc_ctx);
>>
>>  extern const struct vb2_mem_ops vb2_dma_contig_memops; diff --git 
>> a/include/uapi/linux/v4l2-controls.h
>> b/include/uapi/linux/v4l2-controls.h
>> index 2d225bc..63c65d9 100644
>> --- a/include/uapi/linux/v4l2-controls.h
>> +++ b/include/uapi/linux/v4l2-controls.h
>> @@ -520,6 +520,7 @@ enum
>> v4l2_mpeg_video_h264_hierarchical_coding_type {  };
>>  #define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER   (V
>> 4L2_CID_MPEG_BASE+381)
>>  #define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_QP
>> (V4L2_CID_MPEG_BASE+382)
>> +
>>  #define V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP (V4L2_CID_MPEG_B
>> ASE+400)
>>  #define V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP (V4L2_CID_MPEG_B
>> ASE+401)
>>  #define V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP (V4L2_CID_MPEG_B
>> ASE+402)
>> @@ -578,6 +579,8 @@ enum v4l2_vp8_golden_frame_sel {
>>  #define V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP           (V4L2_CID_
>> MPEG_BASE+510)
>>  #define V4L2_CID_MPEG_VIDEO_VPX_PROFILE                      (V4L2
>> _CID_MPEG_BASE+511)
>>
>> +#define V4L2_CID_MPEG_VIDEO_VP8_FRAME_HDR            (V4L2_CID_M
>> PEG_BASE+512)
>> +
>>  /*  MPEG-class control IDs specific to the CX2341x driver as defined 
>> by V4L2 */
>>  #define V4L2_CID_MPEG_CX2341X_BASE                           (
>> V4L2_CTRL_CLASS_MPEG | 0x1000)
>>  #define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE      (V4L
>> 2_CID_MPEG_CX2341X_BASE+0)
>> @@ -963,4 +966,99 @@ enum v4l2_detect_md_mode {
>>  #define V4L2_CID_DETECT_MD_THRESHOLD_GRID    (V4L2_CID_DETECT_CL
>> ASS_BASE + 3)
>>  #define V4L2_CID_DETECT_MD_REGION_GRID               (V4L2_CID_DETE
>> CT_CLASS_BASE + 4)
>>
>> +
>> +/* Complex controls */
>> +
>> +#define V4L2_VP8_SEGMNT_HDR_FLAG_ENABLED              0x01
>> +#define V4L2_VP8_SEGMNT_HDR_FLAG_UPDATE_MAP           0x02
>> +#define V4L2_VP8_SEGMNT_HDR_FLAG_UPDATE_FEATURE_DATA  0x04 struct 
>> +v4l2_vp8_sgmnt_hdr {
>> +     __u8 segment_feature_mode;
>> +
>> +     __s8 quant_update[4];
>> +     __s8 lf_update[4];
>> +     __u8 segment_probs[3];
>> +
>> +     __u8 flags;
>> +};
>> +
>> +#define V4L2_VP8_LF_HDR_ADJ_ENABLE   0x01
>> +#define V4L2_VP8_LF_HDR_DELTA_UPDATE 0x02 struct 
>> +v4l2_vp8_loopfilter_hdr {
>> +     __u8 type;
>> +     __u8 level;
>> +     __u8 sharpness_level;
>> +     __s8 ref_frm_delta_magnitude[4];
>> +     __s8 mb_mode_delta_magnitude[4];
>> +
>> +     __u8 flags;
>> +};
>> +
>> +struct v4l2_vp8_quantization_hdr {
>> +     __u8 y_ac_qi;
>> +     __s8 y_dc_delta;
>> +     __s8 y2_dc_delta;
>> +     __s8 y2_ac_delta;
>> +     __s8 uv_dc_delta;
>> +     __s8 uv_ac_delta;
>> +     __u16 dequant_factors[4][3][2]; };
>> +
>> +struct v4l2_vp8_entropy_hdr {
>> +     __u8 coeff_probs[4][8][3][11];
>> +     __u8 y_mode_probs[4];
>> +     __u8 uv_mode_probs[3];
>> +     __u8 mv_probs[2][19];
>> +};
>> +
>> +#define V4L2_VP8_FRAME_HDR_FLAG_EXPERIMENTAL         0x01
>> +#define V4L2_VP8_FRAME_HDR_FLAG_SHOW_FRAME           0x02
>> +#define V4L2_VP8_FRAME_HDR_FLAG_MB_NO_SKIP_COEFF     0x04
>> +struct v4l2_ctrl_vp8_frame_hdr {
>> +     /* 0: keyframe, 1: not a keyframe */
>> +     __u8 key_frame;
>> +     __u8 version;
>> +
>> +     /* Populated also if not a key frame */
>> +     __u16 width;
>> +     __u8 horizontal_scale;
>> +     __u16 height;
>> +     __u8 vertical_scale;
>> +
>> +     struct v4l2_vp8_sgmnt_hdr sgmnt_hdr;
>> +     struct v4l2_vp8_loopfilter_hdr lf_hdr;
>> +     struct v4l2_vp8_quantization_hdr quant_hdr;
>> +     struct v4l2_vp8_entropy_hdr entropy_hdr;
>> +
>> +     __u8 sign_bias_golden;
>> +     __u8 sign_bias_alternate;
>> +
>> +     __u8 prob_skip_false;
>> +     __u8 prob_intra;
>> +     __u8 prob_last;
>> +     __u8 prob_gf;
>> +
>> +     __u32 first_part_size;
>> +     __u32 first_part_offset;
>> +     /*
>> +      * Offset in bits of MB data in first partition,
>> +      * i.e. bit offset starting from first_part_offset.
>> +      */
>> +     __u32 macroblock_bit_offset;
>> +
>> +     __u8 num_dct_parts;
>> +     __u32 dct_part_sizes[8];
>> +
>> +     __u8 bool_dec_range;
>> +     __u8 bool_dec_value;
>> +     __u8 bool_dec_count;
>> +
>> +     /* v4l2_buffer indices of reference frames */
>> +     __u32 last_frame;
>> +     __u32 golden_frame;
>> +     __u32 alt_frame;
>> +
>> +     __u8 flags;
>> +};
>> +
>>  #endif
>> diff --git a/include/uapi/linux/videodev2.h 
>> b/include/uapi/linux/videodev2.h index 29a6b78..191ca19 100644
>> --- a/include/uapi/linux/videodev2.h
>> +++ b/include/uapi/linux/videodev2.h
>> @@ -593,6 +593,7 @@ struct v4l2_pix_format {  #define 
>> V4L2_PIX_FMT_VC1_ANNEX_G v4l2_fourcc('V', 'C', '1', 'G') /* SMPTE 
>> 421M Annex G compliant stream */  #define V4L2_PIX_FMT_VC1_ANNEX_L 
>> v4l2_fourcc('V', 'C', '1', 'L') /* SMPTE 421M Annex L compliant 
>> stream */
>>  #define V4L2_PIX_FMT_VP8      v4l2_fourcc('V', 'P', '8', '0') /* VP8
>> */
>> +#define V4L2_PIX_FMT_VP8_FRAME       v4l2_fourcc('V', 'P', '8',
>> 'F') /* VP8 parsed frames */
>>
>>  /*  Vendor-specific formats   */
>>  #define V4L2_PIX_FMT_CPIA1    v4l2_fourcc('C', 'P', 'I', 'A') /*
>> cpia1 YUV */
>> @@ -1473,6 +1474,7 @@ struct v4l2_ext_control {
>>               __u8 __user *p_u8;
>>               __u16 __user *p_u16;
>>               __u32 __user *p_u32;
>> +             struct v4l2_ctrl_vp8_frame_hdr __user
>> *p_vp8_frame_hdr;
>>               void __user *ptr;
>>       };
>>  } __attribute__ ((packed));
>> @@ -1517,6 +1519,9 @@ enum v4l2_ctrl_type {
>>       V4L2_CTRL_TYPE_U8            = 0x0100,
>>       V4L2_CTRL_TYPE_U16           = 0x0101,
>>       V4L2_CTRL_TYPE_U32           = 0x0102,
>> +     V4L2_CTRL_TYPE_VP8_FRAME_HDR    = 0x108,
>> +
>> +     V4L2_CTRL_TYPE_PRIVATE       = 0xffff,
>>  };
>>
>>  /*  Used in the VIDIOC_QUERYCTRL ioctl for querying controls */







From heiko at sntech.de  Wed Jan 27 06:10:19 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Wed, 27 Jan 2016 15:10:19 +0100
Subject: [PATCH v2] pinctrl: rockchip: add support for the rk3399
In-Reply-To: <CACRpkdb6Cooo-_eBKJn-SdFkGPg2hR9nud7Tq6aa+bmkDuOEmg@mail.gmail.com>
References: <1451997055-47655-1-git-send-email-wdc@rock-chips.com>
 <1452152498-21780-1-git-send-email-wdc@rock-chips.com>
 <CACRpkdb6Cooo-_eBKJn-SdFkGPg2hR9nud7Tq6aa+bmkDuOEmg@mail.gmail.com>
Message-ID: <1664345.86tvmnNB3Q@diego>

Am Mittwoch, 27. Januar 2016, 15:02:18 schrieb Linus Walleij:
> On Thu, Jan 7, 2016 at 8:41 AM, David Wu <wdc at rock-chips.com> wrote:
> > From: David Wu <david.wu at rock-chips.com>
> > 
> > The pinctrl of rk3399 is much different from other's,
> > especially the 3bits of drive strength.
> > 
> > Signed-off-by: David Wu <david.wu at rock-chips.com>
> > ---
> > change from v1:
> > - need spin_unlock_irqrestore for set drive default case
> 
> Heiko can you please look at this patch?

sorry, about that falling so far down my inbox. I'll try to look at it today 
or tomorrow.

But overall it looks good.

@David: if you wake up before I manage to review more in-depth, the
"reading register twice" (+writing counterpart)
+                       /* need to read regs twice */
+                       ret = regmap_read(regmap, reg, &temp);
+                       if (ret)
+                               return ret;

should definitly get more explanation on how that access scheme works. Seems to 
be some special register access method with the register returning different 
values each time? As I don't have a manual for the soc yet this is definitly 
curious for me :-)

> 
> > +       { .compatible = "rockchip,rk3399-pinctrl",
> > +               .data = (void *)&rk3399_pin_ctrl },
> 
> I guess this requires a small patch to some binding file.
> It's OK to fold that into this patch.

@David:
Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt



From dianders at chromium.org  Wed Jan 27 08:46:13 2016
From: dianders at chromium.org (Doug Anderson)
Date: Wed, 27 Jan 2016 08:46:13 -0800
Subject: [PATCH 0/5] clk: rockchip: add full support for HDMI clock on
 rk3288
In-Reply-To: <CAAObsKDsJgUA9dL+1Fdc616ofLdMqaUOwHhDkBWz7MV5_LJu9Q@mail.gmail.com>
References: <1415087559-19444-1-git-send-email-kever.yang@rock-chips.com>
 <48806799.thBSifdUp8@diego> <54647142.4080800@rock-chips.com>
 <CAD=FV=Vzec3Qm-kc6zZ4VxSREoZbzVkWbzp-noHYdcEVrLYYSw@mail.gmail.com>
 <CAAObsKChYN5nmcm5OWnAzNRGDi_QGSoKvN+0FizhjeT-3mmeXQ@mail.gmail.com>
 <CAD=FV=Wgu=xQ9PcVqfzcedXVU3Rh-HKbBVxHuqgV9OZJEYhYvg@mail.gmail.com>
 <CAAObsKC1v--mZwkiG-c7P2Bwi2-_txbeqaW4Vd5h-noAVNbhjA@mail.gmail.com>
 <CAD=FV=VB93wYigRJ8wa8YL6P+Q+-fRokPiCB-Fo1pC_zAdh2oA@mail.gmail.com>
 <CAAObsKCMh3Bk_oNJdLbU2F9b7jrnFZOp2uFR_oPqUoJ3W1gfaQ@mail.gmail.com>
 <CAD=FV=W=LCJk4m049hBnm7BOYudnVe=oa3jdcSrrZZUU9rFtog@mail.gmail.com>
 <CAAObsKBS45kbvdmdvFxwSo=B9GXRLoX2g7WQYVmZJ0KFnwSejw@mail.gmail.com>
 <CAD=FV=Une-DJ+0zV6Hmx5EmKXCEApb2Zx1FuJaUQf7MT9OPsug@mail.gmail.com>
 <CAAObsKDsJgUA9dL+1Fdc616ofLdMqaUOwHhDkBWz7MV5_LJu9Q@mail.gmail.com>
Message-ID: <CAD=FV=WExXs66t9ybEUiGrLXr_Bfczfo132BJ_8o5_RXZzVQ3A@mail.gmail.com>

Tomeu,

On Wed, Jan 27, 2016 at 2:20 AM, Tomeu Vizoso <tomeu at tomeuvizoso.net> wrote:
> On 26 January 2016 at 17:32, Doug Anderson <dianders at chromium.org> wrote:
>> Tomeu,
>>
>> On Tue, Jan 26, 2016 at 12:28 AM, Tomeu Vizoso <tomeu at tomeuvizoso.net> wrote:
>>> On 22 January 2016 at 18:07, Doug Anderson <dianders at chromium.org> wrote:
>>>> Tomeu,
>>>>
>>>> On Fri, Jan 22, 2016 at 6:00 AM, Tomeu Vizoso <tomeu at tomeuvizoso.net> wrote:
>>>>> On 21 January 2016 at 21:11, Doug Anderson <dianders at chromium.org> wrote:
>>>>>> Hi,
>>>>>>
>>>>>> On Thu, Jan 21, 2016 at 1:03 AM, Tomeu Vizoso <tomeu at tomeuvizoso.net> wrote:
>>>>>>> So we have a mechanism for detecting a conflict in the clock
>>>>>>> hierarchy, and a mechanism to solve it, but we are missing a way for
>>>>>>> userspace to communicate policy regarding which clocks should be given
>>>>>>> priority when solving such a conflict?
>>>>>>
>>>>>> Hrmmm, I guess it could be userspace that makes the decision.  It does
>>>>>> seem a little odd to force it to userspace in all cases, though.  For
>>>>>> a particular laptop that is designed with a specific panel connected
>>>>>> up eDP it seems less than ideal to push this into userspace.  If the
>>>>>> kernel could just work in the expected sane way (or at least work that
>>>>>> way by default) it would be ideal.
>>>>>
>>>>> Ah, I was wrongly assuming that the kernel didn't have enough
>>>>> information to make an informed decision in this case, sorry.
>>>>>
>>>>> Guess the per-user rate limits don't help here because the consumer
>>>>> with higher priority could work with frequencies other than the ideal.
>>>>>
>>>>> And we cannot have a consumer listening for PRE_RATE_CHANGE and
>>>>> aborting unwanted changes or rerouting the ancestors of the clocks of
>>>>> other consumers because that would be a massive violation of
>>>>> separation of concerns.
>>>>>
>>>>> If we were to rearrange the clock topology from within the CCF, then
>>>>> consumers need to have a way to communicate to the core that they are
>>>>> more important than other consumers. clk_set_important(clk, true)
>>>>> could be enough in this case, but would be insufficient in more
>>>>> complex cases where more than two clocks could use the same PLL.
>>>>
>>>> With something like the above I'd still expect some complexity
>>>> depending on the probe order.  If a less important device grabs the
>>>> clock first, it might be forced to re-think its clocks later.  That
>>>> might be disconcerting.
>>>
>>> How much disconcerting do you think this could be? Hopefully those
>>> devices should probe quite close to each other, right?
>>
>> Probe: probably, though with defers it could be several seconds.
>>
>> ...but remember that display interfaces tend to be hotplug.  That
>> might mean that the HDMI interface won't try to set the clock until
>> much, much later.
>
> I'm still having trouble grasping what's the impact to users. Is it
> that if HDMI gets the contended clock first and the internal panel
> device only probes after a second or so, then the user may notice a
> change in frequency in the HDMI screen when the panel lights up?
>
> Though in that particular case I'm not sure if the impact is that big
> for the user, wonder if such rearranging of the clock hierarchy can
> cause bigger problems in other situations.
>
> The on-demand probing series could help here because a downstream that
> cared about these issues could just rearrange the contents of the DT
> (maybe with a script as part of the build process) so that the panel
> is always probed first, but well, that one collided with an iceberg.

Problem I'm imagining is this one:

1. EDP (used for builtin panel) is setup by userspace.  It requests a
pixel clock that's not quite achievable from CPLL / GPLL.  Let's say
it requests 67 MHz.  GPLL can make 66 MHz (594 / 9) and CPLL can make
66.67 (400 / 6).  ...but NPLL is sitting right there, so let's make
things exact and set NPLL to 1.608 GHz (67 MHz * 24).  Now we get
exactly 67 MHz.

2. An hour after bootup, the user plugs in HDMI.  The user wants to
use a 78.8 MHz pixel clock.

Option #1: deny HDMI because CPLL / GPLL are fixed and NPLL is in use
by eDP.  Not great for HDMI support.

Option #2: switch EDP away from NPLL to CPLL.  This presumably will
require disabling / re-enabling eDP and isn't super amazing, but could
be done if need be.  Since user space might care about the eDP refresh
rate (so it can properly schedule video frames) and this will tweak
the refresh rate by a bit, this might be cleanest as a full "unplug"
of eDP and re-plug of eDP.

Option #3: Somehow ahead of time know that on this particular device
that eDP shouldn't use NPLL.

--

The whole situation would presumably be different if eDP wasn't used
for the builtin panel.  Technically eDP can be hotplugged (I think).
If we're on a board where eDP could be hotplugged to different
monitors then all the rules might be different.


>>>> OK, so I was just involved in a change recently that made me realize
>>>> that maybe our original problems were tied to the fact that our
>>>> builtin panels were trying to specify a clock that was impossible to
>>>> achieve with CPLL / GPLL.  It was a known problem that the request
>>>> would be denied and the CCF would just pick the closest rate it could.
>>>> Probably the right thing is to solve _that_ problem first.  If using
>>>> simple panel you could do a change like
>>>> <https://chromium-review.googlesource.com/#/c/323211/> (though
>>>> presumably you'd have to handle people using the same panel in other
>>>> laptops).  You might also be able to do funny things to fixup the mode
>>>> like dbehr tried to do in
>>>> <https://chromium-review.googlesource.com/#/c/270017/>.  By doing this
>>>> and making sure that
>>>
>>> Are we missing something here?
>>
>> Eh?
>
> The sentence above seemed to have been cut in the middle and I was
> wondering if there's something relevant I'm missing because of it.

On the current systems eDP will actually request 72.5 MHz, which is
definitely not achievable from CPLL / GPLL.  You can try redoing the
exercise above where eDP requests 72.5 MHz if you want, but suffice to
say that the eDP refresh rate will change more drastically if it goes
from 72.5 MHz to 66.67 MHz.


From dianders at chromium.org  Wed Jan 27 11:44:26 2016
From: dianders at chromium.org (Doug Anderson)
Date: Wed, 27 Jan 2016 11:44:26 -0800
Subject: [PATCH v5 03/21] usb: dwc2: host: Set host_rx_fifo_size to 528
 for rk3066
In-Reply-To: <56A89838.3040202@rock-chips.com>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
 <1453486736-15358-4-git-send-email-dianders@chromium.org>
 <56A89838.3040202@rock-chips.com>
Message-ID: <CAD=FV=XKAzVOLCkFBH1nXeiaKeU4FoFN2CuRNMankA4xsgPsuw@mail.gmail.com>

Kever,

On Wed, Jan 27, 2016 at 2:13 AM, Kever Yang <kever.yang at rock-chips.com> wrote:
> Hi Doug,
>
> On 01/23/2016 02:18 AM, Douglas Anderson wrote:
>>
>> As documented in dwc2_calculate_dynamic_fifo(), host_rx_fifo_size should
>> really be:
>>   2 * ((Largest Packet size / 4) + 1 + 1) + n
>>   with n = number of host channel.
>>
>> We have 9 host channels, so
>>   2 * ((1024/4) + 2) + 9 = 516 + 9 = 525
>>
>> We've got 960 / 972 total_fifo_size on rk3288 (and presumably on
>> rk3066) and 525 + 128 + 256 = 909 so we're still under on both ports
>> even when we increment by 5.
>>
>> Since we have space, Kever Yang suggests bumping by 8.  He says this
>> will meet INCR16 access and next fifo type can start with a aligned
>> address.
>
> I have double check this feature, the INCR16 is actually a burst 16
> on to the bus, the aligned problem only happen on the DRAM
> access, and it's OK for internal RAM access via internal DMA.
>
> Bump to 8 looks much better when we check the FIFO setting if we
> have enough memory, right? :)
>
> rk3066/rk3188/rk3288 have the same hw config for dwc2 controller,
> so this patch should works on those files.
>
> Reviewed-by: Kever Yang <kever.yang at rock-chips.com>

Thanks for double-checking and thanks for the review!

If it's all the same to you, I'll probably change it back to 525 and
then increase the periodic FIFO size by 3 DWORDS in the next patch.
12 bytes may not be much, but might as well make use of them to
improve performance / compatibility?

Presumably you're also OK with the next patch in the series: ("usb:
dwc2: host: Set host_perio_tx_fifo_size to 304 for rk3066")?


Note to anyone who may be thinking of applying patches in this series:

* You can skip both this and the next patch if you wish and any other
patches in the series are unaffected.

* You can request that I spin just these two patches and I will do it
ASAP.  Otherwise I'll add that fix to the next spin of the whole
series.

* You can make the changes yourself to this patch and the next one
(this patch should be 525 and the next should increase
host_perio_tx_fifo_size to 307).

I'm OK with any of those 3 things.  ;)


-Doug


From dianders at chromium.org  Wed Jan 27 12:49:15 2016
From: dianders at chromium.org (Doug Anderson)
Date: Wed, 27 Jan 2016 12:49:15 -0800
Subject: [PATCH v5 16/21] usb: dwc2: host: Manage frame nums better in
 scheduler
In-Reply-To: <1453486736-15358-17-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
 <1453486736-15358-17-git-send-email-dianders@chromium.org>
Message-ID: <CAD=FV=VkWRAohh5fSPwanHgKjr5MTYH4s82wtg-Ob9OyAKYWEw@mail.gmail.com>

Hi,

On Fri, Jan 22, 2016 at 10:18 AM, Douglas Anderson
<dianders at chromium.org> wrote:
> The dwc2 scheduler (contained in hcd_queue.c) was a bit confusing in the
> way it initted / kept track of which frames a QH was going to be active
> in.  Let's clean things up a little bit in preparation for a rewrite of
> the microframe scheduler.
>
> Specifically:
> * Old code would pick a frame number in dwc2_qh_init() and would try to
>   pick it "in a slightly future (micro)frame".  As far as I can tell the
>   reason for this was that there was a delay between dwc2_qh_init() and
>   when we actually wanted to dwc2_hcd_qh_add().  ...but apparently this
>   attempt to be slightly in the future wasn't enough because
>   dwc2_hcd_qh_add() then had code to reset things if the frame _wasn't_
>   in the future.  There's no reason not to just pick the frame later.
>   For non-periodic QH we now pick the frame in dwc2_hcd_qh_add().  For
>   periodic QH we pick the frame at dwc2_schedule_periodic() time.
> * The old "dwc2_qh_init() actually assigned to "hsotg->frame_number".
>   This doesn't seem like a great idea since that variable is supposed to
>   be used to keep track of which SOF the interrupt handler has seen.
>   Let's be clean: anyone who wants the current frame number (instead of
>   the one as of the last interrupt) should ask for it.
> * The old code wasn't terribly consistent about trying to use the frame
>   that the microframe scheduler assigned to it.  In
>   dwc2_sched_periodic_split() when it was scheduling the first frame it
>   always "ORed" in 0x7 (!).  Since the frame goes on the wire 1 uFrame
>   after next_active_frame it meant that the SSPLIT would always try for
>   uFrame 0 and the transaction would happen on the low speed bus during
>   uFrame 1.  This is irregardless of what the microframe scheduler
>   said.
> * The old code assumed it would get called to schedule the next in a
>   periodic split very quickly.  That is if next_active_frame was
>   0 (transfer on wire in uFrame 1) it assumed it was getting called to
>   schedule the next uFrame during uFrame 1 too (so it could queue
>   something up for uFrame 2).  It should be possible to actually queue
>   something up for uFrame 2 while in uFrame 2 (AKA queue up ASAP).  To
>   do this, code needs to look at the previously scheduled frame when
>   deciding when to next be active, not look at the current frame number.
> * If there was no microframe scheduler, the old code would check for
>   whether we should be active using "qh->next_active_frame ==
>   frame_number".  This seemed like a race waiting to happen.  ...plus
>   there's no way that you wouldn't want to schedule if next_active_frame
>   was actually less than frame number.
>
> Note that this change doesn't make 100% sense on its own since it's
> expecting some sanity in the frame numbers assigned by the microframe
> scheduler and (as per the future patch which rewries it) I think that
> the current microframe scheduler is quite insane.  However, it seems
> like splitting this up from the microframe scheduler patch makes things
> into smaller chunks and hopefully adds to clarity rather than reduces
> it.  The two patches could certainly be squashed.  Not that in the very
> least, I don't see any obvious bad behavior introduced with just this
> patch.
>
> I've attempted to keep the config parameter to disable the microframe
> scheduler in tact in this change, though I'm not sure it's worth it.
> Obviously the code is touched a lot so it's possible I regressed
> something when the microframe scheduler is disabled, though I did some
> basic testing and it seemed to work OK.  I'm still not 100% sure why you
> wouldn't want the microframe scheduler (presuming it works), so maybe a
> future patch (or a future version of this patch?) could remove that
> parameter.
>
> Signed-off-by: Douglas Anderson <dianders at chromium.org>
> ---
> Changes in v5: None
> Changes in v4:
> - Manage frame nums better in scheduler new for v4.
>
> Changes in v3: None
> Changes in v2: None
>
>  drivers/usb/dwc2/hcd.h       |  10 +-
>  drivers/usb/dwc2/hcd_queue.c | 355 ++++++++++++++++++++++++++++++++-----------
>  2 files changed, 273 insertions(+), 92 deletions(-)

As an FYI to anyone considering reviewing or applying this particular patch.

It's proposed to squash
<https://chromium-review.googlesource.com/#/c/324185/> as a fixup to
this patch for the next version.  This is intended to fix problems
that Alan Stern reported at
<http://www.spinics.net/lists/linux-usb/msg135678.html>


From heiko at sntech.de  Wed Jan 27 16:01:43 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Thu, 28 Jan 2016 01:01:43 +0100
Subject: [PATCH 0/3] clk: rockchip: introduce a clock type for factor clocks
Message-ID: <1453939306-984-1-git-send-email-heiko@sntech.de>

We're currently creating factor clocks manually during clock init.
That worked well in the beginning, but since the rk3288 we also see
combined gate-factor clocks and the overall number of factor clocks
we create manually seems to be rising. And also sometimes created
artificial separations that do not come from the clock hirarchy.

So I finished something I started some 6 months ago to finally be
able to model factor clocks inside the clock-tree where they belong.

Heiko Stuebner (3):
  clk: rockchip: rk3036: fix parent of hclk_vcodec
  clk: rockchip: add a factor clock type
  clk: rockchip: convert manually created factor clocks to the new type

 drivers/clk/rockchip/clk-rk3036.c | 32 ++++++-----------------
 drivers/clk/rockchip/clk-rk3188.c | 10 +++-----
 drivers/clk/rockchip/clk-rk3228.c | 33 +++++-------------------
 drivers/clk/rockchip/clk-rk3288.c | 23 +++++------------
 drivers/clk/rockchip/clk-rk3368.c | 29 ++++++---------------
 drivers/clk/rockchip/clk.c        | 53 +++++++++++++++++++++++++++++++++++++++
 drivers/clk/rockchip/clk.h        | 28 +++++++++++++++++++++
 7 files changed, 110 insertions(+), 98 deletions(-)

-- 
2.6.4



From heiko at sntech.de  Wed Jan 27 16:01:44 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Thu, 28 Jan 2016 01:01:44 +0100
Subject: [PATCH 1/3] clk: rockchip: rk3036: fix parent of hclk_vcodec
In-Reply-To: <1453939306-984-1-git-send-email-heiko@sntech.de>
References: <1453939306-984-1-git-send-email-heiko@sntech.de>
Message-ID: <1453939306-984-2-git-send-email-heiko@sntech.de>

hclk_vcodec is a child of aclk_vcodec with the fixed factor clock
hclk_vcodec_pre in between and not a child of hclk_disp_pre.

Signed-off-by: Heiko Stuebner <heiko at sntech.de>
---
 drivers/clk/rockchip/clk-rk3036.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/clk/rockchip/clk-rk3036.c b/drivers/clk/rockchip/clk-rk3036.c
index 9c317a3..38139dc 100644
--- a/drivers/clk/rockchip/clk-rk3036.c
+++ b/drivers/clk/rockchip/clk-rk3036.c
@@ -380,7 +380,7 @@ static struct rockchip_clk_branch rk3036_clk_branches[] __initdata = {
 	GATE(HCLK_LCDC, "hclk_lcdc", "hclk_disp_pre", 0, RK2928_CLKGATE_CON(9), 5, GFLAGS),
 
 	/* hclk_video gates */
-	GATE(HCLK_VCODEC, "hclk_vcodec", "hclk_disp_pre", 0, RK2928_CLKGATE_CON(3), 12, GFLAGS),
+	GATE(HCLK_VCODEC, "hclk_vcodec", "hclk_vcodec_pre", 0, RK2928_CLKGATE_CON(3), 12, GFLAGS),
 
 	/* xin24m gates */
 	GATE(SCLK_PVTM_CORE, "sclk_pvtm_core", "xin24m", 0, RK2928_CLKGATE_CON(10), 0, GFLAGS),
-- 
2.6.4



From heiko at sntech.de  Wed Jan 27 16:01:45 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Thu, 28 Jan 2016 01:01:45 +0100
Subject: [PATCH 2/3] clk: rockchip: add a factor clock type
In-Reply-To: <1453939306-984-1-git-send-email-heiko@sntech.de>
References: <1453939306-984-1-git-send-email-heiko@sntech.de>
Message-ID: <1453939306-984-3-git-send-email-heiko@sntech.de>

Add a clock type for fixed factor clocks. This allows us to define fixed
factor clocks where they appear in the clock hierarchy instead of in the
init function.

The additional factor_gate type, finally allows us to model some last
parts of the clock tree correctly.

Signed-off-by: Heiko Stuebner <heiko at sntech.de>
---
 drivers/clk/rockchip/clk.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/rockchip/clk.h | 28 ++++++++++++++++++++++++
 2 files changed, 81 insertions(+)

diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
index d9a0b5d..f66e277 100644
--- a/drivers/clk/rockchip/clk.c
+++ b/drivers/clk/rockchip/clk.c
@@ -260,6 +260,49 @@ static struct clk *rockchip_clk_register_frac_branch(const char *name,
 	return clk;
 }
 
+static struct clk *rockchip_clk_register_factor_branch(const char *name,
+		const char *const *parent_names, u8 num_parents,
+		void __iomem *base, unsigned int mult, unsigned int div,
+		int gate_offset, u8 gate_shift, u8 gate_flags,
+		unsigned long flags, spinlock_t *lock)
+{
+	struct clk *clk;
+	struct clk_gate *gate = NULL;
+	struct clk_fixed_factor *fix = NULL;
+
+	/* without gate, register a simple factor clock */
+	if (gate_offset == 0) {
+		return clk_register_fixed_factor(NULL, name,
+				parent_names[0], flags, mult,
+				div);
+	}
+
+	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+	if (!gate)
+		return ERR_PTR(-ENOMEM);
+
+	gate->flags = gate_flags;
+	gate->reg = base + gate_offset;
+	gate->bit_idx = gate_shift;
+	gate->lock = lock;
+
+	fix = kzalloc(sizeof(*fix), GFP_KERNEL);
+	if (!fix) {
+		kfree(gate);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	fix->mult = mult;
+	fix->div = div;
+
+	clk = clk_register_composite(NULL, name, parent_names, num_parents,
+				     NULL, NULL,
+				     &fix->hw, &clk_fixed_factor_ops,
+				     &gate->hw, &clk_gate_ops, flags);
+
+	return clk;
+}
+
 static DEFINE_SPINLOCK(clk_lock);
 static struct clk **clk_table;
 static void __iomem *reg_base;
@@ -395,6 +438,16 @@ void __init rockchip_clk_register_branches(
 				reg_base + list->muxdiv_offset,
 				list->div_shift, list->div_flags, &clk_lock);
 			break;
+		case branch_factor:
+			flags |= CLK_SET_RATE_PARENT;
+
+			clk = rockchip_clk_register_factor_branch(
+				list->name, list->parent_names,
+				list->num_parents, reg_base,
+				list->div_shift, list->div_width,
+				list->gate_offset, list->gate_shift,
+				list->gate_flags, flags, &clk_lock);
+			break;
 		}
 
 		/* none of the cases above matched */
diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
index ff8bd23..39c198b 100644
--- a/drivers/clk/rockchip/clk.h
+++ b/drivers/clk/rockchip/clk.h
@@ -254,6 +254,7 @@ enum rockchip_clk_branch_type {
 	branch_gate,
 	branch_mmc,
 	branch_inverter,
+	branch_factor,
 };
 
 struct rockchip_clk_branch {
@@ -508,6 +509,33 @@ struct rockchip_clk_branch {
 		.div_flags	= if,				\
 	}
 
+#define FACTOR(_id, cname, pname,  f, fm, fd)			\
+	{							\
+		.id		= _id,				\
+		.branch_type	= branch_factor,		\
+		.name		= cname,			\
+		.parent_names	= (const char *[]){ pname },	\
+		.num_parents	= 1,				\
+		.flags		= f,				\
+		.div_shift	= fm,				\
+		.div_width	= fd,				\
+	}
+
+#define FACTOR_GATE(_id, cname, pname,  f, fm, fd, go, gb, gf)	\
+	{							\
+		.id		= _id,				\
+		.branch_type	= branch_factor,		\
+		.name		= cname,			\
+		.parent_names	= (const char *[]){ pname },	\
+		.num_parents	= 1,				\
+		.flags		= f,				\
+		.div_shift	= fm,				\
+		.div_width	= fd,				\
+		.gate_offset	= go,				\
+		.gate_shift	= gb,				\
+		.gate_flags	= gf,				\
+	}
+
 void rockchip_clk_init(struct device_node *np, void __iomem *base,
 		       unsigned long nr_clks);
 struct regmap *rockchip_clk_get_grf(void);
-- 
2.6.4



From heiko at sntech.de  Wed Jan 27 16:01:46 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Thu, 28 Jan 2016 01:01:46 +0100
Subject: [PATCH 3/3] clk: rockchip: convert manually created factor clocks to
 the new type
In-Reply-To: <1453939306-984-1-git-send-email-heiko@sntech.de>
References: <1453939306-984-1-git-send-email-heiko@sntech.de>
Message-ID: <1453939306-984-4-git-send-email-heiko@sntech.de>

Clean up the init code and move the creation of factor clocks to the
appropriate positions coming from the clock architecture diagrams.

This also unifies the artificial separation of the hclk_vcodec etc clocks
again.

We do keep the separate definition of some watchdog and usb480m pseudo
clocks for now, as they're not real factor clocks from the clock-tree
but placeholders for fixes to come (usb480m gets supplied by the
missing driver for the new usbphy type and the watchdog-gate is sitting
somewhere else together which we cannot model currently).

Signed-off-by: Heiko Stuebner <heiko at sntech.de>
---
 drivers/clk/rockchip/clk-rk3036.c | 32 +++++++-------------------------
 drivers/clk/rockchip/clk-rk3188.c | 10 +++-------
 drivers/clk/rockchip/clk-rk3228.c | 33 ++++++---------------------------
 drivers/clk/rockchip/clk-rk3288.c | 23 ++++++-----------------
 drivers/clk/rockchip/clk-rk3368.c | 29 +++++++----------------------
 5 files changed, 29 insertions(+), 98 deletions(-)

diff --git a/drivers/clk/rockchip/clk-rk3036.c b/drivers/clk/rockchip/clk-rk3036.c
index 38139dc..eaabb43 100644
--- a/drivers/clk/rockchip/clk-rk3036.c
+++ b/drivers/clk/rockchip/clk-rk3036.c
@@ -177,6 +177,9 @@ static struct rockchip_clk_branch rk3036_clk_branches[] __initdata = {
 	GATE(0, "gpll_armclk", "gpll", CLK_IGNORE_UNUSED,
 			RK2928_CLKGATE_CON(0), 6, GFLAGS),
 
+	/* xin12m is created by a cru-internal divider */
+	FACTOR(0, "xin12m", "xin24m", 0, 1, 2),
+
 	/*
 	 * Clock-Architecture Diagram 2
 	 */
@@ -187,6 +190,7 @@ static struct rockchip_clk_branch rk3036_clk_branches[] __initdata = {
 			RK2928_CLKGATE_CON(0), 8, GFLAGS),
 	COMPOSITE_NOGATE(0, "ddrphy2x", mux_ddrphy_p, CLK_IGNORE_UNUSED,
 			RK2928_CLKSEL_CON(26), 8, 1, MFLAGS, 0, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO),
+	FACTOR(0, "ddrphy", "ddrphy2x", 0, 1, 2),
 
 	COMPOSITE_NOMUX(0, "pclk_dbg", "armclk", CLK_IGNORE_UNUSED,
 			RK2928_CLKSEL_CON(1), 0, 4, DFLAGS | CLK_DIVIDER_READ_ONLY,
@@ -263,6 +267,8 @@ static struct rockchip_clk_branch rk3036_clk_branches[] __initdata = {
 	COMPOSITE(0, "aclk_vcodec", mux_pll_src_3plls_p, 0,
 			RK2928_CLKSEL_CON(32), 14, 2, MFLAGS, 8, 5, DFLAGS,
 			RK2928_CLKGATE_CON(3), 11, GFLAGS),
+	FACTOR_GATE(HCLK_VCODEC, "hclk_vcodec", "aclk_vcodec", 0, 1, 4,
+			RK2928_CLKGATE_CON(3), 12, GFLAGS),
 
 	COMPOSITE(0, "aclk_hvec", mux_pll_src_3plls_p, 0,
 			RK2928_CLKSEL_CON(20), 0, 2, MFLAGS, 2, 5, DFLAGS,
@@ -351,6 +357,7 @@ static struct rockchip_clk_branch rk3036_clk_branches[] __initdata = {
 	COMPOSITE_NOMUX(SCLK_MAC, "mac_clk", "mac_clk_ref", 0,
 			RK2928_CLKSEL_CON(21), 9, 5, DFLAGS,
 			RK2928_CLKGATE_CON(2), 6, GFLAGS),
+	FACTOR(0, "sclk_macref_out", "hclk_peri_src", 0, 1, 2),
 
 	MUX(SCLK_HDMI, "dclk_hdmi", mux_dclk_p, 0,
 			RK2928_CLKSEL_CON(31), 0, 1, MFLAGS),
@@ -379,8 +386,6 @@ static struct rockchip_clk_branch rk3036_clk_branches[] __initdata = {
 	GATE(HCLK_VIO_BUS, "hclk_vio_bus", "hclk_disp_pre", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(6), 12, GFLAGS),
 	GATE(HCLK_LCDC, "hclk_lcdc", "hclk_disp_pre", 0, RK2928_CLKGATE_CON(9), 5, GFLAGS),
 
-	/* hclk_video gates */
-	GATE(HCLK_VCODEC, "hclk_vcodec", "hclk_vcodec_pre", 0, RK2928_CLKGATE_CON(3), 12, GFLAGS),
 
 	/* xin24m gates */
 	GATE(SCLK_PVTM_CORE, "sclk_pvtm_core", "xin24m", 0, RK2928_CLKGATE_CON(10), 0, GFLAGS),
@@ -444,34 +449,11 @@ static void __init rk3036_clk_init(struct device_node *np)
 
 	rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
 
-	/* xin12m is created by an cru-internal divider */
-	clk = clk_register_fixed_factor(NULL, "xin12m", "xin24m", 0, 1, 2);
-	if (IS_ERR(clk))
-		pr_warn("%s: could not register clock xin12m: %ld\n",
-			__func__, PTR_ERR(clk));
-
 	clk = clk_register_fixed_factor(NULL, "usb480m", "xin24m", 0, 20, 1);
 	if (IS_ERR(clk))
 		pr_warn("%s: could not register clock usb480m: %ld\n",
 			__func__, PTR_ERR(clk));
 
-	clk = clk_register_fixed_factor(NULL, "ddrphy", "ddrphy2x", 0, 1, 2);
-	if (IS_ERR(clk))
-		pr_warn("%s: could not register clock ddrphy: %ld\n",
-			__func__, PTR_ERR(clk));
-
-	clk = clk_register_fixed_factor(NULL, "hclk_vcodec_pre",
-					"aclk_vcodec", 0, 1, 4);
-	if (IS_ERR(clk))
-		pr_warn("%s: could not register clock hclk_vcodec_pre: %ld\n",
-			__func__, PTR_ERR(clk));
-
-	clk = clk_register_fixed_factor(NULL, "sclk_macref_out",
-					"hclk_peri_src", 0, 1, 2);
-	if (IS_ERR(clk))
-		pr_warn("%s: could not register clock sclk_macref_out: %ld\n",
-			__func__, PTR_ERR(clk));
-
 	rockchip_clk_register_plls(rk3036_pll_clks,
 				   ARRAY_SIZE(rk3036_pll_clks),
 				   RK3036_GRF_SOC_STATUS0);
diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c
index cc1d09d..a9900d5 100644
--- a/drivers/clk/rockchip/clk-rk3188.c
+++ b/drivers/clk/rockchip/clk-rk3188.c
@@ -339,6 +339,9 @@ static struct rockchip_clk_branch common_clk_branches[] __initdata = {
 	INVERTER(0, "pclk_cif0", "pclkin_cif0",
 			RK2928_CLKSEL_CON(30), 8, IFLAGS),
 
+	/* xin12m is created by a cru-internal divider */
+	FACTOR(0, "xin12m", "xin24m", 0, 1, 2),
+
 	/*
 	 * the 480m are generated inside the usb block from these clocks,
 	 * but they are also a source for the hsicphy clock.
@@ -754,7 +757,6 @@ static const char *const rk3188_critical_clocks[] __initconst = {
 static void __init rk3188_common_clk_init(struct device_node *np)
 {
 	void __iomem *reg_base;
-	struct clk *clk;
 
 	reg_base = of_iomap(np, 0);
 	if (!reg_base) {
@@ -764,12 +766,6 @@ static void __init rk3188_common_clk_init(struct device_node *np)
 
 	rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
 
-	/* xin12m is created by an cru-internal divider */
-	clk = clk_register_fixed_factor(NULL, "xin12m", "xin24m", 0, 1, 2);
-	if (IS_ERR(clk))
-		pr_warn("%s: could not register clock xin12m: %ld\n",
-			__func__, PTR_ERR(clk));
-
 	rockchip_clk_register_branches(common_clk_branches,
 				  ARRAY_SIZE(common_clk_branches));
 
diff --git a/drivers/clk/rockchip/clk-rk3228.c b/drivers/clk/rockchip/clk-rk3228.c
index 981a502..fb57c94 100644
--- a/drivers/clk/rockchip/clk-rk3228.c
+++ b/drivers/clk/rockchip/clk-rk3228.c
@@ -187,7 +187,7 @@ static struct rockchip_clk_branch rk3228_clk_branches[] __initdata = {
 			RK2928_CLKGATE_CON(7), 1, GFLAGS),
 	GATE(0, "ddrc", "ddrphy_pre", CLK_IGNORE_UNUSED,
 			RK2928_CLKGATE_CON(8), 5, GFLAGS),
-	GATE(0, "ddrphy", "ddrphy_pre", CLK_IGNORE_UNUSED,
+	FACTOR_GATE(0, "ddrphy", "ddrphy4x", CLK_IGNORE_UNUSED, 1, 4,
 			RK2928_CLKGATE_CON(7), 0, GFLAGS),
 
 	/* PD_CORE */
@@ -240,13 +240,13 @@ static struct rockchip_clk_branch rk3228_clk_branches[] __initdata = {
 	COMPOSITE(0, "aclk_vpu_pre", mux_pll_src_4plls_p, 0,
 			RK2928_CLKSEL_CON(32), 5, 2, MFLAGS, 0, 5, DFLAGS,
 			RK2928_CLKGATE_CON(3), 11, GFLAGS),
-	GATE(0, "hclk_vpu_src", "aclk_vpu_pre", 0,
+	FACTOR_GATE(0, "hclk_vpu_pre", "aclk_vpu_pre", 0, 1, 4,
 			RK2928_CLKGATE_CON(4), 4, GFLAGS),
 
 	COMPOSITE(0, "aclk_rkvdec_pre", mux_pll_src_4plls_p, 0,
 			RK2928_CLKSEL_CON(28), 6, 2, MFLAGS, 0, 5, DFLAGS,
 			RK2928_CLKGATE_CON(3), 2, GFLAGS),
-	GATE(0, "hclk_rkvdec_src", "aclk_rkvdec_pre", 0,
+	FACTOR_GATE(0, "hclk_rkvdec_pre", "aclk_rkvdec_pre", 0, 1, 4,
 			RK2928_CLKGATE_CON(4), 5, GFLAGS),
 
 	COMPOSITE(0, "sclk_vdec_cabac", mux_pll_src_4plls_p, 0,
@@ -371,6 +371,9 @@ static struct rockchip_clk_branch rk3228_clk_branches[] __initdata = {
 	MUX(0, "dclk_vop", mux_dclk_vop_p, 0,
 			RK2928_CLKSEL_CON(27), 1, 1, MFLAGS),
 
+	/* xin12m is created by a cru-internal divider */
+	FACTOR(0, "xin12m", "xin24m", 0, 1, 2),
+
 	COMPOSITE(0, "i2s0_src", mux_pll_src_2plls_p, 0,
 			RK2928_CLKSEL_CON(9), 15, 1, MFLAGS, 0, 7, DFLAGS,
 			RK2928_CLKGATE_CON(0), 3, GFLAGS),
@@ -624,7 +627,6 @@ static const char *const rk3228_critical_clocks[] __initconst = {
 static void __init rk3228_clk_init(struct device_node *np)
 {
 	void __iomem *reg_base;
-	struct clk *clk;
 
 	reg_base = of_iomap(np, 0);
 	if (!reg_base) {
@@ -634,29 +636,6 @@ static void __init rk3228_clk_init(struct device_node *np)
 
 	rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
 
-	/* xin12m is created by an cru-internal divider */
-	clk = clk_register_fixed_factor(NULL, "xin12m", "xin24m", 0, 1, 2);
-	if (IS_ERR(clk))
-		pr_warn("%s: could not register clock xin12m: %ld\n",
-				__func__, PTR_ERR(clk));
-
-	clk = clk_register_fixed_factor(NULL, "ddrphy_pre", "ddrphy4x", 0, 1, 4);
-	if (IS_ERR(clk))
-		pr_warn("%s: could not register clock ddrphy_pre: %ld\n",
-			__func__, PTR_ERR(clk));
-
-	clk = clk_register_fixed_factor(NULL, "hclk_vpu_pre",
-					"hclk_vpu_src", 0, 1, 4);
-	if (IS_ERR(clk))
-		pr_warn("%s: could not register clock hclk_vpu_pre: %ld\n",
-			__func__, PTR_ERR(clk));
-
-	clk = clk_register_fixed_factor(NULL, "hclk_rkvdec_pre",
-					"hclk_rkvdec_src", 0, 1, 4);
-	if (IS_ERR(clk))
-		pr_warn("%s: could not register clock hclk_rkvdec_pre: %ld\n",
-			__func__, PTR_ERR(clk));
-
 	rockchip_clk_register_plls(rk3228_pll_clks,
 				   ARRAY_SIZE(rk3228_pll_clks),
 				   RK3228_GRF_SOC_STATUS0);
diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c
index 0d23937..b4c3c4b 100644
--- a/drivers/clk/rockchip/clk-rk3288.c
+++ b/drivers/clk/rockchip/clk-rk3288.c
@@ -333,6 +333,9 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = {
 	GATE(0, "aclk_bus_2pmu", "aclk_cpu_pre", CLK_IGNORE_UNUSED,
 			RK3288_CLKGATE_CON(0), 7, GFLAGS),
 
+	/* xin12m is created by a cru-internal divider */
+	FACTOR(0, "xin12m", "xin24m", 0, 1, 2),
+
 	COMPOSITE(0, "i2s_src", mux_pll_src_cpll_gpll_p, 0,
 			RK3288_CLKSEL_CON(4), 15, 1, MFLAGS, 0, 7, DFLAGS,
 			RK3288_CLKGATE_CON(4), 1, GFLAGS),
@@ -399,12 +402,10 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = {
 	 */
 	GATE(ACLK_VCODEC, "aclk_vcodec", "aclk_vdpu", 0,
 		RK3288_CLKGATE_CON(9), 0, GFLAGS),
-	/*
-	 * We introduce a virtul node of hclk_vodec_pre_v to split one clock
-	 * struct with a gate and a fix divider into two node in software.
-	 */
-	GATE(0, "hclk_vcodec_pre_v", "aclk_vdpu", 0,
+
+	FACTOR_GATE(0, "hclk_vcodec_pre", "aclk_vdpu", 0, 1, 4,
 		RK3288_CLKGATE_CON(3), 10, GFLAGS),
+
 	GATE(HCLK_VCODEC, "hclk_vcodec", "hclk_vcodec_pre", 0,
 		RK3288_CLKGATE_CON(9), 1, GFLAGS),
 
@@ -888,18 +889,6 @@ static void __init rk3288_clk_init(struct device_node *np)
 
 	rockchip_clk_init(np, rk3288_cru_base, CLK_NR_CLKS);
 
-	/* xin12m is created by an cru-internal divider */
-	clk = clk_register_fixed_factor(NULL, "xin12m", "xin24m", 0, 1, 2);
-	if (IS_ERR(clk))
-		pr_warn("%s: could not register clock xin12m: %ld\n",
-			__func__, PTR_ERR(clk));
-
-	clk = clk_register_fixed_factor(NULL, "hclk_vcodec_pre",
-					"hclk_vcodec_pre_v", 0, 1, 4);
-	if (IS_ERR(clk))
-		pr_warn("%s: could not register clock hclk_vcodec_pre: %ld\n",
-			__func__, PTR_ERR(clk));
-
 	/* Watchdog pclk is controlled by RK3288_SGRF_SOC_CON0[1]. */
 	clk = clk_register_fixed_factor(NULL, "pclk_wdt", "pclk_pd_alive", 0, 1, 1);
 	if (IS_ERR(clk))
diff --git a/drivers/clk/rockchip/clk-rk3368.c b/drivers/clk/rockchip/clk-rk3368.c
index e90abe8..1065f3e 100644
--- a/drivers/clk/rockchip/clk-rk3368.c
+++ b/drivers/clk/rockchip/clk-rk3368.c
@@ -248,6 +248,9 @@ static struct rockchip_clk_branch rk3368_clk_branches[] __initdata = {
 	 * Clock-Architecture Diagram 2
 	 */
 
+	/* xin12m is created by an cru-internal divider */
+	FACTOR(0, "xin12m", "xin24m", 0, 1, 2),
+
 	MUX(SCLK_USBPHY480M, "usbphy_480m", mux_usbphy480m_p, CLK_SET_RATE_PARENT,
 			RK3368_CLKSEL_CON(13), 8, 1, MFLAGS),
 
@@ -299,7 +302,7 @@ static struct rockchip_clk_branch rk3368_clk_branches[] __initdata = {
 	COMPOSITE_NOGATE_DIVTBL(0, "ddrphy_src", mux_ddrphy_p, CLK_IGNORE_UNUSED,
 			RK3368_CLKSEL_CON(13), 4, 1, MFLAGS, 0, 2, DFLAGS, div_ddrphy_t),
 
-	GATE(0, "sclk_ddr", "ddrphy_div4", CLK_IGNORE_UNUSED,
+	FACTOR_GATE(0, "sclk_ddr", "ddrphy_src", CLK_IGNORE_UNUSED, 1, 4,
 			RK3368_CLKGATE_CON(6), 14, GFLAGS),
 	GATE(0, "sclk_ddr4x", "ddrphy_src", CLK_IGNORE_UNUSED,
 			RK3368_CLKGATE_CON(6), 15, GFLAGS),
@@ -392,10 +395,10 @@ static struct rockchip_clk_branch rk3368_clk_branches[] __initdata = {
 			RK3368_CLKGATE_CON(4), 7, GFLAGS),
 
 	/*
-	 * We introduce a virtual node of hclk_vodec_pre_v to split one clock
-	 * struct with a gate and a fix divider into two node in software.
+	 * We use aclk_vdpu by default ---GRF_SOC_CON0[7] setting in system,
+	 * so we ignore the mux and make clocks nodes as following,
 	 */
-	GATE(0, "hclk_video_pre_v", "aclk_vdpu", 0,
+	FACTOR_GATE(0, "hclk_video_pre", "aclk_vdpu", 0, 1, 4,
 		RK3368_CLKGATE_CON(4), 8, GFLAGS),
 
 	COMPOSITE(0, "sclk_hevc_cabac_src", mux_pll_src_cpll_gpll_npll_usb_p, 0,
@@ -842,24 +845,6 @@ static void __init rk3368_clk_init(struct device_node *np)
 
 	rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
 
-	/* xin12m is created by a cru-internal divider */
-	clk = clk_register_fixed_factor(NULL, "xin12m", "xin24m", 0, 1, 2);
-	if (IS_ERR(clk))
-		pr_warn("%s: could not register clock xin12m: %ld\n",
-			__func__, PTR_ERR(clk));
-
-	/* ddrphy_div4 is created by a cru-internal divider */
-	clk = clk_register_fixed_factor(NULL, "ddrphy_div4", "ddrphy_src", 0, 1, 4);
-	if (IS_ERR(clk))
-		pr_warn("%s: could not register clock xin12m: %ld\n",
-			__func__, PTR_ERR(clk));
-
-	clk = clk_register_fixed_factor(NULL, "hclk_video_pre",
-					"hclk_video_pre_v", 0, 1, 4);
-	if (IS_ERR(clk))
-		pr_warn("%s: could not register clock hclk_vcodec_pre: %ld\n",
-			__func__, PTR_ERR(clk));
-
 	/* Watchdog pclk is controlled by sgrf_soc_con3[7]. */
 	clk = clk_register_fixed_factor(NULL, "pclk_wdt", "pclk_pd_alive", 0, 1, 1);
 	if (IS_ERR(clk))
-- 
2.6.4



From kever.yang at rock-chips.com  Wed Jan 27 17:20:22 2016
From: kever.yang at rock-chips.com (Kever Yang)
Date: Thu, 28 Jan 2016 09:20:22 +0800
Subject: [PATCH v5 07/21] usb: dwc2: hcd: fix split transfer schedule
 sequence
In-Reply-To: <1453486736-15358-8-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
 <1453486736-15358-8-git-send-email-dianders@chromium.org>
Message-ID: <56A96CD6.3040807@rock-chips.com>

Hi Doug,

I test this patch with USB 2.0 analyzer, and it make the CSPLIT in the
same order with the SSPLIT, so

Reviewed-by: Kever Yang <kever.yang at rock-chips.com>
Tested-by: Kever Yang <kever.yang at rock-chips.com>

Thanks,
- Kever
On 01/23/2016 02:18 AM, Douglas Anderson wrote:
> We're supposed to keep outstanding splits in order.  Keep track of a
> list of the order of splits and process channel interrupts in that
> order.
>
> Without this change and the following setup:
> * Rockchip rk3288 Chromebook, using port ff540000
>    -> Pluggable 7-port Hub with Charging (powered)
>       -> Microsoft Wireless Keyboard 2000 in port 1.
>       -> Das Keyboard in port 2.
>
> ...I find that I get dropped keys on the Microsoft keyboard (I'm sure
> there are other combinations that fail, but this documents my test).
> Specifically I've been typing "hahahahahahaha" on the keyboard and often
> see keys dropped or repeated.
>
> After this change the above setup works properly.  This patch is based
> on a previous patch proposed by Yunzhi Li ("usb: dwc2: hcd: fix periodic
> transfer schedule sequence")
>
> Signed-off-by: Douglas Anderson <dianders at chromium.org>
> Signed-off-by: Yunzhi Li <lyz at rock-chips.com>
> ---
> Changes in v5:
> - Move list maintenance to hcd.c to avoid gadget-only compile error
>
> Changes in v4:
> - fix split transfer schedule sequence new for v4.
>
> Changes in v3: None
> Changes in v2: None
>
>   drivers/usb/dwc2/core.c     |  2 ++
>   drivers/usb/dwc2/core.h     |  2 ++
>   drivers/usb/dwc2/hcd.c      |  8 ++++++++
>   drivers/usb/dwc2/hcd.h      |  2 ++
>   drivers/usb/dwc2/hcd_intr.c | 17 +++++++++++++++++
>   5 files changed, 31 insertions(+)
>
> diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
> index 73f2771b7740..ed73b26818c0 100644
> --- a/drivers/usb/dwc2/core.c
> +++ b/drivers/usb/dwc2/core.c
> @@ -1676,6 +1676,8 @@ void dwc2_hc_cleanup(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan)
>   
>   	chan->xfer_started = 0;
>   
> +	list_del_init(&chan->split_order_list_entry);
> +
>   	/*
>   	 * Clear channel interrupt enables and any unhandled channel interrupt
>   	 * conditions
> diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
> index 7fb6434f4639..538cf38af0e4 100644
> --- a/drivers/usb/dwc2/core.h
> +++ b/drivers/usb/dwc2/core.h
> @@ -657,6 +657,7 @@ struct dwc2_hregs_backup {
>    *                      periodic_sched_ready because it must be rescheduled for
>    *                      the next frame. Otherwise, the item moves to
>    *                      periodic_sched_inactive.
> + * @split_order:        List keeping track of channels doing splits, in order.
>    * @periodic_usecs:     Total bandwidth claimed so far for periodic transfers.
>    *                      This value is in microseconds per (micro)frame. The
>    *                      assumption is that all periodic transfers may occur in
> @@ -780,6 +781,7 @@ struct dwc2_hsotg {
>   	struct list_head periodic_sched_ready;
>   	struct list_head periodic_sched_assigned;
>   	struct list_head periodic_sched_queued;
> +	struct list_head split_order;
>   	u16 periodic_usecs;
>   	u16 frame_usecs[8];
>   	u16 frame_number;
> diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
> index d2daaea88d91..87ad5bf2d166 100644
> --- a/drivers/usb/dwc2/hcd.c
> +++ b/drivers/usb/dwc2/hcd.c
> @@ -1045,6 +1045,11 @@ static int dwc2_queue_transaction(struct dwc2_hsotg *hsotg,
>   {
>   	int retval = 0;
>   
> +	if (chan->do_split)
> +		/* Put ourselves on the list to keep order straight */
> +		list_move_tail(&chan->split_order_list_entry,
> +			       &hsotg->split_order);
> +
>   	if (hsotg->core_params->dma_enable > 0) {
>   		if (hsotg->core_params->dma_desc_enable > 0) {
>   			if (!chan->xfer_started ||
> @@ -3151,6 +3156,8 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
>   	INIT_LIST_HEAD(&hsotg->periodic_sched_assigned);
>   	INIT_LIST_HEAD(&hsotg->periodic_sched_queued);
>   
> +	INIT_LIST_HEAD(&hsotg->split_order);
> +
>   	/*
>   	 * Create a host channel descriptor for each host channel implemented
>   	 * in the controller. Initialize the channel descriptor array.
> @@ -3164,6 +3171,7 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
>   		if (channel == NULL)
>   			goto error3;
>   		channel->hc_num = i;
> +		INIT_LIST_HEAD(&channel->split_order_list_entry);
>   		hsotg->hc_ptr_array[i] = channel;
>   	}
>   
> diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
> index 42f2e4e233da..1b46e2e617cc 100644
> --- a/drivers/usb/dwc2/hcd.h
> +++ b/drivers/usb/dwc2/hcd.h
> @@ -106,6 +106,7 @@ struct dwc2_qh;
>    * @hc_list_entry:      For linking to list of host channels
>    * @desc_list_addr:     Current QH's descriptor list DMA address
>    * @desc_list_sz:       Current QH's descriptor list size
> + * @split_order_list_entry: List entry for keeping track of the order of splits
>    *
>    * This structure represents the state of a single host channel when acting in
>    * host mode. It contains the data items needed to transfer packets to an
> @@ -158,6 +159,7 @@ struct dwc2_host_chan {
>   	struct list_head hc_list_entry;
>   	dma_addr_t desc_list_addr;
>   	u32 desc_list_sz;
> +	struct list_head split_order_list_entry;
>   };
>   
>   struct dwc2_hcd_pipe_info {
> diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
> index 2c521c00e5e0..577c91096a51 100644
> --- a/drivers/usb/dwc2/hcd_intr.c
> +++ b/drivers/usb/dwc2/hcd_intr.c
> @@ -2067,6 +2067,7 @@ static void dwc2_hc_intr(struct dwc2_hsotg *hsotg)
>   {
>   	u32 haint;
>   	int i;
> +	struct dwc2_host_chan *chan, *chan_tmp;
>   
>   	haint = dwc2_readl(hsotg->regs + HAINT);
>   	if (dbg_perio()) {
> @@ -2075,6 +2076,22 @@ static void dwc2_hc_intr(struct dwc2_hsotg *hsotg)
>   		dev_vdbg(hsotg->dev, "HAINT=%08x\n", haint);
>   	}
>   
> +	/*
> +	 * According to USB 2.0 spec section 11.18.8, a host must
> +	 * issue complete-split transactions in a microframe for a
> +	 * set of full-/low-speed endpoints in the same relative
> +	 * order as the start-splits were issued in a microframe for.
> +	 */
> +	list_for_each_entry_safe(chan, chan_tmp, &hsotg->split_order,
> +				 split_order_list_entry) {
> +		int hc_num = chan->hc_num;
> +
> +		if (haint & (1 << hc_num)) {
> +			dwc2_hc_n_intr(hsotg, hc_num);
> +			haint &= ~(1 << hc_num);
> +		}
> +	}
> +
>   	for (i = 0; i < hsotg->core_params->host_channels; i++) {
>   		if (haint & (1 << i))
>   			dwc2_hc_n_intr(hsotg, i);




From kever.yang at rock-chips.com  Wed Jan 27 19:10:44 2016
From: kever.yang at rock-chips.com (Kever Yang)
Date: Thu, 28 Jan 2016 11:10:44 +0800
Subject: [PATCH v5 04/21] usb: dwc2: host: Set host_perio_tx_fifo_size
 to 304 for rk3066
In-Reply-To: <1453486736-15358-5-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
 <1453486736-15358-5-git-send-email-dianders@chromium.org>
Message-ID: <56A986B4.2050002@rock-chips.com>

Hi Doug,

We are using the minimum FIFO size mode for TX now, which only
equal to one max packet size.

The addition FIFO size may help shorten the inter-packet data
prepare latency when the bus/DRAM is busy.

For the actual usage in TX, we have very little change to use the
period TX FIFO with more than one max packet size in host mode.
So far as I know, usb audio use the isochronous tx FIFO, but this
king of device won't have much data payload and won't, I haven't
see a usb audio have more data than 1024byte/ms.

So I suggest we assign this 48 words to host_nperio_tx_fifi_size instead
if we have to do this. Because we are using device base on bulk transaction
like U-disk very frequently.

Thanks,
- Kever
On 01/23/2016 02:18 AM, Douglas Anderson wrote:
> Looking at rk3288, there are two dwc2 ports.  One has 960
> total_fifo_size and the other 972.
>
> We're currently assigning 528 + 128 + 256 = 912.  That means we're
> wasting 48 words on one port and 60 words on the other.  Since we have
> one settings block for both ports, let's just eat the 48 words that we
> know we have and assign them to what's probably the most
> latency-senstive part of the system: periodic transfers.
>
> Presumably other rk3288 parts using the rk3066 settings also have these
> extra 48 words as well.
>
> Note: ideally we'd adjust dwc2_calculate_dynamic_fifo() to handle this
> more dynamically, but until that's done hardcoding things here seems OK.
>
> NOTE: no known use cases are improved by this patch, but in my stressful
> USB tests I certainly see my periodic FIFO filling up both before and
> after this change.  Presumably there are some USB configurations where
> the periodic FIFO would have been filled before where it no longer is.
>
> Signed-off-by: Douglas Anderson <dianders at chromium.org>
> ---
> Changes in v5: None
> Changes in v4:
> - Set host_perio_tx_fifo_size to 304 for rk3066 new for v4.
>
> Changes in v3: None
> Changes in v2: None
>
>   drivers/usb/dwc2/platform.c | 2 +-
>   1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
> index b6d7666e715c..afdcdeee266d 100644
> --- a/drivers/usb/dwc2/platform.c
> +++ b/drivers/usb/dwc2/platform.c
> @@ -128,7 +128,7 @@ static const struct dwc2_core_params params_rk3066 = {
>   	.en_multiple_tx_fifo		= -1,
>   	.host_rx_fifo_size		= 528,	/* 528 DWORDs */
>   	.host_nperio_tx_fifo_size	= 128,	/* 128 DWORDs */
> -	.host_perio_tx_fifo_size	= 256,	/* 256 DWORDs */
> +	.host_perio_tx_fifo_size	= 304,	/* 304 DWORDs */
>   	.max_transfer_size		= -1,
>   	.max_packet_count		= -1,
>   	.host_channels			= -1,




From kever.yang at rock-chips.com  Wed Jan 27 19:25:59 2016
From: kever.yang at rock-chips.com (Kever Yang)
Date: Thu, 28 Jan 2016 11:25:59 +0800
Subject: [PATCH v5 05/21] usb: dwc2: host: Avoid use of chan->qh after
 qh freed
In-Reply-To: <1453486736-15358-6-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
 <1453486736-15358-6-git-send-email-dianders@chromium.org>
Message-ID: <56A98A47.2010705@rock-chips.com>

Hi Doug,

The NULL pointer bug is one of the most frequent issue we met
during hot plug stress test, thanks for this bug fix.

Reviewed-by: Kever Yang <kever.yang at rock-chips.com>

Thanks,
- Kever
On 01/23/2016 02:18 AM, Douglas Anderson wrote:
> When poking around with USB devices with slub_debug enabled, I found
> another obvious use after free.  Turns out that in dwc2_hc_n_intr() I
> was in a state when the contents of chan->qh was filled with 0x6b,
> indicating that chan->qh was freed but chan still had a reference to
> it.
>
> Let's make sure that whenever we free qh we also make sure we remove a
> reference from its channel.
>
> The bug fixed here doesn't appear to be new--I believe I just got lucky
> and happened to see it while stress testing.
>
> Signed-off-by: Douglas Anderson <dianders at chromium.org>
> ---
> Changes in v5: None
> Changes in v4:
> - Avoid use of chan->qh after qh freed new for v4.
>
> Changes in v3: None
> Changes in v2: None
>
>   drivers/usb/dwc2/hcd.c      |  8 ++++++++
>   drivers/usb/dwc2/hcd_intr.c | 10 ++++++++++
>   2 files changed, 18 insertions(+)
>
> diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
> index bc4bdbc1534e..7783c8ba0173 100644
> --- a/drivers/usb/dwc2/hcd.c
> +++ b/drivers/usb/dwc2/hcd.c
> @@ -164,6 +164,9 @@ static void dwc2_qh_list_free(struct dwc2_hsotg *hsotg,
>   					 qtd_list_entry)
>   			dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh);
>   
> +		if (qh->channel && qh->channel->qh == qh)
> +			qh->channel->qh = NULL;
> +
>   		spin_unlock_irqrestore(&hsotg->lock, flags);
>   		dwc2_hcd_qh_free(hsotg, qh);
>   		spin_lock_irqsave(&hsotg->lock, flags);
> @@ -554,7 +557,12 @@ static int dwc2_hcd_endpoint_disable(struct dwc2_hsotg *hsotg,
>   		dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh);
>   
>   	ep->hcpriv = NULL;
> +
> +	if (qh->channel && qh->channel->qh == qh)
> +		qh->channel->qh = NULL;
> +
>   	spin_unlock_irqrestore(&hsotg->lock, flags);
> +
>   	dwc2_hcd_qh_free(hsotg, qh);
>   
>   	return 0;
> diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
> index 352c98364317..99efc2bd1617 100644
> --- a/drivers/usb/dwc2/hcd_intr.c
> +++ b/drivers/usb/dwc2/hcd_intr.c
> @@ -1935,6 +1935,16 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum)
>   	}
>   
>   	dwc2_writel(hcint, hsotg->regs + HCINT(chnum));
> +
> +	/*
> +	 * If we got an interrupt after someone called
> +	 * dwc2_hcd_endpoint_disable() we don't want to crash below
> +	 */
> +	if (!chan->qh) {
> +		dev_warn(hsotg->dev, "Interrupt on disabled channel\n");
> +		return;
> +	}
> +
>   	chan->hcint = hcint;
>   	hcint &= hcintmsk;
>   




From dianders at chromium.org  Wed Jan 27 19:28:58 2016
From: dianders at chromium.org (Doug Anderson)
Date: Wed, 27 Jan 2016 19:28:58 -0800
Subject: [PATCH v5 04/21] usb: dwc2: host: Set host_perio_tx_fifo_size to
 304 for rk3066
In-Reply-To: <56A986B4.2050002@rock-chips.com>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
 <1453486736-15358-5-git-send-email-dianders@chromium.org>
 <56A986B4.2050002@rock-chips.com>
Message-ID: <CAD=FV=VAkY0ijkUBrgjY04+OfYq35u=MbqbbomeQxp_J179iHw@mail.gmail.com>

Kever,

On Wed, Jan 27, 2016 at 7:10 PM, Kever Yang <kever.yang at rock-chips.com> wrote:
> Hi Doug,
>
> We are using the minimum FIFO size mode for TX now, which only
> equal to one max packet size.
>
> The addition FIFO size may help shorten the inter-packet data
> prepare latency when the bus/DRAM is busy.
>
> For the actual usage in TX, we have very little change to use the
> period TX FIFO with more than one max packet size in host mode.
> So far as I know, usb audio use the isochronous tx FIFO, but this
> king of device won't have much data payload and won't, I haven't
> see a usb audio have more data than 1024byte/ms.
>
> So I suggest we assign this 48 words to host_nperio_tx_fifi_size instead
> if we have to do this. Because we are using device base on bulk transaction
> like U-disk very frequently.

Try using a USB webcam.  With that plus a USB audio device it's easy
to overwhelm the periodic TX FIFO.

If we overwhelm the periodic TX FIFO we might actually fail to
transmit ISO or INT packets at the scheduled time.  That seems more
serious of a problem to try to fix than eeking out a tiny bit
performance on a USB disk.  ...but of course, it all depends on what
you consider important.  ;)

We could split the difference, I suppose and put half on each?

-Doug


From kever.yang at rock-chips.com  Wed Jan 27 19:39:14 2016
From: kever.yang at rock-chips.com (Kever Yang)
Date: Thu, 28 Jan 2016 11:39:14 +0800
Subject: [PATCH v5 08/21] usb: dwc2: host: Add scheduler tracing
In-Reply-To: <1453486736-15358-9-git-send-email-dianders@chromium.org>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
 <1453486736-15358-9-git-send-email-dianders@chromium.org>
Message-ID: <56A98D62.8040505@rock-chips.com>

Hi Doug,

     Very good idea here, I used to add some local patch and with USB
analyzer to debug this part of code, and we can use more tools with
your patch now, I can try kernelshark next time.

     Do you consider to add tracing for qtd? Then we can track the whole
lift cycle of urb in dwc2 driver.

     For this patch:
Reviewed-by: Kever Yang <kever.yang at rock-chips.com>

Thanks,
- Kever
On 01/23/2016 02:18 AM, Douglas Anderson wrote:
> In preparation for future changes to the scheduler let's add some
> tracing that makes it easy for us to see what's happening.  By default
> this tracing will be off.
>
> By changing "core.h" you can easily trace to ftrace, the console, or
> nowhere.
>
> Signed-off-by: Douglas Anderson <dianders at chromium.org>
> ---
> Changes in v5: None
> Changes in v4:
> - Retooled scheduler tracing a bit, so left off John's Ack from v3.
>
> Changes in v3: None
> Changes in v2: None
>
>   drivers/usb/dwc2/core.h      | 20 ++++++++++++++++++++
>   drivers/usb/dwc2/hcd.h       |  5 +++++
>   drivers/usb/dwc2/hcd_intr.c  |  6 +++++-
>   drivers/usb/dwc2/hcd_queue.c | 24 +++++++++++++++++++++++-
>   4 files changed, 53 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
> index 538cf38af0e4..18f9e4045643 100644
> --- a/drivers/usb/dwc2/core.h
> +++ b/drivers/usb/dwc2/core.h
> @@ -44,6 +44,26 @@
>   #include <linux/usb/phy.h>
>   #include "hw.h"
>   
> +/*
> + * Suggested defines for tracers:
> + * - no_printk:    Disable tracing
> + * - pr_info:      Print this info to the console
> + * - trace_printk: Print this info to trace buffer (good for verbose logging)
> + */
> +
> +#define DWC2_TRACE_SCHEDULER		no_printk
> +#define DWC2_TRACE_SCHEDULER_VB		no_printk
> +
> +/* Detailed scheduler tracing, but won't overwhelm console */
> +#define dwc2_sch_dbg(hsotg, fmt, ...)					\
> +	DWC2_TRACE_SCHEDULER(pr_fmt("%s: SCH: " fmt),			\
> +			     dev_name(hsotg->dev), ##__VA_ARGS__)
> +
> +/* Verbose scheduler tracing */
> +#define dwc2_sch_vdbg(hsotg, fmt, ...)					\
> +	DWC2_TRACE_SCHEDULER_VB(pr_fmt("%s: SCH: " fmt),		\
> +				dev_name(hsotg->dev), ##__VA_ARGS__)
> +
>   static inline u32 dwc2_readl(const void __iomem *addr)
>   {
>   	u32 value = __raw_readl(addr);
> diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
> index 1b46e2e617cc..809bc4ff9116 100644
> --- a/drivers/usb/dwc2/hcd.h
> +++ b/drivers/usb/dwc2/hcd.h
> @@ -563,6 +563,11 @@ static inline u16 dwc2_frame_num_inc(u16 frame, u16 inc)
>   	return (frame + inc) & HFNUM_MAX_FRNUM;
>   }
>   
> +static inline u16 dwc2_frame_num_dec(u16 frame, u16 dec)
> +{
> +	return (frame + HFNUM_MAX_FRNUM + 1 - dec) & HFNUM_MAX_FRNUM;
> +}
> +
>   static inline u16 dwc2_full_frame_num(u16 frame)
>   {
>   	return (frame & HFNUM_MAX_FRNUM) >> 3;
> diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
> index 577c91096a51..5d25a5ec9736 100644
> --- a/drivers/usb/dwc2/hcd_intr.c
> +++ b/drivers/usb/dwc2/hcd_intr.c
> @@ -138,13 +138,17 @@ static void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
>   	while (qh_entry != &hsotg->periodic_sched_inactive) {
>   		qh = list_entry(qh_entry, struct dwc2_qh, qh_list_entry);
>   		qh_entry = qh_entry->next;
> -		if (dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number))
> +		if (dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number)) {
> +			dwc2_sch_vdbg(hsotg, "QH=%p ready fn=%04x, sch=%04x\n",
> +				      qh, hsotg->frame_number, qh->sched_frame);
> +
>   			/*
>   			 * Move QH to the ready list to be executed next
>   			 * (micro)frame
>   			 */
>   			list_move_tail(&qh->qh_list_entry,
>   				  &hsotg->periodic_sched_ready);
> +		}
>   	}
>   	tr_type = dwc2_hcd_select_transactions(hsotg);
>   	if (tr_type != DWC2_TRANSACTION_NONE)
> diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
> index bc632a72f611..0e9faa75593c 100644
> --- a/drivers/usb/dwc2/hcd_queue.c
> +++ b/drivers/usb/dwc2/hcd_queue.c
> @@ -113,6 +113,9 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
>   		qh->sched_frame = dwc2_frame_num_inc(hsotg->frame_number,
>   						     SCHEDULE_SLOP);
>   		qh->interval = urb->interval;
> +		dwc2_sch_dbg(hsotg, "QH=%p init sch=%04x, fn=%04x, int=%#x\n",
> +			     qh, qh->sched_frame, hsotg->frame_number,
> +			     qh->interval);
>   #if 0
>   		/* Increase interrupt polling rate for debugging */
>   		if (qh->ep_type == USB_ENDPOINT_XFER_INT)
> @@ -126,6 +129,11 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
>   			qh->interval *= 8;
>   			qh->sched_frame |= 0x7;
>   			qh->start_split_frame = qh->sched_frame;
> +			dwc2_sch_dbg(hsotg,
> +				     "QH=%p init*8 sch=%04x, fn=%04x, int=%#x\n",
> +				     qh, qh->sched_frame, hsotg->frame_number,
> +				     qh->interval);
> +
>   		}
>   		dev_dbg(hsotg->dev, "interval=%d\n", qh->interval);
>   	}
> @@ -482,6 +490,8 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
>   		if (frame >= 0) {
>   			qh->sched_frame &= ~0x7;
>   			qh->sched_frame |= (frame & 7);
> +			dwc2_sch_dbg(hsotg, "QH=%p sched_p sch=%04x, uf=%d\n",
> +				     qh, qh->sched_frame, frame);
>   		}
>   
>   		if (status > 0)
> @@ -583,10 +593,16 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
>   
>   	if (!dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number) &&
>   			!hsotg->frame_number) {
> +		u16 new_frame;
> +
>   		dev_dbg(hsotg->dev,
>   				"reset frame number counter\n");
> -		qh->sched_frame = dwc2_frame_num_inc(hsotg->frame_number,
> +		new_frame = dwc2_frame_num_inc(hsotg->frame_number,
>   				SCHEDULE_SLOP);
> +
> +		dwc2_sch_vdbg(hsotg, "QH=%p reset sch=%04x=>%04x\n",
> +			      qh, qh->sched_frame, new_frame);
> +		qh->sched_frame = new_frame;
>   	}
>   
>   	/* Add the new QH to the appropriate schedule */
> @@ -652,6 +668,7 @@ static void dwc2_sched_periodic_split(struct dwc2_hsotg *hsotg,
>   				      int sched_next_periodic_split)
>   {
>   	u16 incr;
> +	u16 old_frame = qh->sched_frame;
>   
>   	if (sched_next_periodic_split) {
>   		qh->sched_frame = frame_number;
> @@ -677,6 +694,11 @@ static void dwc2_sched_periodic_split(struct dwc2_hsotg *hsotg,
>   		qh->sched_frame |= 0x7;
>   		qh->start_split_frame = qh->sched_frame;
>   	}
> +
> +	dwc2_sch_vdbg(hsotg, "QH=%p next(%d) fn=%04x, sch=%04x=>%04x (%+d)\n",
> +		      qh, sched_next_periodic_split, frame_number, old_frame,
> +		      qh->sched_frame,
> +		      dwc2_frame_num_dec(qh->sched_frame, old_frame));
>   }
>   
>   /*




From kever.yang at rock-chips.com  Wed Jan 27 22:41:14 2016
From: kever.yang at rock-chips.com (Kever Yang)
Date: Thu, 28 Jan 2016 14:41:14 +0800
Subject: [PATCH v5 04/21] usb: dwc2: host: Set host_perio_tx_fifo_size
 to 304 for rk3066
In-Reply-To: <CAD=FV=VAkY0ijkUBrgjY04+OfYq35u=MbqbbomeQxp_J179iHw@mail.gmail.com>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>	<1453486736-15358-5-git-send-email-dianders@chromium.org>	<56A986B4.2050002@rock-chips.com>
 <CAD=FV=VAkY0ijkUBrgjY04+OfYq35u=MbqbbomeQxp_J179iHw@mail.gmail.com>
Message-ID: <56A9B80A.2020506@rock-chips.com>

Hi Doug,

We are in HOST mode, we only need to receive data from USB camera
with RX FIFO, and no need to use TX FIFO for USB webcam, right? :)

Any way, I think we need to NAK this patch after look into the design
of dwc2 controller. Because all the dwc2 controller inside the Rockchip
chips don't support the thresholding FIFO mode, in this case, there is
no more transaction before a whole packet is send out and the dwc2 only
care if the available FIFO is enough for next packet or not.

So, the addition 48 words won't help to shorten the latency for data prepare
in this case.

Thanks,
- Kever
On 01/28/2016 11:28 AM, Doug Anderson wrote:
> Kever,
>
> On Wed, Jan 27, 2016 at 7:10 PM, Kever Yang <kever.yang at rock-chips.com> wrote:
>> Hi Doug,
>>
>> We are using the minimum FIFO size mode for TX now, which only
>> equal to one max packet size.
>>
>> The addition FIFO size may help shorten the inter-packet data
>> prepare latency when the bus/DRAM is busy.
>>
>> For the actual usage in TX, we have very little change to use the
>> period TX FIFO with more than one max packet size in host mode.
>> So far as I know, usb audio use the isochronous tx FIFO, but this
>> king of device won't have much data payload and won't, I haven't
>> see a usb audio have more data than 1024byte/ms.
>>
>> So I suggest we assign this 48 words to host_nperio_tx_fifi_size instead
>> if we have to do this. Because we are using device base on bulk transaction
>> like U-disk very frequently.
> Try using a USB webcam.  With that plus a USB audio device it's easy
> to overwhelm the periodic TX FIFO.
>
> If we overwhelm the periodic TX FIFO we might actually fail to
> transmit ISO or INT packets at the scheduled time.  That seems more
> serious of a problem to try to fix than eeking out a tiny bit
> performance on a USB disk.  ...but of course, it all depends on what
> you consider important.  ;)
>
> We could split the difference, I suppose and put half on each?
>
> -Doug
>




From kever.yang at rock-chips.com  Thu Jan 28 00:28:19 2016
From: kever.yang at rock-chips.com (Kever Yang)
Date: Thu, 28 Jan 2016 16:28:19 +0800
Subject: [PATCH v5 03/21] usb: dwc2: host: Set host_rx_fifo_size to 528
 for rk3066
In-Reply-To: <CAD=FV=XKAzVOLCkFBH1nXeiaKeU4FoFN2CuRNMankA4xsgPsuw@mail.gmail.com>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>	<1453486736-15358-4-git-send-email-dianders@chromium.org>	<56A89838.3040202@rock-chips.com>
 <CAD=FV=XKAzVOLCkFBH1nXeiaKeU4FoFN2CuRNMankA4xsgPsuw@mail.gmail.com>
Message-ID: <56A9D123.7030601@rock-chips.com>

Hi Doug,

On 01/28/2016 03:44 AM, Doug Anderson wrote:
> If it's all the same to you, I'll probably change it back to 525 and
> then increase the periodic FIFO size by 3 DWORDS in the next patch.
> 12 bytes may not be much, but might as well make use of them to
> improve performance / compatibility?
>
> Presumably you're also OK with the next patch in the series: ("usb:
> dwc2: host: Set host_perio_tx_fifo_size to 304 for rk3066")?
I'm OK for change in this patch, but I think we don't need next patch,
pls see the reply in another mail.

Thank,
- Kever



From wxt at rock-chips.com  Thu Jan 28 00:43:29 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Thu, 28 Jan 2016 16:43:29 +0800
Subject: [PATCH v4 0/9] Add the family patches to support for kylin board
Message-ID: <1453970618-4383-1-git-send-email-wxt@rock-chips.com>

Hi all,

This series patches are based on kernel 4.5-rc1 version, also
is based on Heiko's branch.
(kernel/git/mmind/linux-rockchip.git -b for-next & v4.5-clk/fixes)

e.g: boot log..
Linux version 4.5.0-rc1+ (wxt at ubuntu) ....#60 SMP Thu Jan 28 12:54:50 CST 2016

1) The kylin history series patches:

V0:
https://lkml.org/lkml/2015/12/22/168
V1:
https://lkml.org/lkml/2015/12/17/319
V2:
https://lkml.org/lkml/2016/1/7/66
V3:
http://www.spinics.net/lists/arm-kernel/msg474796.html

This series patches are verified on the following github:
https://github.com/Caesar-github/rockchip/tree/kylin/next

This series has 9 patches: (9--->1)
c455bf5 ARM: dts: rockchip: support the spi for rk3036
41bbd5d ASoC: rt5616: add the mclk for the codec driver
6ee2dc0 ASoC: rt5616: trivial: fix the typo
2775e41 ASoC: rt5616: add mclk property for rt5616 document
b4ec10b ARM: dts: rockchip: add mclk for rt5616 on kylin board
8d272a1 ARM: dts: rockchip: add support emac for RK3036
02e501f clk: rockchip: rk3036: fix and add node id for emac clock
b948f42 ARM: dts: rockchip: add hdmi/vop device node for rk3036
fa45230 ARM: dts: rockchip: add the leds control for kylin board
....

This series patches can apply into Heiko for-next branch.
(https://git.kernel.org/cgit/linux/kernel/git/mmind/linux-rockchip.git/log/?h=for-next)
Also, ths ASoC can apply into Mark branch.
---

2) This series patches have the following decriptions:

PATCH[1/9]: Add the leds control on kylin board.
ARM: dts: rockchip: add the leds control for kylin board

PATCH[2/9]: ====> add hdmi/vop display node for rk3036
ARM: dts: rockchip: add hdmi/vop device node for rk3036

This Patch is based on Ykk's Inno HDMI V7 patches. That's verified on
my github.

bd5ed3e ARM: dts: rockchip: add hdmi/vop device node for rk3036
438e7bd FROMLIST: dt-bindings: add document for Innosilicon HDMI on Rockchip platform
((am from https://patchwork.kernel.org/patch/8117611/))
ad0623b FROMLIST: drm/rockchip: hdmi: add Innosilicon HDMI support
((am https://patchwork.kernel.org/patch/8117571/))

PATCH[3/9-4/9]: Add the emac patch

The emac clock is depend APLL, That shoud be incorrect.
We should fix it on the DPLL.

df91ab ARM: dts: rockchip: add support emac for RK3036
d5c2781 clk: rockchip: rk3036: fix and add node id for emac clock

That's verified on kylin board with ubuntu os.

How to test and verify?

You can refer to the following wiki document.
http://rockchip.wikidot.com/kylin

PATCH[5/9-8/9]: Make the codec work on kylin board

41bbd5d ASoC: rt5616: add the mclk for the codec driver
6ee2dc0 ASoC: rt5616: trivial: fix the typo
2775e41 ASoC: rt5616: add mclk property for rt5616 document
b4ec10b ARM: dts: rockchip: add mclk for rt5616 on kylin board

As the previous discussed on https://patchwork.kernel.org/patch/8041001/
Move the mclk into codec driver to enable and disable.

How to test and verify?

You can refer to the following wiki document.
http://rockchip.wikidot.com/kylin

PATCH[9/9]: Make the codec work on kylin board

Add the spi node for rk3036 dts, that's seem not related to the kylin.
Maybe the future will support the spi on kylin board or connect the spi devices.

f0fff43 ARM: dts: rockchip: support the spi for rk3036
---

Thanks your reviewing!

-
Caesar


Changes in v4:
- Add the missing led control on kylin board.
- This patch picks up at this URL.
  (https://patchwork.kernel.org/patch/8129971/)
- solve the lastest conflict, picked up from
  https://patchwork.kernel.org/patch/8040961/.
- fix the commit, pick up from the
  https://patchwork.kernel.org/patch/7976631/.
- The emac parent shouldn't depend on the APLL. instead of DPLL.
- included in the kylin series patches.
- This patch picked up from https://patchwork.kernel.org/patch/7924971/
- Change to solve the conflict based on the Heiko's branch.
- Make the emac parent as the DPLL.
- AS the previous discussed by them, add the mclk for codec.
 (https://patchwork.kernel.org/patch/8041001/)
- Add this patch included in kylin series patches.
- Add this patch included in kylin series patches.
- AS the previous discussed by them, add the mclk for codec.
  (https://patchwork.kernel.org/patch/8041001/)
- Add this patch included in kylin series patches.

Caesar Wang (7):
  ARM: dts: rockchip: add the leds control for kylin board
  ARM: dts: rockchip: add hdmi/vop device node for rk3036
  ARM: dts: rockchip: add mclk for rt5616 on kylin board
  ASoC: rt5616: add mclk property for rt5616 document
  ASoC: rt5616: trivial: fix the typo
  ASoC: rt5616: add the mclk for the codec driver
  ARM: dts: rockchip: support the spi for rk3036

zhengxing (2):
  clk: rockchip: rk3036: fix and add node id for emac clock
  ARM: dts: rockchip: add support emac for RK3036

 Documentation/devicetree/bindings/sound/rt5616.txt |   6 +
 arch/arm/boot/dts/rk3036-evb.dts                   |  23 ++
 arch/arm/boot/dts/rk3036-kylin.dts                 |  52 +++
 arch/arm/boot/dts/rk3036.dtsi                      | 147 ++++++++
 drivers/clk/rockchip/clk-rk3036.c                  |   9 +-
 include/dt-bindings/clock/rk3036-cru.h             |   2 +
 sound/soc/codecs/rt5616.c                          | 410 +++++++++++----------
 7 files changed, 461 insertions(+), 188 deletions(-)

-- 
1.9.1



From wxt at rock-chips.com  Thu Jan 28 00:43:30 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Thu, 28 Jan 2016 16:43:30 +0800
Subject: [PATCH v4 1/9] ARM: dts: rockchip: add the leds control for kylin
 board
In-Reply-To: <1453970618-4383-1-git-send-email-wxt@rock-chips.com>
References: <1453970618-4383-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1453970618-4383-2-git-send-email-wxt@rock-chips.com>

As the kylin schematic drawing, add the needed work led for
kylin board.

Run:
echo 0 > /sys/class/leds/kylin:red:led/brightness
echo 1 > /sys/class/leds/kylin:red:led/brightness

The led can normal on/off on kylin board.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v4:
- Add the missing led control on kylin board.
- This patch picks up at this URL.
  (https://patchwork.kernel.org/patch/8129971/)

 arch/arm/boot/dts/rk3036-kylin.dts | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/arch/arm/boot/dts/rk3036-kylin.dts b/arch/arm/boot/dts/rk3036-kylin.dts
index 190f22c..3332a7f 100644
--- a/arch/arm/boot/dts/rk3036-kylin.dts
+++ b/arch/arm/boot/dts/rk3036-kylin.dts
@@ -46,6 +46,17 @@
 	model = "Rockchip RK3036 KylinBoard";
 	compatible = "rockchip,rk3036-kylin", "rockchip,rk3036";
 
+	leds: gpio-leds {
+		compatible = "gpio-leds";
+
+		work {
+			gpios = <&gpio2 30 GPIO_ACTIVE_HIGH>;
+			label = "kylin:red:led";
+			pinctrl-names = "default";
+			pinctrl-0 = <&led_ctl>;
+		};
+	};
+
 	sdio_pwrseq: sdio-pwrseq {
 		compatible = "mmc-pwrseq-simple";
 		pinctrl-names = "default";
@@ -359,6 +370,12 @@
 };
 
 &pinctrl {
+	leds {
+		led_ctl: led-ctl {
+			rockchip,pins = <2 30 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+
 	pmic {
 		pmic_int: pmic-int {
 			rockchip,pins = <2 2 RK_FUNC_GPIO &pcfg_pull_default>;
-- 
1.9.1



From wxt at rock-chips.com  Thu Jan 28 00:43:31 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Thu, 28 Jan 2016 16:43:31 +0800
Subject: [PATCH v4 2/9] ARM: dts: rockchip: add hdmi/vop device node for rk3036
In-Reply-To: <1453970618-4383-1-git-send-email-wxt@rock-chips.com>
References: <1453970618-4383-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1453970618-4383-3-git-send-email-wxt@rock-chips.com>

This patch adds the needed display info for rk3036 SOCs.

The rk3036 support two overlay plane and one hwc plane,
it supports IOMMU, and its IOMMU same as rk3288's.
Meanwhile, add the inno hdmi for HDMI display.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v4:
- solve the lastest conflict, picked up from
  https://patchwork.kernel.org/patch/8040961/.

 arch/arm/boot/dts/rk3036-kylin.dts | 12 +++++++
 arch/arm/boot/dts/rk3036.dtsi      | 66 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 78 insertions(+)

diff --git a/arch/arm/boot/dts/rk3036-kylin.dts b/arch/arm/boot/dts/rk3036-kylin.dts
index 3332a7f..1037ad6 100644
--- a/arch/arm/boot/dts/rk3036-kylin.dts
+++ b/arch/arm/boot/dts/rk3036-kylin.dts
@@ -116,6 +116,10 @@
 	status = "okay";
 };
 
+&hdmi {
+	status = "okay";
+};
+
 &i2c1 {
 	clock-frequency = <400000>;
 
@@ -369,6 +373,14 @@
 	status = "okay";
 };
 
+&vop {
+	status = "okay";
+};
+
+&vop_mmu {
+	status = "okay";
+};
+
 &pinctrl {
 	leds {
 		led_ctl: led-ctl {
diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
index 7897449..7abe3e2 100644
--- a/arch/arm/boot/dts/rk3036.dtsi
+++ b/arch/arm/boot/dts/rk3036.dtsi
@@ -147,6 +147,42 @@
 		};
 	};
 
+	vop: vop at 10118000 {
+		compatible = "rockchip,rk3036-vop";
+		reg = <0x10118000 0x19c>;
+		interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru ACLK_LCDC>, <&cru SCLK_LCDC>, <&cru HCLK_LCDC>;
+		clock-names = "aclk_vop", "dclk_vop", "hclk_vop";
+		resets = <&cru SRST_LCDC1_A>, <&cru SRST_LCDC1_H>, <&cru SRST_LCDC1_D>;
+		reset-names = "axi", "ahb", "dclk";
+		iommus = <&vop_mmu>;
+
+		status = "disabled";
+
+		vop_out: port {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			vop_out_hdmi: endpoint at 0 {
+				reg = <1>;
+				remote-endpoint = <&hdmi_in_vop>;
+			};
+		};
+	};
+
+	display-subsystem {
+		compatible = "rockchip,display-subsystem";
+		ports = <&vop_out>;
+	};
+
+	vop_mmu: iommu at 10118300 {
+		compatible = "rockchip,iommu";
+		reg = <0x10118300 0x100>;
+		interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-names = "vop_mmu";
+		#iommu-cells = <0>;
+		status = "disabled";
+	};
+
 	gic: interrupt-controller at 10139000 {
 		compatible = "arm,gic-400";
 		interrupt-controller;
@@ -274,6 +310,27 @@
 		status = "disabled";
 	};
 
+	hdmi: hdmi at 20034000 {
+		compatible = "rockchip,rk3036-inno-hdmi";
+		reg = <0x20034000 0x4000>;
+		interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru  PCLK_HDMI>;
+		clock-names = "pclk";
+		rockchip,grf = <&grf>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&hdmi_ctl>;
+		status = "disabled";
+
+		hdmi_in: port {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			hdmi_in_vop: endpoint at 0 {
+				reg = <0>;
+				remote-endpoint = <&vop_out_hdmi>;
+			};
+		};
+	};
+
 	timer: timer at 20044000 {
 		compatible = "rockchip,rk3036-timer", "rockchip,rk3288-timer";
 		reg = <0x20044000 0x20>;
@@ -588,6 +645,15 @@
 			};
 		};
 
+		hdmi {
+			hdmi_ctl: hdmi-ctl {
+				rockchip,pins = <1 8  RK_FUNC_1 &pcfg_pull_none>,
+						<1 9  RK_FUNC_1 &pcfg_pull_none>,
+						<1 10 RK_FUNC_1 &pcfg_pull_none>,
+						<1 11 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
 		uart0 {
 			uart0_xfer: uart0-xfer {
 				rockchip,pins = <0 16 RK_FUNC_1 &pcfg_pull_default>,
-- 
1.9.1



From wxt at rock-chips.com  Thu Jan 28 00:43:32 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Thu, 28 Jan 2016 16:43:32 +0800
Subject: [PATCH v4 3/9] clk: rockchip: rk3036: fix and add node id for emac
 clock
In-Reply-To: <1453970618-4383-1-git-send-email-wxt@rock-chips.com>
References: <1453970618-4383-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1453970618-4383-4-git-send-email-wxt@rock-chips.com>

From: zhengxing <zhengxing at rock-chips.com>

In the emac driver, we need to refer HCLK_MAC since there are
only 3PLLs (APLL/GPLL/DPLL) on the rk3036, most clock are under the
GPLL, and it is unable to provide the accurate rate for mac_ref which
need to 50MHz probability, we should let it under the DPLL and are
able to set the freq which integer multiples of 50MHz, so we add these
emac node for reference.

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v4:
- fix the commit, pick up from the
  https://patchwork.kernel.org/patch/7976631/.
- The emac parent shouldn't depend on the APLL. instead of DPLL.

 drivers/clk/rockchip/clk-rk3036.c      | 9 ++++++---
 include/dt-bindings/clock/rk3036-cru.h | 2 ++
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/drivers/clk/rockchip/clk-rk3036.c b/drivers/clk/rockchip/clk-rk3036.c
index be71a41..701f702 100644
--- a/drivers/clk/rockchip/clk-rk3036.c
+++ b/drivers/clk/rockchip/clk-rk3036.c
@@ -343,8 +343,11 @@ static struct rockchip_clk_branch rk3036_clk_branches[] __initdata = {
 			RK2928_CLKSEL_CON(16), 0, 2, MFLAGS, 2, 5, DFLAGS,
 			RK2928_CLKGATE_CON(10), 5, GFLAGS),
 
-	COMPOSITE_NOGATE(0, "mac_pll_src", mux_pll_src_3plls_p, 0,
-			RK2928_CLKSEL_CON(21), 0, 2, MFLAGS, 9, 5, DFLAGS),
+	MUX(SCLK_MACPLL, "mac_pll_pre", mux_pll_src_3plls_p, 0,
+			RK2928_CLKSEL_CON(21), 0, 2, MFLAGS),
+	DIV(0, "mac_pll_src", "mac_pll_pre", 0,
+			RK2928_CLKSEL_CON(21), 9, 5, DFLAGS),
+
 	MUX(SCLK_MACREF, "mac_clk_ref", mux_mac_p, CLK_SET_RATE_PARENT,
 			RK2928_CLKSEL_CON(21), 3, 1, MFLAGS),
 
@@ -404,7 +407,7 @@ static struct rockchip_clk_branch rk3036_clk_branches[] __initdata = {
 	GATE(HCLK_OTG1, "hclk_otg1", "hclk_peri", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(7), 3, GFLAGS),
 	GATE(HCLK_I2S, "hclk_i2s", "hclk_peri", 0, RK2928_CLKGATE_CON(7), 2, GFLAGS),
 	GATE(0, "hclk_sfc", "hclk_peri", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(3), 14, GFLAGS),
-	GATE(0, "hclk_mac", "hclk_peri", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(3), 15, GFLAGS),
+	GATE(HCLK_MAC, "hclk_mac", "hclk_peri", 0, RK2928_CLKGATE_CON(3), 5, GFLAGS),
 
 	/* pclk_peri gates */
 	GATE(0, "pclk_peri_matrix", "pclk_peri", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(4), 1, GFLAGS),
diff --git a/include/dt-bindings/clock/rk3036-cru.h b/include/dt-bindings/clock/rk3036-cru.h
index ebc7a7b..de44109 100644
--- a/include/dt-bindings/clock/rk3036-cru.h
+++ b/include/dt-bindings/clock/rk3036-cru.h
@@ -54,6 +54,7 @@
 #define SCLK_PVTM_VIDEO		125
 #define SCLK_MAC		151
 #define SCLK_MACREF		152
+#define SCLK_MACPLL		153
 #define SCLK_SFC		160
 
 /* aclk gates */
@@ -92,6 +93,7 @@
 #define HCLK_SDMMC		456
 #define HCLK_SDIO		457
 #define HCLK_EMMC		459
+#define HCLK_MAC		460
 #define HCLK_I2S		462
 #define HCLK_LCDC		465
 #define HCLK_ROM		467
-- 
1.9.1



From wxt at rock-chips.com  Thu Jan 28 00:43:33 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Thu, 28 Jan 2016 16:43:33 +0800
Subject: [PATCH v4 4/9] ARM: dts: rockchip: add support emac for RK3036
In-Reply-To: <1453970618-4383-1-git-send-email-wxt@rock-chips.com>
References: <1453970618-4383-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1453970618-4383-5-git-send-email-wxt@rock-chips.com>

From: zhengxing <zhengxing at rock-chips.com>

This patch describe the emac, and we need to let mac clock under
the APLL which is able to provide the accurate 50MHz what mac_ref
need.

This patch makes the emac parent clock is DPLL instead of APLL.
since that will cause some unstable things if the cpufreq is working.

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v4:
- included in the kylin series patches.
- This patch picked up from https://patchwork.kernel.org/patch/7924971/
- Change to solve the conflict based on the Heiko's branch.
- Make the emac parent as the DPLL.

 arch/arm/boot/dts/rk3036-evb.dts   | 23 ++++++++++++++++++++++
 arch/arm/boot/dts/rk3036-kylin.dts | 21 ++++++++++++++++++++
 arch/arm/boot/dts/rk3036.dtsi      | 39 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 83 insertions(+)

diff --git a/arch/arm/boot/dts/rk3036-evb.dts b/arch/arm/boot/dts/rk3036-evb.dts
index 28a0336..d7d3719 100644
--- a/arch/arm/boot/dts/rk3036-evb.dts
+++ b/arch/arm/boot/dts/rk3036-evb.dts
@@ -47,6 +47,17 @@
 	compatible = "rockchip,rk3036-evb", "rockchip,rk3036";
 };
 
+&emac {
+	pinctrl-names = "default";
+	pinctrl-0 = <&emac_xfer>, <&emac_mdio>, <&rmii_rst>;
+	phy = <&phy0>;
+	status = "okay";
+
+	phy0: ethernet-phy at 0 {
+		reg = <0>;
+	};
+};
+
 &i2c1 {
 	status = "okay";
 
@@ -62,3 +73,15 @@
 &uart2 {
 	status = "okay";
 };
+
+&pinctrl {
+	pcfg_output_high: pcfg-output-high {
+		output-high;
+	};
+
+	emac {
+		rmii_rst: rmii-rst {
+			rockchip,pins = <2 22 RK_FUNC_GPIO &pcfg_output_high>;
+		};
+	};
+};
diff --git a/arch/arm/boot/dts/rk3036-kylin.dts b/arch/arm/boot/dts/rk3036-kylin.dts
index 1037ad6..cd45434 100644
--- a/arch/arm/boot/dts/rk3036-kylin.dts
+++ b/arch/arm/boot/dts/rk3036-kylin.dts
@@ -112,6 +112,17 @@
 	status = "okay";
 };
 
+&emac {
+	pinctrl-names = "default";
+	pinctrl-0 = <&emac_xfer>, <&emac_mdio>, <&rmii_rst>;
+	phy = <&phy0>;
+	status = "okay";
+
+	phy0: ethernet-phy at 0 {
+		reg = <0>;
+	};
+};
+
 &emmc {
 	status = "okay";
 };
@@ -382,6 +393,16 @@
 };
 
 &pinctrl {
+	pcfg_output_high: pcfg-output-high {
+		output-high;
+	};
+
+	emac {
+		rmii_rst: rmii-rst {
+			rockchip,pins = <2 22 RK_FUNC_GPIO &pcfg_output_high>;
+		};
+	};
+
 	leds {
 		led_ctl: led-ctl {
 			rockchip,pins = <2 30 RK_FUNC_GPIO &pcfg_pull_none>;
diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
index 7abe3e2..532f232 100644
--- a/arch/arm/boot/dts/rk3036.dtsi
+++ b/arch/arm/boot/dts/rk3036.dtsi
@@ -222,6 +222,27 @@
 		status = "disabled";
 	};
 
+	emac: ethernet at 10200000 {
+		compatible = "rockchip,rk3036-emac", "snps,arc-emac";
+		reg = <0x10200000 0x4000>;
+		interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		rockchip,grf = <&grf>;
+		clocks = <&cru HCLK_MAC>, <&cru SCLK_MACREF>, <&cru SCLK_MAC>;
+		clock-names = "hclk", "macref", "macclk";
+		/*
+		 * Fix the emac parent clock is DPLL instead of APLL.
+		 * since that will cause some unstable things if the cpufreq
+		 * is working. (e.g: the accurate 50MHz what mac_ref need)
+		 */
+		assigned-clocks = <&cru SCLK_MACPLL>;
+		assigned-clock-parents = <&cru PLL_DPLL>;
+		max-speed = <100>;
+		phy-mode = "rmii";
+		status = "disabled";
+	};
+
 	sdmmc: dwmmc at 10214000 {
 		compatible = "rockchip,rk3036-dw-mshc", "rockchip,rk3288-dw-mshc";
 		reg = <0x10214000 0x4000>;
@@ -613,6 +634,24 @@
 			};
 		};
 
+		emac {
+			emac_xfer: emac-xfer {
+				rockchip,pins = <2 10 RK_FUNC_1 &pcfg_pull_default>, /* crs_dvalid */
+						<2 13 RK_FUNC_1 &pcfg_pull_default>, /* tx_en */
+						<2 14 RK_FUNC_1 &pcfg_pull_default>, /* mac_clk */
+						<2 15 RK_FUNC_1 &pcfg_pull_default>, /* rx_err */
+						<2 16 RK_FUNC_1 &pcfg_pull_default>, /* rxd1 */
+						<2 17 RK_FUNC_1 &pcfg_pull_default>, /* rxd0 */
+						<2 18 RK_FUNC_1 &pcfg_pull_default>, /* txd1 */
+						<2 19 RK_FUNC_1 &pcfg_pull_default>; /* txd0 */
+			};
+
+			emac_mdio: emac-mdio {
+				rockchip,pins = <2 12 RK_FUNC_1 &pcfg_pull_default>, /* mac_md */
+						<2 25 RK_FUNC_1 &pcfg_pull_default>; /* mac_mdclk */
+			};
+		};
+
 		i2c0 {
 			i2c0_xfer: i2c0-xfer {
 				rockchip,pins = <0 0 RK_FUNC_1 &pcfg_pull_none>,
-- 
1.9.1



From wxt at rock-chips.com  Thu Jan 28 00:43:34 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Thu, 28 Jan 2016 16:43:34 +0800
Subject: [PATCH v4 5/9] ARM: dts: rockchip: add mclk for rt5616 on kylin board
In-Reply-To: <1453970618-4383-1-git-send-email-wxt@rock-chips.com>
References: <1453970618-4383-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1453970618-4383-6-git-send-email-wxt@rock-chips.com>

The I2S block that provide the output clock as the mclk for rt5616,
That will be the master clock  input.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v4:
- AS the previous discussed by them, add the mclk for codec.
 (https://patchwork.kernel.org/patch/8041001/)

 arch/arm/boot/dts/rk3036-kylin.dts | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/arm/boot/dts/rk3036-kylin.dts b/arch/arm/boot/dts/rk3036-kylin.dts
index cd45434..b0473bc 100644
--- a/arch/arm/boot/dts/rk3036-kylin.dts
+++ b/arch/arm/boot/dts/rk3036-kylin.dts
@@ -328,6 +328,8 @@
 	rt5616: rt5616 at 1b {
 		compatible = "rt5616";
 		reg = <0x1b>;
+		clocks = <&cru SCLK_I2S_OUT>;
+		clock-names = "mclk";
 		#sound-dai-cells = <0>;
 	};
 };
-- 
1.9.1



From wxt at rock-chips.com  Thu Jan 28 00:43:35 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Thu, 28 Jan 2016 16:43:35 +0800
Subject: [PATCH v4 6/9] ASoC: rt5616: add mclk property for rt5616 document
In-Reply-To: <1453970618-4383-1-git-send-email-wxt@rock-chips.com>
References: <1453970618-4383-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1453970618-4383-7-git-send-email-wxt@rock-chips.com>

This patch add the mclk property for the CODEC driver,
since sometime the CODEC driver needs the clock enabled.

The system clock of ALC5616 can be selected from MCLK,
That's also make as the codec master clock provider,

Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v4:
- Add this patch included in kylin series patches.

 Documentation/devicetree/bindings/sound/rt5616.txt | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Documentation/devicetree/bindings/sound/rt5616.txt b/Documentation/devicetree/bindings/sound/rt5616.txt
index efc48c6..e410858 100644
--- a/Documentation/devicetree/bindings/sound/rt5616.txt
+++ b/Documentation/devicetree/bindings/sound/rt5616.txt
@@ -8,6 +8,12 @@ Required properties:
 
 - reg : The I2C address of the device.
 
+Optional properties:
+
+- clocks: The phandle of the master clock to the CODEC.
+
+- clock-names: Should be "mclk".
+
 Pins on the device (for linking into audio routes) for RT5616:
 
   * IN1P
-- 
1.9.1



From wxt at rock-chips.com  Thu Jan 28 00:43:36 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Thu, 28 Jan 2016 16:43:36 +0800
Subject: [PATCH v4 7/9] ASoC: rt5616: trivial: fix the typo
In-Reply-To: <1453970618-4383-1-git-send-email-wxt@rock-chips.com>
References: <1453970618-4383-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1453970618-4383-8-git-send-email-wxt@rock-chips.com>

This patch try to fix the trivial typo.

Run "scripts/checkpatch.pl -f --subjective xxx"
The enable more subjective tests.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v4:
- Add this patch included in kylin series patches.

 sound/soc/codecs/rt5616.c | 376 +++++++++++++++++++++++-----------------------
 1 file changed, 191 insertions(+), 185 deletions(-)

diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c
index 1c10d8e..d4bdf9f 100644
--- a/sound/soc/codecs/rt5616.c
+++ b/sound/soc/codecs/rt5616.c
@@ -53,6 +53,7 @@ static const struct reg_sequence init_list[] = {
 	{RT5616_PR_BASE + 0x21,	0x4040},
 	{RT5616_PR_BASE + 0x23,	0x0004},
 };
+
 #define RT5616_INIT_REG_LEN ARRAY_SIZE(init_list)
 
 static const struct reg_default rt5616_reg[] = {
@@ -162,9 +163,8 @@ static bool rt5616_volatile_register(struct device *dev, unsigned int reg)
 
 	for (i = 0; i < ARRAY_SIZE(rt5616_ranges); i++) {
 		if (reg >= rt5616_ranges[i].range_min &&
-			reg <= rt5616_ranges[i].range_max) {
+		    reg <= rt5616_ranges[i].range_max)
 			return true;
-		}
 	}
 
 	switch (reg) {
@@ -190,9 +190,8 @@ static bool rt5616_readable_register(struct device *dev, unsigned int reg)
 
 	for (i = 0; i < ARRAY_SIZE(rt5616_ranges); i++) {
 		if (reg >= rt5616_ranges[i].range_min &&
-			reg <= rt5616_ranges[i].range_max) {
+		    reg <= rt5616_ranges[i].range_max)
 			return true;
-		}
 	}
 
 	switch (reg) {
@@ -307,45 +306,45 @@ static unsigned int bst_tlv[] = {
 static const struct snd_kcontrol_new rt5616_snd_controls[] = {
 	/* Headphone Output Volume */
 	SOC_DOUBLE("HP Playback Switch", RT5616_HP_VOL,
-		RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
+		   RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
 	SOC_DOUBLE_TLV("HP Playback Volume", RT5616_HP_VOL,
-		RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv),
+		       RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv),
 	/* OUTPUT Control */
 	SOC_DOUBLE("OUT Playback Switch", RT5616_LOUT_CTRL1,
-		RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
+		   RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
 	SOC_DOUBLE("OUT Channel Switch", RT5616_LOUT_CTRL1,
-		RT5616_VOL_L_SFT, RT5616_VOL_R_SFT, 1, 1),
+		   RT5616_VOL_L_SFT, RT5616_VOL_R_SFT, 1, 1),
 	SOC_DOUBLE_TLV("OUT Playback Volume", RT5616_LOUT_CTRL1,
-		RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv),
+		       RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv),
 
 	/* DAC Digital Volume */
 	SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5616_DAC1_DIG_VOL,
-			RT5616_L_VOL_SFT, RT5616_R_VOL_SFT,
-			175, 0, dac_vol_tlv),
+		       RT5616_L_VOL_SFT, RT5616_R_VOL_SFT,
+		       175, 0, dac_vol_tlv),
 	/* IN1/IN2 Control */
 	SOC_SINGLE_TLV("IN1 Boost Volume", RT5616_IN1_IN2,
-		RT5616_BST_SFT1, 8, 0, bst_tlv),
+		       RT5616_BST_SFT1, 8, 0, bst_tlv),
 	SOC_SINGLE_TLV("IN2 Boost Volume", RT5616_IN1_IN2,
-		RT5616_BST_SFT2, 8, 0, bst_tlv),
+		       RT5616_BST_SFT2, 8, 0, bst_tlv),
 	/* INL/INR Volume Control */
 	SOC_DOUBLE_TLV("IN Capture Volume", RT5616_INL1_INR1_VOL,
-			RT5616_INL_VOL_SFT, RT5616_INR_VOL_SFT,
-			31, 1, in_vol_tlv),
+		       RT5616_INL_VOL_SFT, RT5616_INR_VOL_SFT,
+		       31, 1, in_vol_tlv),
 	/* ADC Digital Volume Control */
 	SOC_DOUBLE("ADC Capture Switch", RT5616_ADC_DIG_VOL,
-		RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
+		   RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
 	SOC_DOUBLE_TLV("ADC Capture Volume", RT5616_ADC_DIG_VOL,
-			RT5616_L_VOL_SFT, RT5616_R_VOL_SFT,
-			127, 0, adc_vol_tlv),
+		       RT5616_L_VOL_SFT, RT5616_R_VOL_SFT,
+		       127, 0, adc_vol_tlv),
 
 	/* ADC Boost Volume Control */
 	SOC_DOUBLE_TLV("ADC Boost Volume", RT5616_ADC_BST_VOL,
-			RT5616_ADC_L_BST_SFT, RT5616_ADC_R_BST_SFT,
-			3, 0, adc_bst_tlv),
+		       RT5616_ADC_L_BST_SFT, RT5616_ADC_R_BST_SFT,
+		       3, 0, adc_bst_tlv),
 };
 
 static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
-			 struct snd_soc_dapm_widget *sink)
+			       struct snd_soc_dapm_widget *sink)
 {
 	unsigned int val;
 
@@ -462,20 +461,20 @@ static const struct snd_kcontrol_new rt5616_lout_mix[] = {
 };
 
 static int rt5616_adc_event(struct snd_soc_dapm_widget *w,
-	struct snd_kcontrol *kcontrol, int event)
+			    struct snd_kcontrol *kcontrol, int event)
 {
 	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
 		snd_soc_update_bits(codec, RT5616_ADC_DIG_VOL,
-				RT5616_L_MUTE | RT5616_R_MUTE, 0);
+				    RT5616_L_MUTE | RT5616_R_MUTE, 0);
 		break;
 
 	case SND_SOC_DAPM_POST_PMD:
 		snd_soc_update_bits(codec, RT5616_ADC_DIG_VOL,
-				RT5616_L_MUTE | RT5616_R_MUTE,
-				RT5616_L_MUTE | RT5616_R_MUTE);
+				    RT5616_L_MUTE | RT5616_R_MUTE,
+				    RT5616_L_MUTE | RT5616_R_MUTE);
 		break;
 
 	default:
@@ -486,7 +485,7 @@ static int rt5616_adc_event(struct snd_soc_dapm_widget *w,
 }
 
 static int rt5616_charge_pump_event(struct snd_soc_dapm_widget *w,
-			   struct snd_kcontrol *kcontrol, int event)
+				    struct snd_kcontrol *kcontrol, int event)
 {
 	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 
@@ -494,54 +493,55 @@ static int rt5616_charge_pump_event(struct snd_soc_dapm_widget *w,
 	case SND_SOC_DAPM_POST_PMU:
 		/* depop parameters */
 		snd_soc_update_bits(codec, RT5616_DEPOP_M2,
-			RT5616_DEPOP_MASK, RT5616_DEPOP_MAN);
+				    RT5616_DEPOP_MASK, RT5616_DEPOP_MAN);
 		snd_soc_update_bits(codec, RT5616_DEPOP_M1,
-			RT5616_HP_CP_MASK | RT5616_HP_SG_MASK |
-			RT5616_HP_CB_MASK, RT5616_HP_CP_PU |
-			RT5616_HP_SG_DIS | RT5616_HP_CB_PU);
+				    RT5616_HP_CP_MASK | RT5616_HP_SG_MASK |
+				    RT5616_HP_CB_MASK, RT5616_HP_CP_PU |
+				    RT5616_HP_SG_DIS | RT5616_HP_CB_PU);
 		snd_soc_write(codec, RT5616_PR_BASE +
-			RT5616_HP_DCC_INT1, 0x9f00);
+			      RT5616_HP_DCC_INT1, 0x9f00);
 		/* headphone amp power on */
 		snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
-			RT5616_PWR_FV1 | RT5616_PWR_FV2, 0);
+				    RT5616_PWR_FV1 | RT5616_PWR_FV2, 0);
 		snd_soc_update_bits(codec, RT5616_PWR_VOL,
-			RT5616_PWR_HV_L | RT5616_PWR_HV_R,
-			RT5616_PWR_HV_L | RT5616_PWR_HV_R);
+				    RT5616_PWR_HV_L | RT5616_PWR_HV_R,
+				    RT5616_PWR_HV_L | RT5616_PWR_HV_R);
 		snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
-			RT5616_PWR_HP_L | RT5616_PWR_HP_R |
-			RT5616_PWR_HA, RT5616_PWR_HP_L |
-			RT5616_PWR_HP_R | RT5616_PWR_HA);
+				    RT5616_PWR_HP_L | RT5616_PWR_HP_R |
+				    RT5616_PWR_HA, RT5616_PWR_HP_L |
+				    RT5616_PWR_HP_R | RT5616_PWR_HA);
 		msleep(50);
 		snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
-			RT5616_PWR_FV1 | RT5616_PWR_FV2,
-			RT5616_PWR_FV1 | RT5616_PWR_FV2);
+				    RT5616_PWR_FV1 | RT5616_PWR_FV2,
+				    RT5616_PWR_FV1 | RT5616_PWR_FV2);
 
 		snd_soc_update_bits(codec, RT5616_CHARGE_PUMP,
-			RT5616_PM_HP_MASK, RT5616_PM_HP_HV);
+				    RT5616_PM_HP_MASK, RT5616_PM_HP_HV);
 		snd_soc_update_bits(codec, RT5616_PR_BASE +
-			RT5616_CHOP_DAC_ADC, 0x0200, 0x0200);
+				    RT5616_CHOP_DAC_ADC, 0x0200, 0x0200);
 		snd_soc_update_bits(codec, RT5616_DEPOP_M1,
-			RT5616_HP_CO_MASK | RT5616_HP_SG_MASK,
-			RT5616_HP_CO_EN | RT5616_HP_SG_EN);
+				    RT5616_HP_CO_MASK | RT5616_HP_SG_MASK,
+				    RT5616_HP_CO_EN | RT5616_HP_SG_EN);
 		break;
 	case SND_SOC_DAPM_PRE_PMD:
 		snd_soc_update_bits(codec, RT5616_PR_BASE +
-			RT5616_CHOP_DAC_ADC, 0x0200, 0x0);
+				    RT5616_CHOP_DAC_ADC, 0x0200, 0x0);
 		snd_soc_update_bits(codec, RT5616_DEPOP_M1,
-			RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK |
-			RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS |
-			RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS);
+				    RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK |
+				    RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS |
+				    RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS);
 		/* headphone amp power down */
 		snd_soc_update_bits(codec, RT5616_DEPOP_M1,
-			RT5616_SMT_TRIG_MASK | RT5616_HP_CD_PD_MASK |
-			RT5616_HP_CO_MASK | RT5616_HP_CP_MASK |
-			RT5616_HP_SG_MASK | RT5616_HP_CB_MASK,
-			RT5616_SMT_TRIG_DIS | RT5616_HP_CD_PD_EN |
-			RT5616_HP_CO_DIS | RT5616_HP_CP_PD |
-			RT5616_HP_SG_EN | RT5616_HP_CB_PD);
+				    RT5616_SMT_TRIG_MASK |
+				    RT5616_HP_CD_PD_MASK | RT5616_HP_CO_MASK |
+				    RT5616_HP_CP_MASK | RT5616_HP_SG_MASK |
+				    RT5616_HP_CB_MASK,
+				    RT5616_SMT_TRIG_DIS | RT5616_HP_CD_PD_EN |
+				    RT5616_HP_CO_DIS | RT5616_HP_CP_PD |
+				    RT5616_HP_SG_EN | RT5616_HP_CB_PD);
 		snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
-			RT5616_PWR_HP_L | RT5616_PWR_HP_R |
-			RT5616_PWR_HA, 0);
+				    RT5616_PWR_HP_L | RT5616_PWR_HP_R |
+				    RT5616_PWR_HA, 0);
 		break;
 	default:
 		return 0;
@@ -551,7 +551,7 @@ static int rt5616_charge_pump_event(struct snd_soc_dapm_widget *w,
 }
 
 static int rt5616_hp_event(struct snd_soc_dapm_widget *w,
-	struct snd_kcontrol *kcontrol, int event)
+			   struct snd_kcontrol *kcontrol, int event)
 {
 	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 
@@ -559,57 +559,57 @@ static int rt5616_hp_event(struct snd_soc_dapm_widget *w,
 	case SND_SOC_DAPM_POST_PMU:
 		/* headphone unmute sequence */
 		snd_soc_update_bits(codec, RT5616_DEPOP_M3,
-			RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK |
-			RT5616_CP_FQ3_MASK,
-			(RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ1_SFT) |
-			(RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT) |
-			(RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ3_SFT));
+				    RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK |
+				    RT5616_CP_FQ3_MASK,
+				    RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ1_SFT |
+				    RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT |
+				    RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ3_SFT);
 		snd_soc_write(codec, RT5616_PR_BASE +
-			RT5616_MAMP_INT_REG2, 0xfc00);
+			      RT5616_MAMP_INT_REG2, 0xfc00);
 		snd_soc_update_bits(codec, RT5616_DEPOP_M1,
-			RT5616_SMT_TRIG_MASK, RT5616_SMT_TRIG_EN);
+				    RT5616_SMT_TRIG_MASK, RT5616_SMT_TRIG_EN);
 		snd_soc_update_bits(codec, RT5616_DEPOP_M1,
-			RT5616_RSTN_MASK, RT5616_RSTN_EN);
+				    RT5616_RSTN_MASK, RT5616_RSTN_EN);
 		snd_soc_update_bits(codec, RT5616_DEPOP_M1,
-			RT5616_RSTN_MASK | RT5616_HP_L_SMT_MASK |
-			RT5616_HP_R_SMT_MASK, RT5616_RSTN_DIS |
-			RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN);
+				    RT5616_RSTN_MASK | RT5616_HP_L_SMT_MASK |
+				    RT5616_HP_R_SMT_MASK, RT5616_RSTN_DIS |
+				    RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN);
 		snd_soc_update_bits(codec, RT5616_HP_VOL,
-			RT5616_L_MUTE | RT5616_R_MUTE, 0);
+				    RT5616_L_MUTE | RT5616_R_MUTE, 0);
 		msleep(100);
 		snd_soc_update_bits(codec, RT5616_DEPOP_M1,
-			RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK |
-			RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS |
-			RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS);
+				    RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK |
+				    RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS |
+				    RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS);
 		msleep(20);
 		snd_soc_update_bits(codec, RT5616_HP_CALIB_AMP_DET,
-			RT5616_HPD_PS_MASK, RT5616_HPD_PS_EN);
+				    RT5616_HPD_PS_MASK, RT5616_HPD_PS_EN);
 		break;
 
 	case SND_SOC_DAPM_PRE_PMD:
 		/* headphone mute sequence */
 		snd_soc_update_bits(codec, RT5616_DEPOP_M3,
-			RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK |
-			RT5616_CP_FQ3_MASK,
-			(RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ1_SFT) |
-			(RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT) |
-			(RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ3_SFT));
+				    RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK |
+				    RT5616_CP_FQ3_MASK,
+				    RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ1_SFT |
+				    RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT |
+				    RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ3_SFT);
 		snd_soc_write(codec, RT5616_PR_BASE +
-			RT5616_MAMP_INT_REG2, 0xfc00);
+			      RT5616_MAMP_INT_REG2, 0xfc00);
 		snd_soc_update_bits(codec, RT5616_DEPOP_M1,
-			RT5616_HP_SG_MASK, RT5616_HP_SG_EN);
+				    RT5616_HP_SG_MASK, RT5616_HP_SG_EN);
 		snd_soc_update_bits(codec, RT5616_DEPOP_M1,
-			RT5616_RSTP_MASK, RT5616_RSTP_EN);
+				    RT5616_RSTP_MASK, RT5616_RSTP_EN);
 		snd_soc_update_bits(codec, RT5616_DEPOP_M1,
-			RT5616_RSTP_MASK | RT5616_HP_L_SMT_MASK |
-			RT5616_HP_R_SMT_MASK, RT5616_RSTP_DIS |
-			RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN);
+				    RT5616_RSTP_MASK | RT5616_HP_L_SMT_MASK |
+				    RT5616_HP_R_SMT_MASK, RT5616_RSTP_DIS |
+				    RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN);
 		snd_soc_update_bits(codec, RT5616_HP_CALIB_AMP_DET,
-			RT5616_HPD_PS_MASK, RT5616_HPD_PS_DIS);
+				    RT5616_HPD_PS_MASK, RT5616_HPD_PS_DIS);
 		msleep(90);
 		snd_soc_update_bits(codec, RT5616_HP_VOL,
-			RT5616_L_MUTE | RT5616_R_MUTE,
-			RT5616_L_MUTE | RT5616_R_MUTE);
+				    RT5616_L_MUTE | RT5616_R_MUTE,
+				    RT5616_L_MUTE | RT5616_R_MUTE);
 		msleep(30);
 		break;
 
@@ -621,24 +621,24 @@ static int rt5616_hp_event(struct snd_soc_dapm_widget *w,
 }
 
 static int rt5616_lout_event(struct snd_soc_dapm_widget *w,
-	struct snd_kcontrol *kcontrol, int event)
+			     struct snd_kcontrol *kcontrol, int event)
 {
 	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
 		snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
-			RT5616_PWR_LM, RT5616_PWR_LM);
+				    RT5616_PWR_LM, RT5616_PWR_LM);
 		snd_soc_update_bits(codec, RT5616_LOUT_CTRL1,
-			RT5616_L_MUTE | RT5616_R_MUTE, 0);
+				    RT5616_L_MUTE | RT5616_R_MUTE, 0);
 		break;
 
 	case SND_SOC_DAPM_PRE_PMD:
 		snd_soc_update_bits(codec, RT5616_LOUT_CTRL1,
-			RT5616_L_MUTE | RT5616_R_MUTE,
-			RT5616_L_MUTE | RT5616_R_MUTE);
+				    RT5616_L_MUTE | RT5616_R_MUTE,
+				    RT5616_L_MUTE | RT5616_R_MUTE);
 		snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
-			RT5616_PWR_LM, 0);
+				    RT5616_PWR_LM, 0);
 		break;
 
 	default:
@@ -649,19 +649,19 @@ static int rt5616_lout_event(struct snd_soc_dapm_widget *w,
 }
 
 static int rt5616_bst1_event(struct snd_soc_dapm_widget *w,
-	struct snd_kcontrol *kcontrol, int event)
+			     struct snd_kcontrol *kcontrol, int event)
 {
 	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
 		snd_soc_update_bits(codec, RT5616_PWR_ANLG2,
-			RT5616_PWR_BST1_OP2, RT5616_PWR_BST1_OP2);
+				    RT5616_PWR_BST1_OP2, RT5616_PWR_BST1_OP2);
 		break;
 
 	case SND_SOC_DAPM_PRE_PMD:
 		snd_soc_update_bits(codec, RT5616_PWR_ANLG2,
-			RT5616_PWR_BST1_OP2, 0);
+				    RT5616_PWR_BST1_OP2, 0);
 		break;
 
 	default:
@@ -672,19 +672,19 @@ static int rt5616_bst1_event(struct snd_soc_dapm_widget *w,
 }
 
 static int rt5616_bst2_event(struct snd_soc_dapm_widget *w,
-	struct snd_kcontrol *kcontrol, int event)
+			     struct snd_kcontrol *kcontrol, int event)
 {
 	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
 		snd_soc_update_bits(codec, RT5616_PWR_ANLG2,
-			RT5616_PWR_BST2_OP2, RT5616_PWR_BST2_OP2);
+				    RT5616_PWR_BST2_OP2, RT5616_PWR_BST2_OP2);
 		break;
 
 	case SND_SOC_DAPM_PRE_PMD:
 		snd_soc_update_bits(codec, RT5616_PWR_ANLG2,
-			RT5616_PWR_BST2_OP2, 0);
+				    RT5616_PWR_BST2_OP2, 0);
 		break;
 
 	default:
@@ -696,13 +696,13 @@ static int rt5616_bst2_event(struct snd_soc_dapm_widget *w,
 
 static const struct snd_soc_dapm_widget rt5616_dapm_widgets[] = {
 	SND_SOC_DAPM_SUPPLY("PLL1", RT5616_PWR_ANLG2,
-			RT5616_PWR_PLL_BIT, 0, NULL, 0),
+			    RT5616_PWR_PLL_BIT, 0, NULL, 0),
 	/* Input Side */
 	/* micbias */
 	SND_SOC_DAPM_SUPPLY("LDO", RT5616_PWR_ANLG1,
-			RT5616_PWR_LDO_BIT, 0, NULL, 0),
+			    RT5616_PWR_LDO_BIT, 0, NULL, 0),
 	SND_SOC_DAPM_SUPPLY("micbias1", RT5616_PWR_ANLG2,
-			RT5616_PWR_MB1_BIT, 0, NULL, 0),
+			    RT5616_PWR_MB1_BIT, 0, NULL, 0),
 
 	/* Input Lines */
 	SND_SOC_DAPM_INPUT("MIC1"),
@@ -714,45 +714,47 @@ static const struct snd_soc_dapm_widget rt5616_dapm_widgets[] = {
 
 	/* Boost */
 	SND_SOC_DAPM_PGA_E("BST1", RT5616_PWR_ANLG2,
-		RT5616_PWR_BST1_BIT, 0, NULL, 0, rt5616_bst1_event,
-		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+			   RT5616_PWR_BST1_BIT, 0, NULL, 0, rt5616_bst1_event,
+			   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
 	SND_SOC_DAPM_PGA_E("BST2", RT5616_PWR_ANLG2,
-		RT5616_PWR_BST2_BIT, 0, NULL, 0, rt5616_bst2_event,
-		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+			   RT5616_PWR_BST2_BIT, 0, NULL, 0, rt5616_bst2_event,
+			   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
 	/* Input Volume */
 	SND_SOC_DAPM_PGA("INL1 VOL", RT5616_PWR_VOL,
-		RT5616_PWR_IN1_L_BIT, 0, NULL, 0),
+			 RT5616_PWR_IN1_L_BIT, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("INR1 VOL", RT5616_PWR_VOL,
-		RT5616_PWR_IN1_R_BIT, 0, NULL, 0),
+			 RT5616_PWR_IN1_R_BIT, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("INL2 VOL", RT5616_PWR_VOL,
-		RT5616_PWR_IN2_L_BIT, 0, NULL, 0),
+			 RT5616_PWR_IN2_L_BIT, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("INR2 VOL", RT5616_PWR_VOL,
-		RT5616_PWR_IN2_R_BIT, 0, NULL, 0),
+			 RT5616_PWR_IN2_R_BIT, 0, NULL, 0),
 
 	/* REC Mixer */
 	SND_SOC_DAPM_MIXER("RECMIXL", RT5616_PWR_MIXER, RT5616_PWR_RM_L_BIT, 0,
-			rt5616_rec_l_mix, ARRAY_SIZE(rt5616_rec_l_mix)),
+			   rt5616_rec_l_mix, ARRAY_SIZE(rt5616_rec_l_mix)),
 	SND_SOC_DAPM_MIXER("RECMIXR", RT5616_PWR_MIXER, RT5616_PWR_RM_R_BIT, 0,
-			rt5616_rec_r_mix, ARRAY_SIZE(rt5616_rec_r_mix)),
+			   rt5616_rec_r_mix, ARRAY_SIZE(rt5616_rec_r_mix)),
 	/* ADCs */
 	SND_SOC_DAPM_ADC_E("ADC L", NULL, RT5616_PWR_DIG1,
-		RT5616_PWR_ADC_L_BIT, 0, rt5616_adc_event,
-		SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
+			   RT5616_PWR_ADC_L_BIT, 0, rt5616_adc_event,
+			   SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
 	SND_SOC_DAPM_ADC_E("ADC R", NULL, RT5616_PWR_DIG1,
-		RT5616_PWR_ADC_R_BIT, 0, rt5616_adc_event,
-		SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
+			   RT5616_PWR_ADC_R_BIT, 0, rt5616_adc_event,
+			   SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
 
 	/* ADC Mixer */
 	SND_SOC_DAPM_SUPPLY("stereo1 filter", RT5616_PWR_DIG2,
-		RT5616_PWR_ADC_STO1_F_BIT, 0, NULL, 0),
+			    RT5616_PWR_ADC_STO1_F_BIT, 0, NULL, 0),
 	SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0,
-		rt5616_sto1_adc_l_mix, ARRAY_SIZE(rt5616_sto1_adc_l_mix)),
+			   rt5616_sto1_adc_l_mix,
+			   ARRAY_SIZE(rt5616_sto1_adc_l_mix)),
 	SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", SND_SOC_NOPM, 0, 0,
-		rt5616_sto1_adc_r_mix, ARRAY_SIZE(rt5616_sto1_adc_r_mix)),
+			   rt5616_sto1_adc_r_mix,
+			   ARRAY_SIZE(rt5616_sto1_adc_r_mix)),
 
 	/* Digital Interface */
 	SND_SOC_DAPM_SUPPLY("I2S1", RT5616_PWR_DIG1,
-		RT5616_PWR_I2S1_BIT, 0, NULL, 0),
+			    RT5616_PWR_I2S1_BIT, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("IF1 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -770,68 +772,70 @@ static const struct snd_soc_dapm_widget rt5616_dapm_widgets[] = {
 	/* Output Side */
 	/* DAC mixer before sound effect  */
 	SND_SOC_DAPM_MIXER("DAC MIXL", SND_SOC_NOPM, 0, 0,
-		rt5616_dac_l_mix, ARRAY_SIZE(rt5616_dac_l_mix)),
+			   rt5616_dac_l_mix, ARRAY_SIZE(rt5616_dac_l_mix)),
 	SND_SOC_DAPM_MIXER("DAC MIXR", SND_SOC_NOPM, 0, 0,
-		rt5616_dac_r_mix, ARRAY_SIZE(rt5616_dac_r_mix)),
+			   rt5616_dac_r_mix, ARRAY_SIZE(rt5616_dac_r_mix)),
 
 	SND_SOC_DAPM_SUPPLY("Stero1 DAC Power", RT5616_PWR_DIG2,
-			RT5616_PWR_DAC_STO1_F_BIT, 0, NULL, 0),
+			    RT5616_PWR_DAC_STO1_F_BIT, 0, NULL, 0),
 
 	/* DAC Mixer */
 	SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0,
-		rt5616_sto_dac_l_mix, ARRAY_SIZE(rt5616_sto_dac_l_mix)),
+			   rt5616_sto_dac_l_mix,
+			   ARRAY_SIZE(rt5616_sto_dac_l_mix)),
 	SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
-		rt5616_sto_dac_r_mix, ARRAY_SIZE(rt5616_sto_dac_r_mix)),
+			   rt5616_sto_dac_r_mix,
+			   ARRAY_SIZE(rt5616_sto_dac_r_mix)),
 
 	/* DACs */
 	SND_SOC_DAPM_DAC("DAC L1", NULL, RT5616_PWR_DIG1,
-			RT5616_PWR_DAC_L1_BIT, 0),
+			 RT5616_PWR_DAC_L1_BIT, 0),
 	SND_SOC_DAPM_DAC("DAC R1", NULL, RT5616_PWR_DIG1,
-			RT5616_PWR_DAC_R1_BIT, 0),
+			 RT5616_PWR_DAC_R1_BIT, 0),
 	/* OUT Mixer */
 	SND_SOC_DAPM_MIXER("OUT MIXL", RT5616_PWR_MIXER, RT5616_PWR_OM_L_BIT,
-		0, rt5616_out_l_mix, ARRAY_SIZE(rt5616_out_l_mix)),
+			   0, rt5616_out_l_mix, ARRAY_SIZE(rt5616_out_l_mix)),
 	SND_SOC_DAPM_MIXER("OUT MIXR", RT5616_PWR_MIXER, RT5616_PWR_OM_R_BIT,
-		0, rt5616_out_r_mix, ARRAY_SIZE(rt5616_out_r_mix)),
+			   0, rt5616_out_r_mix, ARRAY_SIZE(rt5616_out_r_mix)),
 	/* Output Volume */
 	SND_SOC_DAPM_PGA("OUTVOL L", RT5616_PWR_VOL,
-		RT5616_PWR_OV_L_BIT, 0, NULL, 0),
+			 RT5616_PWR_OV_L_BIT, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("OUTVOL R", RT5616_PWR_VOL,
-		RT5616_PWR_OV_R_BIT, 0, NULL, 0),
+			 RT5616_PWR_OV_R_BIT, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("HPOVOL L", RT5616_PWR_VOL,
-		RT5616_PWR_HV_L_BIT, 0, NULL, 0),
+			 RT5616_PWR_HV_L_BIT, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("HPOVOL R", RT5616_PWR_VOL,
-		RT5616_PWR_HV_R_BIT, 0, NULL, 0),
+			 RT5616_PWR_HV_R_BIT, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("DAC 1", SND_SOC_NOPM,
-		0, 0, NULL, 0),
+			 0, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("DAC 2", SND_SOC_NOPM,
-		0, 0, NULL, 0),
+			 0, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("HPOVOL", SND_SOC_NOPM,
-		0, 0, NULL, 0),
+			 0, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("INL1", RT5616_PWR_VOL,
-		RT5616_PWR_IN1_L_BIT, 0, NULL, 0),
+			 RT5616_PWR_IN1_L_BIT, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("INR1", RT5616_PWR_VOL,
-		RT5616_PWR_IN1_R_BIT, 0, NULL, 0),
+			 RT5616_PWR_IN1_R_BIT, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("INL2", RT5616_PWR_VOL,
-		RT5616_PWR_IN2_L_BIT, 0, NULL, 0),
+			 RT5616_PWR_IN2_L_BIT, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("INR2", RT5616_PWR_VOL,
-		RT5616_PWR_IN2_R_BIT, 0, NULL, 0),
+			 RT5616_PWR_IN2_R_BIT, 0, NULL, 0),
 	/* HPO/LOUT/Mono Mixer */
 	SND_SOC_DAPM_MIXER("HPO MIX", SND_SOC_NOPM, 0, 0,
-		rt5616_hpo_mix, ARRAY_SIZE(rt5616_hpo_mix)),
+			   rt5616_hpo_mix, ARRAY_SIZE(rt5616_hpo_mix)),
 	SND_SOC_DAPM_MIXER("LOUT MIX", SND_SOC_NOPM, 0, 0,
-		rt5616_lout_mix, ARRAY_SIZE(rt5616_lout_mix)),
+			   rt5616_lout_mix, ARRAY_SIZE(rt5616_lout_mix)),
 
 	SND_SOC_DAPM_PGA_S("HP amp", 1, SND_SOC_NOPM, 0, 0,
-		rt5616_hp_event, SND_SOC_DAPM_PRE_PMD |
-		SND_SOC_DAPM_POST_PMU),
+			   rt5616_hp_event, SND_SOC_DAPM_PRE_PMD |
+			   SND_SOC_DAPM_POST_PMU),
 	SND_SOC_DAPM_PGA_S("LOUT amp", 1, SND_SOC_NOPM, 0, 0,
-		rt5616_lout_event, SND_SOC_DAPM_PRE_PMD |
-		SND_SOC_DAPM_POST_PMU),
+			   rt5616_lout_event, SND_SOC_DAPM_PRE_PMD |
+			   SND_SOC_DAPM_POST_PMU),
 
 	SND_SOC_DAPM_SUPPLY_S("Charge Pump", 1, SND_SOC_NOPM, 0, 0,
-		rt5616_charge_pump_event, SND_SOC_DAPM_POST_PMU |
-		SND_SOC_DAPM_PRE_PMD),
+			      rt5616_charge_pump_event, SND_SOC_DAPM_POST_PMU |
+			      SND_SOC_DAPM_PRE_PMD),
 
 	/* Output Lines */
 	SND_SOC_DAPM_OUTPUT("HPOL"),
@@ -950,7 +954,8 @@ static const struct snd_soc_dapm_route rt5616_dapm_routes[] = {
 };
 
 static int rt5616_hw_params(struct snd_pcm_substream *substream,
-	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+			    struct snd_pcm_hw_params *params,
+			    struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_codec *codec = rtd->codec;
@@ -977,7 +982,7 @@ static int rt5616_hw_params(struct snd_pcm_substream *substream,
 	dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n",
 		rt5616->bclk[dai->id], rt5616->lrck[dai->id]);
 	dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
-				bclk_ms, pre_div, dai->id);
+		bclk_ms, pre_div, dai->id);
 
 	switch (params_format(params)) {
 	case SNDRV_PCM_FORMAT_S16_LE:
@@ -998,10 +1003,9 @@ static int rt5616_hw_params(struct snd_pcm_substream *substream,
 	mask_clk = RT5616_I2S_PD1_MASK;
 	val_clk = pre_div << RT5616_I2S_PD1_SFT;
 	snd_soc_update_bits(codec, RT5616_I2S1_SDP,
-		RT5616_I2S_DL_MASK, val_len);
+			    RT5616_I2S_DL_MASK, val_len);
 	snd_soc_update_bits(codec, RT5616_ADDA_CLK1, mask_clk, val_clk);
 
-
 	return 0;
 }
 
@@ -1050,15 +1054,14 @@ static int rt5616_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 	}
 
 	snd_soc_update_bits(codec, RT5616_I2S1_SDP,
-		RT5616_I2S_MS_MASK | RT5616_I2S_BP_MASK |
-		RT5616_I2S_DF_MASK, reg_val);
-
+			    RT5616_I2S_MS_MASK | RT5616_I2S_BP_MASK |
+			    RT5616_I2S_DF_MASK, reg_val);
 
 	return 0;
 }
 
 static int rt5616_set_dai_sysclk(struct snd_soc_dai *dai,
-		int clk_id, unsigned int freq, int dir)
+				 int clk_id, unsigned int freq, int dir)
 {
 	struct snd_soc_codec *codec = dai->codec;
 	struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
@@ -1078,8 +1081,9 @@ static int rt5616_set_dai_sysclk(struct snd_soc_dai *dai,
 		dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
 		return -EINVAL;
 	}
+
 	snd_soc_update_bits(codec, RT5616_GLB_CLK,
-		RT5616_SCLK_SRC_MASK, reg_val);
+			    RT5616_SCLK_SRC_MASK, reg_val);
 	rt5616->sysclk = freq;
 	rt5616->sysclk_src = clk_id;
 
@@ -1089,7 +1093,7 @@ static int rt5616_set_dai_sysclk(struct snd_soc_dai *dai,
 }
 
 static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
-			unsigned int freq_in, unsigned int freq_out)
+			      unsigned int freq_in, unsigned int freq_out)
 {
 	struct snd_soc_codec *codec = dai->codec;
 	struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
@@ -1106,19 +1110,22 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
 		rt5616->pll_in = 0;
 		rt5616->pll_out = 0;
 		snd_soc_update_bits(codec, RT5616_GLB_CLK,
-			RT5616_SCLK_SRC_MASK, RT5616_SCLK_SRC_MCLK);
+				    RT5616_SCLK_SRC_MASK,
+				    RT5616_SCLK_SRC_MCLK);
 		return 0;
 	}
 
 	switch (source) {
 	case RT5616_PLL1_S_MCLK:
 		snd_soc_update_bits(codec, RT5616_GLB_CLK,
-			RT5616_PLL1_SRC_MASK, RT5616_PLL1_SRC_MCLK);
+				    RT5616_PLL1_SRC_MASK,
+				    RT5616_PLL1_SRC_MCLK);
 		break;
 	case RT5616_PLL1_S_BCLK1:
 	case RT5616_PLL1_S_BCLK2:
 		snd_soc_update_bits(codec, RT5616_GLB_CLK,
-			RT5616_PLL1_SRC_MASK, RT5616_PLL1_SRC_BCLK1);
+				    RT5616_PLL1_SRC_MASK,
+				    RT5616_PLL1_SRC_BCLK1);
 		break;
 	default:
 		dev_err(codec->dev, "Unknown PLL source %d\n", source);
@@ -1136,10 +1143,11 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
 		pll_code.n_code, pll_code.k_code);
 
 	snd_soc_write(codec, RT5616_PLL_CTRL1,
-		pll_code.n_code << RT5616_PLL_N_SFT | pll_code.k_code);
+		      pll_code.n_code << RT5616_PLL_N_SFT | pll_code.k_code);
 	snd_soc_write(codec, RT5616_PLL_CTRL2,
-		(pll_code.m_bp ? 0 : pll_code.m_code) << RT5616_PLL_M_SFT |
-		pll_code.m_bp << RT5616_PLL_M_BP_SFT);
+		      (pll_code.m_bp ? 0 : pll_code.m_code) <<
+		      RT5616_PLL_M_SFT |
+		      pll_code.m_bp << RT5616_PLL_M_BP_SFT);
 
 	rt5616->pll_in = freq_in;
 	rt5616->pll_out = freq_out;
@@ -1149,22 +1157,23 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
 }
 
 static int rt5616_set_bias_level(struct snd_soc_codec *codec,
-			enum snd_soc_bias_level level)
+				 enum snd_soc_bias_level level)
 {
 	switch (level) {
 	case SND_SOC_BIAS_STANDBY:
 		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
-				RT5616_PWR_VREF1 | RT5616_PWR_MB |
-				RT5616_PWR_BG | RT5616_PWR_VREF2,
-				RT5616_PWR_VREF1 | RT5616_PWR_MB |
-				RT5616_PWR_BG | RT5616_PWR_VREF2);
+					    RT5616_PWR_VREF1 | RT5616_PWR_MB |
+					    RT5616_PWR_BG | RT5616_PWR_VREF2,
+					    RT5616_PWR_VREF1 | RT5616_PWR_MB |
+					    RT5616_PWR_BG | RT5616_PWR_VREF2);
 			mdelay(10);
 			snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
-				RT5616_PWR_FV1 | RT5616_PWR_FV2,
-				RT5616_PWR_FV1 | RT5616_PWR_FV2);
+					    RT5616_PWR_FV1 | RT5616_PWR_FV2,
+					    RT5616_PWR_FV1 | RT5616_PWR_FV2);
 			snd_soc_update_bits(codec, RT5616_D_MISC,
-				RT5616_D_GATE_EN, RT5616_D_GATE_EN);
+					    RT5616_D_GATE_EN,
+					    RT5616_D_GATE_EN);
 		}
 		break;
 
@@ -1222,7 +1231,6 @@ static int rt5616_resume(struct snd_soc_codec *codec)
 #define RT5616_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
 			SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
 
-
 struct snd_soc_dai_ops rt5616_aif_dai_ops = {
 	.hw_params = rt5616_hw_params,
 	.set_fmt = rt5616_set_dai_fmt,
@@ -1296,15 +1304,15 @@ MODULE_DEVICE_TABLE(of, rt5616_of_match);
 #endif
 
 static int rt5616_i2c_probe(struct i2c_client *i2c,
-		    const struct i2c_device_id *id)
+			    const struct i2c_device_id *id)
 {
 	struct rt5616_priv *rt5616;
 	unsigned int val;
 	int ret;
 
 	rt5616 = devm_kzalloc(&i2c->dev, sizeof(struct rt5616_priv),
-				GFP_KERNEL);
-	if (rt5616 == NULL)
+			      GFP_KERNEL);
+	if (!rt5616)
 		return -ENOMEM;
 
 	i2c_set_clientdata(i2c, rt5616);
@@ -1326,14 +1334,14 @@ static int rt5616_i2c_probe(struct i2c_client *i2c,
 	}
 	regmap_write(rt5616->regmap, RT5616_RESET, 0);
 	regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1,
-		RT5616_PWR_VREF1 | RT5616_PWR_MB |
-		RT5616_PWR_BG | RT5616_PWR_VREF2,
-		RT5616_PWR_VREF1 | RT5616_PWR_MB |
-		RT5616_PWR_BG | RT5616_PWR_VREF2);
+			   RT5616_PWR_VREF1 | RT5616_PWR_MB |
+			   RT5616_PWR_BG | RT5616_PWR_VREF2,
+			   RT5616_PWR_VREF1 | RT5616_PWR_MB |
+			   RT5616_PWR_BG | RT5616_PWR_VREF2);
 	mdelay(10);
 	regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1,
-		RT5616_PWR_FV1 | RT5616_PWR_FV2,
-		RT5616_PWR_FV1 | RT5616_PWR_FV2);
+			   RT5616_PWR_FV1 | RT5616_PWR_FV2,
+			   RT5616_PWR_FV1 | RT5616_PWR_FV2);
 
 	ret = regmap_register_patch(rt5616->regmap, init_list,
 				    ARRAY_SIZE(init_list));
@@ -1341,11 +1349,10 @@ static int rt5616_i2c_probe(struct i2c_client *i2c,
 		dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
 
 	regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1,
-		RT5616_PWR_LDO_DVO_MASK, RT5616_PWR_LDO_DVO_1_2V);
+			   RT5616_PWR_LDO_DVO_MASK, RT5616_PWR_LDO_DVO_1_2V);
 
 	return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5616,
-			rt5616_dai, ARRAY_SIZE(rt5616_dai));
-
+				      rt5616_dai, ARRAY_SIZE(rt5616_dai));
 }
 
 static int rt5616_i2c_remove(struct i2c_client *i2c)
@@ -1361,7 +1368,6 @@ static void rt5616_i2c_shutdown(struct i2c_client *client)
 
 	regmap_write(rt5616->regmap, RT5616_HP_VOL, 0xc8c8);
 	regmap_write(rt5616->regmap, RT5616_LOUT_CTRL1, 0xc8c8);
-
 }
 
 static struct i2c_driver rt5616_i2c_driver = {
-- 
1.9.1



From wxt at rock-chips.com  Thu Jan 28 00:43:37 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Thu, 28 Jan 2016 16:43:37 +0800
Subject: [PATCH v4 8/9] ASoC: rt5616: add the mclk for the codec driver
In-Reply-To: <1453970618-4383-1-git-send-email-wxt@rock-chips.com>
References: <1453970618-4383-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1453970618-4383-9-git-send-email-wxt@rock-chips.com>

This patch adds the code to enable the clock to the CODEC driver
if it needs the clock enabled.

In some case, We need to claim the clock which is driving the codec
so that when we enable clock gating, we continue to clock the codec
when needed.

if mclk provided, to enable and disable the clock source.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v4:
- AS the previous discussed by them, add the mclk for codec.
  (https://patchwork.kernel.org/patch/8041001/)

 sound/soc/codecs/rt5616.c | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c
index d4bdf9f..fdca636 100644
--- a/sound/soc/codecs/rt5616.c
+++ b/sound/soc/codecs/rt5616.c
@@ -12,6 +12,7 @@
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/init.h>
+#include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/pm.h>
 #include <linux/i2c.h>
@@ -144,6 +145,7 @@ struct rt5616_priv {
 	struct snd_soc_codec *codec;
 	struct delayed_work patch_work;
 	struct regmap *regmap;
+	struct clk *mclk;
 
 	int sysclk;
 	int sysclk_src;
@@ -1159,7 +1161,34 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
 static int rt5616_set_bias_level(struct snd_soc_codec *codec,
 				 enum snd_soc_bias_level level)
 {
+	struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
+	int ret;
+
 	switch (level) {
+
+	case SND_SOC_BIAS_ON:
+		break;
+
+	case SND_SOC_BIAS_PREPARE:
+		/*
+		 * SND_SOC_BIAS_PREPARE is called while preparing for a
+		 * transition to ON or away from ON. If current bias_level
+		 * is SND_SOC_BIAS_ON, then it is preparing for a transition
+		 * away from ON. Disable the clock in that case, otherwise
+		 * enable it.
+		 */
+		if (IS_ERR(rt5616->mclk))
+			break;
+
+		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) {
+			clk_disable_unprepare(rt5616->mclk);
+		} else {
+			ret = clk_prepare_enable(rt5616->mclk);
+			if (ret)
+				return ret;
+		}
+		break;
+
 	case SND_SOC_BIAS_STANDBY:
 		if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
 			snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
@@ -1198,6 +1227,11 @@ static int rt5616_probe(struct snd_soc_codec *codec)
 {
 	struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
 
+	/* Check if MCLK provided */
+	rt5616->mclk = devm_clk_get(codec->dev, "mclk");
+	if (PTR_ERR(rt5616->mclk) == -EPROBE_DEFER)
+		return -EPROBE_DEFER;
+
 	rt5616->codec = codec;
 
 	return 0;
-- 
1.9.1



From wxt at rock-chips.com  Thu Jan 28 00:43:38 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Thu, 28 Jan 2016 16:43:38 +0800
Subject: [PATCH v4 9/9] ARM: dts: rockchip: support the spi for rk3036
In-Reply-To: <1453970618-4383-1-git-send-email-wxt@rock-chips.com>
References: <1453970618-4383-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1453970618-4383-10-git-send-email-wxt@rock-chips.com>

You have to use the 4 bus to work if someone wants to support
the spi devices, since the the pin is re-used by data[5-8] and spi.
If support the spi making the happy work, that will waste the
emmc performance.

Moment, the kylin hasn't the spi devices to work, so maybe we need wait
the new required to enable in kylin board.

Anyway, the spi should be needed land in rk3036 dts.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v4:
- Add this patch included in kylin series patches.

 arch/arm/boot/dts/rk3036.dtsi | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
index 532f232..40a5017 100644
--- a/arch/arm/boot/dts/rk3036.dtsi
+++ b/arch/arm/boot/dts/rk3036.dtsi
@@ -60,6 +60,7 @@
 		serial0 = &uart0;
 		serial1 = &uart1;
 		serial2 = &uart2;
+		spi = &spi;
 	};
 
 	memory {
@@ -485,6 +486,23 @@
 		status = "disabled";
 	};
 
+	spi: spi at 20074000 {
+		compatible = "rockchip,rockchip-spi";
+		reg = <0x20074000 0x1000>;
+		interrupts = <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&spi_txd &spi_rxd &spi_clk &spi_cs0 &spi_cs1>;
+		num-cs = <2>;
+		clocks =<&cru PCLK_SPI>, <&cru SCLK_SPI>;
+		clock-names = "apb-pclk","spi_pclk";
+		dmas = <&pdma 8>, <&pdma 9>;
+		#dma-cells = <2>;
+		dma-names = "tx", "rx";
+		status = "disabled";
+	};
+
 	pinctrl: pinctrl {
 		compatible = "rockchip,rk3036-pinctrl";
 		rockchip,grf = <&grf>;
@@ -723,5 +741,29 @@
 			};
 			/* no rts / cts for uart2 */
 		};
+
+		spi {
+			spi_txd:spi-txd {
+				rockchip,pins = <1 29 RK_FUNC_3 &pcfg_pull_default>;
+			};
+
+			spi_rxd:spi-rxd {
+				rockchip,pins = <1 28 RK_FUNC_3 &pcfg_pull_default>;
+			};
+
+			spi_clk:spi-clk {
+				rockchip,pins = <2 0 RK_FUNC_2 &pcfg_pull_default>;
+			};
+
+			spi_cs0:spi0-cs0 {
+				rockchip,pins = <1 30 RK_FUNC_3 &pcfg_pull_default>;
+
+			};
+
+			spi_cs1:spi-cs1 {
+				rockchip,pins = <1 31 RK_FUNC_3 &pcfg_pull_default>;
+
+			};
+		};
 	};
 };
-- 
1.9.1



From heiko at sntech.de  Thu Jan 28 08:56:45 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Thu, 28 Jan 2016 17:56:45 +0100
Subject: [PATCH v2] pinctrl: rockchip: add support for the rk3399
In-Reply-To: <1452152498-21780-1-git-send-email-wdc@rock-chips.com>
References: <1451997055-47655-1-git-send-email-wdc@rock-chips.com>
 <1452152498-21780-1-git-send-email-wdc@rock-chips.com>
Message-ID: <7536813.E6m4KCLL9V@diego>

Hi David,

Am Donnerstag, 7. Januar 2016, 15:41:38 schrieb David Wu:
> From: David Wu <david.wu at rock-chips.com>
> 
> The pinctrl of rk3399 is much different from other's,
> especially the 3bits of drive strength.
> 
> Signed-off-by: David Wu <david.wu at rock-chips.com>
> ---
> change from v1:
> - need spin_unlock_irqrestore for set drive default case
> 
>  .../bindings/pinctrl/rockchip,pinctrl.txt          |   1 +
>  drivers/pinctrl/pinctrl-rockchip.c                 | 339
> ++++++++++++++++++++- 2 files changed, 326 insertions(+), 14 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
> b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt index
> 391ef4b..3bb9456 100644
> --- a/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
> +++ b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
> @@ -22,6 +22,7 @@ Required properties for iomux controller:
>    - compatible: one of "rockchip,rk2928-pinctrl",
> "rockchip,rk3066a-pinctrl" "rockchip,rk3066b-pinctrl",
> "rockchip,rk3188-pinctrl"
>  		       "rockchip,rk3288-pinctrl", "rockchip,rk3368-pinctrl"
> +		       "rockchip,rk3399-pinctrl"
>    - rockchip,grf: phandle referencing a syscon providing the
>  	 "general register files"
> 
> diff --git a/drivers/pinctrl/pinctrl-rockchip.c
> b/drivers/pinctrl/pinctrl-rockchip.c index a065112..9d61c6e 100644
> --- a/drivers/pinctrl/pinctrl-rockchip.c
> +++ b/drivers/pinctrl/pinctrl-rockchip.c

[...]

> @@ -685,19 +821,60 @@ static int rockchip_get_drive_perpin(struct
> rockchip_pin_bank *bank, struct rockchip_pin_ctrl *ctrl = info->ctrl;
>  	struct regmap *regmap;
>  	int reg, ret;
> -	u32 data;
> +	u32 data, temp, rmask_bits;
>  	u8 bit;
> +	int drv_type = bank->drv[pin_num / 8].drv_type;
> 
>  	ctrl->drv_calc_reg(bank, pin_num, &regmap, &reg, &bit);
> 
> +	switch (drv_type) {
> +	case DRV_TYPE_IO_1V8_3V0_AUTO:
> +	case DRV_TYPE_IO_3V3_ONLY:
> +		if (RK3399_DRV_3BITS_PER_PIN + bit >= 16) {

According to the doc excerpts, wouldn't the big special handling only be 
necessary for for gpio3b5 alone (+ other banks) ... i.e. the one setting being 
split over both registers?
If so I think doing something like the following might be easier to 
understand? Same of course for the set-counterpart. [Beware if I missed up the 
indices somewhere :-) ]

rmask_bits = RK3399_DRV_3BITS_PER_PIN;
switch(bit) {
case 0 ... 12:
	/* regular case, nothing to do */
	break;
case 15:
	/* drive-strength offset is special, as it is spread over 2 registers */
	ret = regmap_read(regmap, reg, &data);
	if (ret)
		return ret;

	ret = regmap_read(regmap, reg + 0x4, &temp);
	if (ret)
		return ret;

	/*
	 * the bit data[15] contains bit 0 of the value
	 * while temp[1:0] containts bits 2 and 1
	 */
	data >>= 15;
	temp &= 0x3;
	temp <<= 1;
	data |= temp;

	return rockchip_perpin_drv_list[drv_type][data];
case 18 ... 21:
	/* setting fully enclosed in the second register */
	rmask_bits = RK3399_DRV_3BITS_PER_PIN;
	reg += 4;
	bit -= 18;
}

> +			/* need to read regs twice */
> +			ret = regmap_read(regmap, reg, &temp);
> +			if (ret)
> +				return ret;
> +
> +			temp >>= bit;
> +			rmask_bits = 16 - bit;
> +			temp &= (1 << rmask_bits) - 1;
> +
> +			reg = reg + 0x4;
> +			ret = regmap_read(regmap, reg, &data);
> +			if (ret)
> +				return ret;
> +
> +			rmask_bits = RK3399_DRV_3BITS_PER_PIN
> +				+ bit - 16;
> +			data &= (1 << rmask_bits) - 1;
> +			data <<= 16 - bit;
> +			data |= temp;
> +
> +			return rockchip_perpin_drv_list[drv_type][data];
> +		}
> +
> +		rmask_bits = RK3399_DRV_3BITS_PER_PIN;
> +		break;
> +	case DRV_TYPE_IO_DEFAULT:
> +	case DRV_TYPE_IO_1V8_OR_3V0:
> +	case DRV_TYPE_IO_1V8_ONLY:
> +		rmask_bits = RK3288_DRV_BITS_PER_PIN;
> +		break;
> +	default:
> +		dev_err(info->dev, "unsupported pinctrl drive type: %d\n",
> +			drv_type);
> +		return -EINVAL;
> +	}
> +
>  	ret = regmap_read(regmap, reg, &data);
>  	if (ret)
>  		return ret;
> 
>  	data >>= bit;
> -	data &= (1 << RK3288_DRV_BITS_PER_PIN) - 1;
> +	data &= (1 << rmask_bits) - 1;
> 
> -	return rockchip_perpin_drv_list[data];
> +	return rockchip_perpin_drv_list[drv_type][data];
>  }
> 
>  static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank,

[...]

> @@ -729,8 +913,45 @@ static int rockchip_set_drive_perpin(struct
> rockchip_pin_bank *bank,
> 
>  	spin_lock_irqsave(&bank->slock, flags);
> 
> +	switch (drv_type) {
> +	case DRV_TYPE_IO_1V8_3V0_AUTO:
> +	case DRV_TYPE_IO_3V3_ONLY:
> +		if (RK3399_DRV_3BITS_PER_PIN + bit >= 16) {
> +			/* need to write regs twice */
> +			rmask_bits = 16 - bit;
> +			/* enable the write to the equivalent lower bits */
> +			data = ((1 << rmask_bits) - 1) << (bit + 16);
> +			rmask = data | (data >> 16);
> +			ret &= (1 << rmask_bits) - 1;
> +			data |= (ret << bit);
> +
> +			ret = regmap_update_bits(regmap, reg, rmask, data);
> +			if (ret)
> +				return ret;
> +
> +			ret = i >> rmask_bits;
> +			rmask_bits = RK3399_DRV_3BITS_PER_PIN + bit - 16;
> +			reg = reg + 0x4;
> +			bit = 0;

Apart from a change similar to the above, should the setting maybe also 
directly return and not depend on the final write?

> +		} else {
> +			rmask_bits = RK3399_DRV_3BITS_PER_PIN;
> +		}
> +		break;
> +	case DRV_TYPE_IO_DEFAULT:
> +	case DRV_TYPE_IO_1V8_OR_3V0:
> +	case DRV_TYPE_IO_1V8_ONLY:
> +		rmask_bits = RK3288_DRV_BITS_PER_PIN;
> +		break;
> +	default:
> +		spin_unlock_irqrestore(&bank->slock, flags);
> +		dev_err(info->dev, "unsupported pinctrl drive type: %d\n",
> +			drv_type);
> +		return -EINVAL;
> +
> +	}
> +
>  	/* enable the write to the equivalent lower bits */
> -	data = ((1 << RK3288_DRV_BITS_PER_PIN) - 1) << (bit + 16);
> +	data = ((1 << rmask_bits) - 1) << (bit + 16);
>  	rmask = data | (data >> 16);
>  	data |= (ret << bit);
> 

[...]

> @@ -2208,6 +2461,62 @@ static struct rockchip_pin_ctrl rk3368_pin_ctrl = {
>  		.drv_calc_reg		= rk3368_calc_drv_reg_and_bit,
>  };
> 
> +static struct rockchip_pin_bank rk3399_pin_banks[] = {
> +	PIN_BANK_IOMUX_DRV_FLAGS_OFFSET(0, 32, "gpio0", IOMUX_SOURCE_PMU,
> +					IOMUX_SOURCE_PMU,
> +					IOMUX_SOURCE_PMU,
> +					IOMUX_SOURCE_PMU,
> +					DRV_TYPE_IO_1V8_ONLY,
> +					DRV_TYPE_IO_1V8_ONLY,
> +					0,
> +					0,

DRV_TYPE_IO_DEFAULT instead of 0 please.



From heiko at sntech.de  Thu Jan 28 09:01:08 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Thu, 28 Jan 2016 18:01:08 +0100
Subject: [PATCH] clk: rockchip: Allow sclk_i2s0 and i2s0_frac to change
 their parents rate on rk3188
In-Reply-To: <1453897469-2134-1-git-send-email-al.kochet@gmail.com>
References: <1453897469-2134-1-git-send-email-al.kochet@gmail.com>
Message-ID: <1687954.9WCMHmWDJQ@diego>

Hi Alexander,

Am Mittwoch, 27. Januar 2016, 15:24:29 schrieb Alexander Kochetkov:
> Allow sclk_i2s0 and i2s0_frac to change their parents rate as
> that the upstream dividers are purely there to feed sclk_i2s0
> 
> Tested on radxarock-lite.
> 
> Signed-off-by: Alexander Kochetkov <al.kochet at gmail.com>

doesn't apply due to the fractional divider change going into 4.5

please respin on top of 4.5-rc1 (or my clock-branch for 4.6 [0],
but should be the same in that regard).


Thanks
Heiko

[0] https://git.kernel.org/cgit/linux/kernel/git/mmind/linux-rockchip.git/log/?h=v4.6-clk/next



From heiko at sntech.de  Thu Jan 28 09:04:01 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Thu, 28 Jan 2016 18:04:01 +0100
Subject: [PATCH] clk: rockchip: fix wrong mmc phase shift for rk3228
In-Reply-To: <1453779018-30666-1-git-send-email-shawn.lin@rock-chips.com>
References: <1453779018-30666-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <2854115.dRPO4ABWrL@diego>

Am Dienstag, 26. Januar 2016, 11:30:18 schrieb Shawn Lin:
> mmc sample shift is 0 for rk3228 refer to user manaul.
> So it's broken if we enable mmc tuning for rk3228.
> 
> Fixes: 307a2e9ac ("clk: rockchip: add clock controller for rk3228")
> Cc: Xing Zheng <zhengxing at rock-chips.com>
> Cc: Jeffy Chen <jeffy.chen at rock-chips.com>
> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>

applied to my clk branch for 4.6 with Xing's review


Thanks
Heiko


From dianders at chromium.org  Thu Jan 28 10:16:22 2016
From: dianders at chromium.org (Doug Anderson)
Date: Thu, 28 Jan 2016 10:16:22 -0800
Subject: [PATCH v5 04/21] usb: dwc2: host: Set host_perio_tx_fifo_size to
 304 for rk3066
In-Reply-To: <56A9B80A.2020506@rock-chips.com>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
 <1453486736-15358-5-git-send-email-dianders@chromium.org>
 <56A986B4.2050002@rock-chips.com>
 <CAD=FV=VAkY0ijkUBrgjY04+OfYq35u=MbqbbomeQxp_J179iHw@mail.gmail.com>
 <56A9B80A.2020506@rock-chips.com>
Message-ID: <CAD=FV=Xtbmf2yOQx=cNTnrsV_35jTUhJCKbBNuHCRpr-qXrW0A@mail.gmail.com>

Kever,


On Wed, Jan 27, 2016 at 10:41 PM, Kever Yang <kever.yang at rock-chips.com> wrote:
> Hi Doug,
>
> We are in HOST mode, we only need to receive data from USB camera
> with RX FIFO, and no need to use TX FIFO for USB webcam, right? :)
>
> Any way, I think we need to NAK this patch after look into the design
> of dwc2 controller. Because all the dwc2 controller inside the Rockchip
> chips don't support the thresholding FIFO mode, in this case, there is
> no more transaction before a whole packet is send out and the dwc2 only
> care if the available FIFO is enough for next packet or not.
>
> So, the addition 48 words won't help to shorten the latency for data prepare
> in this case.

Ah ha!  I see where I messed up.  It wasn't lack of FIFO space that I
was running into, it was lack of queue space.  :-P  I had conflated
the two of them in my mind.  We still use the TX queue to transmit
small packets so that we can receive our data...

OK, so it's pretty sane to assume that exhausting the periodic TX FIFO
isn't terribly common, I think.  Audio won't do it by itself and you
probably won't have more than one audio device.  I guess if you had a
video _output_ device hooked over USB then it could possible exhaust
things.  ...but I think dwc2 still has a ways to do before I'd suggest
anyone hooking that up.  :-P  At some point in time you could imagine
the driver being more dynamic.

OK, so the non periodic FIFO it is, then.

-Doug


From dianders at chromium.org  Thu Jan 28 15:25:35 2016
From: dianders at chromium.org (Doug Anderson)
Date: Thu, 28 Jan 2016 15:25:35 -0800
Subject: [PATCH v5 04/21] usb: dwc2: host: Set host_perio_tx_fifo_size to
 304 for rk3066
In-Reply-To: <CAD=FV=Xtbmf2yOQx=cNTnrsV_35jTUhJCKbBNuHCRpr-qXrW0A@mail.gmail.com>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
 <1453486736-15358-5-git-send-email-dianders@chromium.org>
 <56A986B4.2050002@rock-chips.com>
 <CAD=FV=VAkY0ijkUBrgjY04+OfYq35u=MbqbbomeQxp_J179iHw@mail.gmail.com>
 <56A9B80A.2020506@rock-chips.com>
 <CAD=FV=Xtbmf2yOQx=cNTnrsV_35jTUhJCKbBNuHCRpr-qXrW0A@mail.gmail.com>
Message-ID: <CAD=FV=UQF7u1t79Aac0Y70eYERuhg4dS-ciof9KpqFTL+EYujQ@mail.gmail.com>

Hi,

On Thu, Jan 28, 2016 at 10:16 AM, Doug Anderson <dianders at chromium.org> wrote:
> Kever,
>
>
> On Wed, Jan 27, 2016 at 10:41 PM, Kever Yang <kever.yang at rock-chips.com> wrote:
>> Hi Doug,
>>
>> We are in HOST mode, we only need to receive data from USB camera
>> with RX FIFO, and no need to use TX FIFO for USB webcam, right? :)
>>
>> Any way, I think we need to NAK this patch after look into the design
>> of dwc2 controller. Because all the dwc2 controller inside the Rockchip
>> chips don't support the thresholding FIFO mode, in this case, there is
>> no more transaction before a whole packet is send out and the dwc2 only
>> care if the available FIFO is enough for next packet or not.
>>
>> So, the addition 48 words won't help to shorten the latency for data prepare
>> in this case.
>
> Ah ha!  I see where I messed up.  It wasn't lack of FIFO space that I
> was running into, it was lack of queue space.  :-P  I had conflated
> the two of them in my mind.  We still use the TX queue to transmit
> small packets so that we can receive our data...
>
> OK, so it's pretty sane to assume that exhausting the periodic TX FIFO
> isn't terribly common, I think.  Audio won't do it by itself and you
> probably won't have more than one audio device.  I guess if you had a
> video _output_ device hooked over USB then it could possible exhaust
> things.  ...but I think dwc2 still has a ways to do before I'd suggest
> anyone hooking that up.  :-P  At some point in time you could imagine
> the driver being more dynamic.
>
> OK, so the non periodic FIFO it is, then.

Actually, in my simple tests adding it to the non periodic FIFO didn't
help at all.  I'll just drop this patch.

-Doug


From dianders at chromium.org  Thu Jan 28 15:26:36 2016
From: dianders at chromium.org (Doug Anderson)
Date: Thu, 28 Jan 2016 15:26:36 -0800
Subject: [PATCH v5 05/21] usb: dwc2: host: Avoid use of chan->qh after qh
 freed
In-Reply-To: <56A98A47.2010705@rock-chips.com>
References: <1453486736-15358-1-git-send-email-dianders@chromium.org>
 <1453486736-15358-6-git-send-email-dianders@chromium.org>
 <56A98A47.2010705@rock-chips.com>
Message-ID: <CAD=FV=X=oRSXvxYvEQJY0aFCLFoGT7C2mDoiKxaODQmtFRwYFQ@mail.gmail.com>

Hi,

On Wed, Jan 27, 2016 at 7:25 PM, Kever Yang <kever.yang at rock-chips.com> wrote:
> Hi Doug,
>
> The NULL pointer bug is one of the most frequent issue we met
> during hot plug stress test, thanks for this bug fix.
>
> Reviewed-by: Kever Yang <kever.yang at rock-chips.com>
>
> Thanks,
> - Kever

Thanks for your review.  I think I actually found one more place where
I needed to clean up the channel->qh, so I'll include that in my next
version.  I'll plan to keep your reviewed-by.  Please yell if you want
it removed.

-Doug


From mark.yao at rock-chips.com  Thu Jan 28 17:49:55 2016
From: mark.yao at rock-chips.com (Mark yao)
Date: Fri, 29 Jan 2016 09:49:55 +0800
Subject: [PATCH v7 0/2] Add Rockchip Inno-HDMI driver
In-Reply-To: <1453777142-30119-1-git-send-email-ykk@rock-chips.com>
References: <1453777142-30119-1-git-send-email-ykk@rock-chips.com>
Message-ID: <56AAC543.8080108@rock-chips.com>

On 2016?01?26? 10:59, Yakir Yang wrote:
> Here are a brief introduction to Innosilicon HDMI IP:
>    - Support HDMI 1.4a, HDCP 1.2 and DVI 1.0 standard compliant transmitter
>    - Support HDMI1.4 a/b 3D function defined in HDMI 1.4 a/b spec
>    - Digital video interface supports a pixel size of 24, 30, 36, 48bits color depth in RGB
>    - S/PDIF output supports PCM, Dolby Digital, DTS digital audio transmission
>      (32-192kHz Fs) using IEC60958 and IEC 61937
>    - The EDID and CEC function are also supported by Innosilicon HDMI Transmitter Controlle
>
> Changes in v7:
> - Correct the module licnese statement (Paul)
>       - MODULE_LICENSE("GPL");
>       + MODULE_LICENSE("GPL v2");
> - Start indentation with tabs and fix the misspell in Kconfig (Paul)
> - Carry the lost device-binding document (Heiko)
>
> Changes in v6:
> - Rebase the Makefile/Kconfig files which add by Chris's rockchip-mipi driver (Caeser)
>
> Changes in v5:
> - Use hdmi_infoframe helper functions to packed the infoframe (Russell)
> - Remove the unused double wait_for_completion_timeout for ddc transfer (Russell)
> - Remove the unused local variable in "inno_hdmi_i2c_write()" function (Russell)
>
> Changes in v4:
> - Modify the commit title "drm/rockchip: hdmi: ..." (Mark)
> - Correct the "DKMS" to "DPMS" (Mark)
> - Fix over 80 characters problems (Mark)
> - Remove encoder .prepare/.commit helper functions, and move the vop mode
> configure function into encoder .enable helper functions. (Mark)
>
> Changes in v3:
> - Use encoder enable/disable function, and remove the encoder DPMS function
> - Keep HDMI PLL power on in standby mode
>
> Changes in v2:
> - Using DRM atomic helper functions for connector init (Mark)
> - Remove "hdmi->connector.encoder = encoder;" (Mark)
> - Add the Acked-by tags from Rob
> - Correct the misspell "rk3036-dw-hdmi" (Heiko)
>
> Yakir Yang (2):
>    drm/rockchip: hdmi: add Innosilicon HDMI support
>    dt-bindings: add document for Innosilicon HDMI on Rockchip platform
>
>   .../display/rockchip/inno_hdmi-rockchip.txt        |  50 ++
>   drivers/gpu/drm/rockchip/Kconfig                   |   8 +
>   drivers/gpu/drm/rockchip/Makefile                  |   1 +
>   drivers/gpu/drm/rockchip/inno_hdmi.c               | 941 +++++++++++++++++++++
>   drivers/gpu/drm/rockchip/inno_hdmi.h               | 362 ++++++++
>   5 files changed, 1362 insertions(+)
>   create mode 100644 Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt
>   create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.c
>   create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.h
>

Ping, inno HDMI driver looks good for me, and it works, So I'd like to 
merge it into drm/rockchip if there is no doubt these days.

Thanks.

-- 
?ark Yao




From dianders at chromium.org  Thu Jan 28 18:19:51 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Thu, 28 Jan 2016 18:19:51 -0800
Subject: [PATCH v6 0/22] usb: dwc2: host: Fix and speed up all the stuff,
 especially with splits
Message-ID: <1454034013-24657-1-git-send-email-dianders@chromium.org>

This is a bit of catchall series for all the bug fix and performance
patches I've been working on over the last few months.  Note that for
dwc2 we need to do LOTS in software and need super low interrupt
latency, so most performance improvements actually fix real bugs.

Patches are structured to start with no-brainer stuff that could be
applied ASAP, especially things I've already gotten Acks for.  Things
get slightly more RFC / RFT like as we get farther down the series.
Anything that can be landed sooner rather than later (especially those
Acked long ago) would help in re-posts (I'm not biased, of course).

It's been a few months since my last post of this series.  In the
meantime I've added a bunch of small bugfixes to the start of it and
also TOTALLY REWROTE the microframe scheduler.  I'll say up front: I
know nothing about USB.  I haven't read the whole spec.  I'm not
terribly familiar with the OHCI, EHCI, and XHCI drivers in the kernel.
...and I'm pretty clueless overall.  Nevertheless, I've attempted to
write up a fancy scheduler based on the portion of the spec talking
about microframe scheduling requirements.  This rewritten scheduler does
seem to help when I start jamming lots of USB things into a hub, so
presumably the code is a reasonably starting point.  Given my current
understanding of USB the old code was fairly insane, so presumably even
if my new patch isn't perfect it's better than what we had.

Anyway, on to the patches:

1. usb: dwc2: rockchip: Make the max_transfer_size automatic

   No brainer.  Can land any time.

2. usb: dwc2: host: Get aligned DMA in a more supported way

   Although this touches a lot of code, it's mostly just deleting
   stuff.  The way this is working is nearly the same as tegra.  Biggest
   objection I expect is that it has too much duplication with tegra and
   musb.  I'd personally prefer to land it now and remove duplication
   later, but up to others.  Speeding up interrupt handler helps with
   SOF scheduling, so this is not just a dumb optimization.

3. usb: dwc2: host: Set host_rx_fifo_size to 525 for rk3066

   Seems like a good idea and small impact, but if someone hates it or
   it breaks on some Rockchip SoC, just drop it.  I've only tested on
   rk3288 so it would be nice if someone with access to more Rockchip
   SoCs can give a tested by.

4. usb: dwc2: host: Avoid use of chan->qh after qh freed

   Simple bugfix.  Unrelated to the series but thrown in here.

5. usb: dwc2: host: Always add to the tail of queues

   Big functionality improvement.  Small patch.  Suggest applying ASAP.

6. usb: dwc2: host: fix split transfer schedule sequence

   Unless I'm misunderstanding, this should be a no-brainer to fix.
   Could be some bikeshedding on how to fix this.  Let me know if/how
   you want me to spin.  Otherwise I'd say land it and it will fix a
   bunch of stuff.

7. usb: dwc2: host: Add scheduler tracing

   Shouldn't hurt anything.  If you have bikesheds, let me know.  Many
   future patches require this one just because they add additional
   traces.

8. usb: dwc2: host: Add a delay before releasing periodic bandwidth
9. usb: dwc2: host: Giveback URB in tasklet context

   I think we should take these.  They improve things a bunch and I have
   found no regressions due to them.  Additional testing appreciated, of
   course.

10. usb: dwc2: host: Properly set the HFIR

   I sent this out on its own, but since I'm resending the series I
   figured I'm jam it in here.  Can really go anywhere in the series or
   applied totally on its own.

11. usb: dwc2: host: There's not really a TT for the root hub

   Seems right to me, but if someone knows better then please drop.
   Wasn't part of the previous series so doesn't have any Tested-by
   tags, though Stefan did indicated that he tried it and it didn't
   appear to break anything for him.

   Can be applied totally on its own.

12. usb: dwc2: host: Use periodic interrupt even with DMA

   Just came up with this one recently so it's had slightly less
   testing.  ...but it certainly fixed a bunch of stuff.  Could probably
   be moved around in the series to be pretty much anywhere.  I don't
   think this has a huge impact until we fix the scheduler (below) but
   at the same time I'm pretty sure it's something that's been wrong for
   a long time.

13. usb: dwc2: host: Rename some fields in struct dwc2_qh
14. usb: dwc2: host: Reorder things in hcd_queue.c
15. usb: dwc2: host: Split code out to make dwc2_do_reserve()

   Cleanups to make future patches easier to understand.  Bikeshed away.
   All no-op changes.

16. usb: dwc2: host: Add scheduler logging for missed SOFs

   I found this to be quite helpful.  If you hate it, drop it from the
   series.

17. usb: dwc2: host: Manage frame nums better in scheduler

   Doesn't totally make sense on its own, but a good halfway point to
   the microframe scheduler.  ...and shouldn't regress anything.  Allows
   us to do the "Properly set even/odd frame" patch below which
   definitely improves things.

18. usb: dwc2: host: Schedule periodic right away if it's time

   Yet another small change to make scheduling tighter.

19. usb: dwc2: host: Add dwc2_hcd_get_future_frame_number() call

   Prep for ("usb: dwc2: host: Properly set even/odd frame")

20. usb: dwc2: host: Properly set even/odd frame

   Helps quite a bit.  Helps even more after the redone microframe
   scheduler.  Feel free to tidy up if you see easy ways to do this.
   Maybe someone has a better way to estimate time on the wire?

21. usb: dwc2: host: Totally redo the microframe scheduler

   Eyeballs please!  I think I've stared at this too much and now my
   eyes are glazing over.  This definitely helps but also probably needs
   a few more spins?  Of course, if nobody wants to review it, IMHO
   checking it in as-is is WAAAAY better than what we had before.

22. usb: dwc2: host: If using uframe scheduler, end splits better

   Low confidence in this one.  Worry that it will end something too
   soon, but haven't seen it yet.

===

Below is discussion of some of the speedup stuff (mostly relevant to the
first few patches).

===

The dwc2 interrupt handler is quite slow.  On rk3288 with a few things
plugged into the ports and with cpufreq locked at 696MHz (to simulate
real world idle system), I can easily observe dwc2_handle_hcd_intr()
taking > 120 us, sometimes > 150 us.  Note that SOF interrupts come
every 125 us with high speed USB, so taking > 120 us in the interrupt
handler is a big deal.

The patches here will speed up the interrupt controller significantly.
After this series, I have a hard time seeing the interrupt controller
taking > 20 us and I don't ever see it taking > 30 us ever in my tests
unless I bring the cpufreq back down.  With the cpufreq at 126 MHz I can
still see the interrupt handler take > 50 us, so I'm sure we could
improve this further.  ...but hey, it's a start.

This series also shows big speed improvements when testing with a USB
Gigabit Ethernet adapter.  Previously the tested adapter would top out
at about 15MB/s.  After these changes it gets about 23MB/s.

In addition to the speedup, this series also has the advantage of
simplifying dwc2 and making it more like everyone else (introducing the
possibility of future simplifications).  Picking this series up will
help your diffstat and likely win you friends.  ;)

===

Steps for gathering data with ftrace (for some reason I have to run
twice):

cd /sys/devices/system/cpu/cpu0/cpufreq/
echo userspace > scaling_governor
echo 696000 > scaling_setspeed

cd /sys/kernel/debug/tracing
echo 0 > tracing_on
echo "" > trace
echo nop > current_tracer
echo function_graph > current_tracer
echo dwc2_handle_hcd_intr > set_graph_function
echo dwc2_handle_common_intr >> set_graph_function
echo dwc2_handle_hcd_intr > set_ftrace_filter
echo dwc2_handle_common_intr >> set_ftrace_filter
echo funcgraph-abstime > trace_options
echo 70 > tracing_thresh
echo 1 > /sys/kernel/debug/tracing/tracing_on

sleep 2
cat trace

Changes in v6:
- Add Kever's Reviewed-bys.
- Add Kever's Tested-bys.
- Add Heiko's Tested-bys.
- Add Stefan's Tested-bys.
- Back to 525 dwords, not 528.
- Add one more instance of check; kept Reviewed-by / Tested-by (OK?).
- Fix patch tags (hcd -> host)
- Incorporated Properly set the HFIR patch to big series in v6
- There's not really a TT for the root hub new for v6
- Fix bug where periodic things get scheduled too quick (Alan Stern)
- Removed incorrect limit on number of channels (Heiko Stuebner).
- Fixed order of operations bug in debug print.

Changes in v5:
- Move list maintenance to hcd.c to avoid gadget-only compile error
- Moved defines outside of ifdef to avoid gadget-only compile error.

Changes in v4:
- Add John's Acks from <https://patchwork.kernel.org/patch/7631551>
- Set host_rx_fifo_size to 528 for rk3066 new for v4.
- Avoid use of chan->qh after qh freed new for v4.
- Always add to the tail of queues new for v4.
- fix split transfer schedule sequence new for v4.
- Retooled scheduler tracing a bit, so left off John's Ack from v3.
- Moved periodic bandwidth release delay patch earlier again.
- A bit earlier in the list of patches than in v3.
- Use periodic interrupt even with DMA new for v4.
- Rename some fields in struct dwc2_qh new for v4.
- Reorder things in hcd_queue.c new for v4.
- Split code out to make dwc2_do_reserve() new for v4.
- Add scheduler logging for missed SOFs new for v4.
- Manage frame nums better in scheduler new for v4.
- Schedule periodic right away if it's time new for v4.
- Add dwc2_hcd_get_future_frame_number() call new for v4.
- Properly set even/odd frame new for v4.
- Figured out what the microframe scheduler was supposed to do.
- Microframe rewrite is totally different from v3, hopefully more right.
- Microframe rewrite is later in the series now.
- If using uframe scheduler, end splits better new for v4.

Changes in v3:
- Moved periodic bandwidth release delay patch later in the series.
- The uframe scheduler patch is folded into optimization series.
- Optimize uframe scheduler "single uframe" case a little.
- uframe scheduler now atop logging patches.
- uframe scheduler now before delayed bandwidth release patches.
- Add defines like EARLY_FRAME_USEC
- Reorder dwc2_deschedule_periodic() in prep for future patches.
- uframe scheduler now shows real usefulness w/ future patches!
- Assuming single_tt is new for v3; not terribly well tested (yet).
- Keep track and use our uframe new for v3.

Changes in v2:
- Add a warn if setup_dma is not aligned (Julius Werner).
- Periodic bandwidth release delay new for V2
- Commit message now says that URB giveback change needs delay change.
- Totally rewrote uframe scheduler again after writing test code.
- uframe scheduler atop delayed bandwidth release patches.

Douglas Anderson (22):
  usb: dwc2: rockchip: Make the max_transfer_size automatic
  usb: dwc2: host: Get aligned DMA in a more supported way
  usb: dwc2: host: Set host_rx_fifo_size to 525 for rk3066
  usb: dwc2: host: Avoid use of chan->qh after qh freed
  usb: dwc2: host: Always add to the tail of queues
  usb: dwc2: host: fix split transfer schedule sequence
  usb: dwc2: host: Add scheduler tracing
  usb: dwc2: host: Add a delay before releasing periodic bandwidth
  usb: dwc2: host: Giveback URB in tasklet context
  usb: dwc2: host: Properly set the HFIR
  usb: dwc2: host: There's not really a TT for the root hub
  usb: dwc2: host: Use periodic interrupt even with DMA
  usb: dwc2: host: Rename some fields in struct dwc2_qh
  usb: dwc2: host: Reorder things in hcd_queue.c
  usb: dwc2: host: Split code out to make dwc2_do_reserve()
  usb: dwc2: host: Add scheduler logging for missed SOFs
  usb: dwc2: host: Manage frame nums better in scheduler
  usb: dwc2: host: Schedule periodic right away if it's time
  usb: dwc2: host: Add dwc2_hcd_get_future_frame_number() call
  usb: dwc2: host: Properly set even/odd frame
  usb: dwc2: host: Totally redo the microframe scheduler
  usb: dwc2: host: If using uframe scheduler, end splits better

 drivers/usb/dwc2/core.c      |  119 ++-
 drivers/usb/dwc2/core.h      |  114 ++-
 drivers/usb/dwc2/hcd.c       |  392 ++++++---
 drivers/usb/dwc2/hcd.h       |  126 ++-
 drivers/usb/dwc2/hcd_ddma.c  |   41 +-
 drivers/usb/dwc2/hcd_intr.c  |  174 ++--
 drivers/usb/dwc2/hcd_queue.c | 1965 ++++++++++++++++++++++++++++++++++--------
 drivers/usb/dwc2/platform.c  |    4 +-
 8 files changed, 2276 insertions(+), 659 deletions(-)

-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Thu Jan 28 18:19:52 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Thu, 28 Jan 2016 18:19:52 -0800
Subject: [PATCH v6 01/22] usb: dwc2: rockchip: Make the max_transfer_size
 automatic
In-Reply-To: <1454034013-24657-1-git-send-email-dianders@chromium.org>
References: <1454034013-24657-1-git-send-email-dianders@chromium.org>
Message-ID: <1454034013-24657-2-git-send-email-dianders@chromium.org>

Previously we needed to set the max_transfer_size to explicitly be 65535
because the old driver would detect that our hardware could support much
bigger transfers and then would try to do them.  This wouldn't work
since the DMA alignment code couldn't support it.

Later in commit e8f8c14d9da7 ("usb: dwc2: clip max_transfer_size to
65535") upstream added support for clipping this automatically.  Since
that commit it has been OK to just use "-1" (default), but nobody
bothered to change it.

Let's change it to default now for two reasons:
- It's nice to use autodetected params.
- If we can remove the 65535 limit, we can transfer more!

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Acked-by: John Youn <johnyoun at synopsys.com>
Tested-by: Heiko Stuebner <heiko at sntech.de>
---
Changes in v6: None
Changes in v5: None
Changes in v4:
- Add John's Acks from <https://patchwork.kernel.org/patch/7631551>

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/platform.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
index 510f787434b3..5008a467ce06 100644
--- a/drivers/usb/dwc2/platform.c
+++ b/drivers/usb/dwc2/platform.c
@@ -129,7 +129,7 @@ static const struct dwc2_core_params params_rk3066 = {
 	.host_rx_fifo_size		= 520,	/* 520 DWORDs */
 	.host_nperio_tx_fifo_size	= 128,	/* 128 DWORDs */
 	.host_perio_tx_fifo_size	= 256,	/* 256 DWORDs */
-	.max_transfer_size		= 65535,
+	.max_transfer_size		= -1,
 	.max_packet_count		= -1,
 	.host_channels			= -1,
 	.phy_type			= -1,
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Thu Jan 28 18:19:54 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Thu, 28 Jan 2016 18:19:54 -0800
Subject: [PATCH v6 03/22] usb: dwc2: host: Set host_rx_fifo_size to 525 for
 rk3066
In-Reply-To: <1454034013-24657-1-git-send-email-dianders@chromium.org>
References: <1454034013-24657-1-git-send-email-dianders@chromium.org>
Message-ID: <1454034013-24657-4-git-send-email-dianders@chromium.org>

As documented in dwc2_calculate_dynamic_fifo(), host_rx_fifo_size should
really be:
 2 * ((Largest Packet size / 4) + 1 + 1) + n
 with n = number of host channel.

We have 9 host channels, so
 2 * ((1024/4) + 2) + 9 = 516 + 9 = 525

We've got 960 / 972 total_fifo_size on rk3288 (and presumably on
rk3066) and 525 + 128 + 256 = 909 so we're still under on both ports
even when we increment by 5.

In the future, it would be nice if dwc2_calculate_dynamic_fifo() could
handle the "too small" FIFO case and come up with something more
dynamically.  When we do that we can figure out how to allocate the
extra 48 / 60 bytes of FIFO that we're currently wasting.

NOTE: no known bugs are fixed by this patch, but it seems like a simple
fix and ought to fix someone.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Reviewed-by: Kever Yang <kever.yang at rock-chips.com>
Tested-by: Heiko Stuebner <heiko at sntech.de>
---
Changes in v6:
- Back to 525 dwords, not 528.
- Add Kever's Reviewed-by.
- Add Heiko's Tested-by.

Changes in v5: None
Changes in v4:
- Set host_rx_fifo_size to 528 for rk3066 new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/platform.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
index 5008a467ce06..b277e521a311 100644
--- a/drivers/usb/dwc2/platform.c
+++ b/drivers/usb/dwc2/platform.c
@@ -126,7 +126,7 @@ static const struct dwc2_core_params params_rk3066 = {
 	.speed				= -1,
 	.enable_dynamic_fifo		= 1,
 	.en_multiple_tx_fifo		= -1,
-	.host_rx_fifo_size		= 520,	/* 520 DWORDs */
+	.host_rx_fifo_size		= 525,	/* 525 DWORDs */
 	.host_nperio_tx_fifo_size	= 128,	/* 128 DWORDs */
 	.host_perio_tx_fifo_size	= 256,	/* 256 DWORDs */
 	.max_transfer_size		= -1,
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Thu Jan 28 18:19:55 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Thu, 28 Jan 2016 18:19:55 -0800
Subject: [PATCH v6 04/22] usb: dwc2: host: Avoid use of chan->qh after qh freed
In-Reply-To: <1454034013-24657-1-git-send-email-dianders@chromium.org>
References: <1454034013-24657-1-git-send-email-dianders@chromium.org>
Message-ID: <1454034013-24657-5-git-send-email-dianders@chromium.org>

When poking around with USB devices with slub_debug enabled, I found
another obvious use after free.  Turns out that in dwc2_hc_n_intr() I
was in a state when the contents of chan->qh was filled with 0x6b,
indicating that chan->qh was freed but chan still had a reference to
it.

Let's make sure that whenever we free qh we also make sure we remove a
reference from its channel.

The bug fixed here doesn't appear to be new--I believe I just got lucky
and happened to see it while stress testing.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Reviewed-by: Kever Yang <kever.yang at rock-chips.com>
Tested-by: Heiko Stuebner <heiko at sntech.de>
Tested-by: Stefan Wahren <stefan.wahren at i2se.com>
---
Changes in v6:
- Add one more instance of check; kept Reviewed-by / Tested-by (OK?).
- Add Kever's Reviewed-by.
- Add Heiko's Tested-by.
- Add Stefan's Tested-by.

Changes in v5: None
Changes in v4:
- Avoid use of chan->qh after qh freed new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/hcd.c      | 10 ++++++++++
 drivers/usb/dwc2/hcd_intr.c | 10 ++++++++++
 2 files changed, 20 insertions(+)

diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index bc4bdbc1534e..e2d2e9be366e 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -164,6 +164,9 @@ static void dwc2_qh_list_free(struct dwc2_hsotg *hsotg,
 					 qtd_list_entry)
 			dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh);
 
+		if (qh->channel && qh->channel->qh == qh)
+			qh->channel->qh = NULL;
+
 		spin_unlock_irqrestore(&hsotg->lock, flags);
 		dwc2_hcd_qh_free(hsotg, qh);
 		spin_lock_irqsave(&hsotg->lock, flags);
@@ -554,7 +557,12 @@ static int dwc2_hcd_endpoint_disable(struct dwc2_hsotg *hsotg,
 		dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh);
 
 	ep->hcpriv = NULL;
+
+	if (qh->channel && qh->channel->qh == qh)
+		qh->channel->qh = NULL;
+
 	spin_unlock_irqrestore(&hsotg->lock, flags);
+
 	dwc2_hcd_qh_free(hsotg, qh);
 
 	return 0;
@@ -2782,6 +2790,8 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
 fail3:
 	dwc2_urb->priv = NULL;
 	usb_hcd_unlink_urb_from_ep(hcd, urb);
+	if (qh_allocated && qh->channel && qh->channel->qh == qh)
+		qh->channel->qh = NULL;
 fail2:
 	spin_unlock_irqrestore(&hsotg->lock, flags);
 	urb->hcpriv = NULL;
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index 352c98364317..99efc2bd1617 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -1935,6 +1935,16 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum)
 	}
 
 	dwc2_writel(hcint, hsotg->regs + HCINT(chnum));
+
+	/*
+	 * If we got an interrupt after someone called
+	 * dwc2_hcd_endpoint_disable() we don't want to crash below
+	 */
+	if (!chan->qh) {
+		dev_warn(hsotg->dev, "Interrupt on disabled channel\n");
+		return;
+	}
+
 	chan->hcint = hcint;
 	hcint &= hcintmsk;
 
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Thu Jan 28 18:19:53 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Thu, 28 Jan 2016 18:19:53 -0800
Subject: [PATCH v6 02/22] usb: dwc2: host: Get aligned DMA in a more supported
 way
In-Reply-To: <1454034013-24657-1-git-send-email-dianders@chromium.org>
References: <1454034013-24657-1-git-send-email-dianders@chromium.org>
Message-ID: <1454034013-24657-3-git-send-email-dianders@chromium.org>

All other host controllers who want aligned buffers for DMA do it a
certain way.  Let's do that too instead of working behind the USB core's
back.  This makes our interrupt handler not take forever and also rips
out a lot of code, simplifying things a bunch.

This also has the side effect of removing the 65535 max transfer size
limit.

NOTE: The actual code to allocate the aligned buffers is ripped almost
completely from the tegra EHCI driver.  At some point in the future we
may want to add this functionality to the USB core to share more code
everywhere.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Acked-by: John Youn <johnyoun at synopsys.com>
Tested-by: Heiko Stuebner <heiko at sntech.de>
Tested-by: John Youn <johnyoun at synopsys.com>
Tested-by: Stefan Wahren <stefan.wahren at i2se.com>
---
Changes in v6:
- Add Stefan's Tested-by.

Changes in v5: None
Changes in v4:
- Add John's Acks from <https://patchwork.kernel.org/patch/7631551>

Changes in v3: None
Changes in v2:
- Add a warn if setup_dma is not aligned (Julius Werner).

 drivers/usb/dwc2/core.c      |  21 +-----
 drivers/usb/dwc2/hcd.c       | 170 +++++++++++++++++++++----------------------
 drivers/usb/dwc2/hcd.h       |  10 ---
 drivers/usb/dwc2/hcd_intr.c  |  65 -----------------
 drivers/usb/dwc2/hcd_queue.c |   7 +-
 5 files changed, 87 insertions(+), 186 deletions(-)

diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index 39a0fa8a4c0a..73f2771b7740 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -1958,19 +1958,11 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
 	}
 
 	if (hsotg->core_params->dma_enable > 0) {
-		dma_addr_t dma_addr;
-
-		if (chan->align_buf) {
-			if (dbg_hc(chan))
-				dev_vdbg(hsotg->dev, "align_buf\n");
-			dma_addr = chan->align_buf;
-		} else {
-			dma_addr = chan->xfer_dma;
-		}
-		dwc2_writel((u32)dma_addr, hsotg->regs + HCDMA(chan->hc_num));
+		dwc2_writel((u32)chan->xfer_dma,
+			    hsotg->regs + HCDMA(chan->hc_num));
 		if (dbg_hc(chan))
 			dev_vdbg(hsotg->dev, "Wrote %08lx to HCDMA(%d)\n",
-				 (unsigned long)dma_addr, chan->hc_num);
+				 (unsigned long)chan->xfer_dma, chan->hc_num);
 	}
 
 	/* Start the split */
@@ -3363,13 +3355,6 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
 	width = (hwcfg3 & GHWCFG3_XFER_SIZE_CNTR_WIDTH_MASK) >>
 		GHWCFG3_XFER_SIZE_CNTR_WIDTH_SHIFT;
 	hw->max_transfer_size = (1 << (width + 11)) - 1;
-	/*
-	 * Clip max_transfer_size to 65535. dwc2_hc_setup_align_buf() allocates
-	 * coherent buffers with this size, and if it's too large we can
-	 * exhaust the coherent DMA pool.
-	 */
-	if (hw->max_transfer_size > 65535)
-		hw->max_transfer_size = 65535;
 	width = (hwcfg3 & GHWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK) >>
 		GHWCFG3_PACKET_SIZE_CNTR_WIDTH_SHIFT;
 	hw->max_packet_count = (1 << (width + 4)) - 1;
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 8847c72e55f6..bc4bdbc1534e 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -635,9 +635,9 @@ static void dwc2_hc_init_split(struct dwc2_hsotg *hsotg,
 	chan->hub_port = (u8)hub_port;
 }
 
-static void *dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
-			       struct dwc2_host_chan *chan,
-			       struct dwc2_qtd *qtd, void *bufptr)
+static void dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
+			      struct dwc2_host_chan *chan,
+			      struct dwc2_qtd *qtd)
 {
 	struct dwc2_hcd_urb *urb = qtd->urb;
 	struct dwc2_hcd_iso_packet_desc *frame_desc;
@@ -657,7 +657,6 @@ static void *dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
 			else
 				chan->xfer_buf = urb->setup_packet;
 			chan->xfer_len = 8;
-			bufptr = NULL;
 			break;
 
 		case DWC2_CONTROL_DATA:
@@ -684,7 +683,6 @@ static void *dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
 				chan->xfer_dma = hsotg->status_buf_dma;
 			else
 				chan->xfer_buf = hsotg->status_buf;
-			bufptr = NULL;
 			break;
 		}
 		break;
@@ -717,14 +715,6 @@ static void *dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
 
 		chan->xfer_len = frame_desc->length - qtd->isoc_split_offset;
 
-		/* For non-dword aligned buffers */
-		if (hsotg->core_params->dma_enable > 0 &&
-		    (chan->xfer_dma & 0x3))
-			bufptr = (u8 *)urb->buf + frame_desc->offset +
-					qtd->isoc_split_offset;
-		else
-			bufptr = NULL;
-
 		if (chan->xact_pos == DWC2_HCSPLT_XACTPOS_ALL) {
 			if (chan->xfer_len <= 188)
 				chan->xact_pos = DWC2_HCSPLT_XACTPOS_ALL;
@@ -733,63 +723,93 @@ static void *dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
 		}
 		break;
 	}
+}
+
+#define DWC2_USB_DMA_ALIGN 4
+
+struct dma_aligned_buffer {
+	void *kmalloc_ptr;
+	void *old_xfer_buffer;
+	u8 data[0];
+};
+
+static void dwc2_free_dma_aligned_buffer(struct urb *urb)
+{
+	struct dma_aligned_buffer *temp;
+
+	if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER))
+		return;
+
+	temp = container_of(urb->transfer_buffer,
+		struct dma_aligned_buffer, data);
 
-	return bufptr;
+	if (usb_urb_dir_in(urb))
+		memcpy(temp->old_xfer_buffer, temp->data,
+		       urb->transfer_buffer_length);
+	urb->transfer_buffer = temp->old_xfer_buffer;
+	kfree(temp->kmalloc_ptr);
+
+	urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER;
 }
 
-static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
-				   struct dwc2_host_chan *chan,
-				   struct dwc2_hcd_urb *urb, void *bufptr)
+static int dwc2_alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags)
 {
-	u32 buf_size;
-	struct urb *usb_urb;
-	struct usb_hcd *hcd;
+	struct dma_aligned_buffer *temp, *kmalloc_ptr;
+	size_t kmalloc_size;
 
-	if (!qh->dw_align_buf) {
-		if (chan->ep_type != USB_ENDPOINT_XFER_ISOC)
-			buf_size = hsotg->core_params->max_transfer_size;
-		else
-			/* 3072 = 3 max-size Isoc packets */
-			buf_size = 3072;
+	if (urb->num_sgs || urb->sg ||
+	    urb->transfer_buffer_length == 0 ||
+	    !((uintptr_t)urb->transfer_buffer & (DWC2_USB_DMA_ALIGN - 1)))
+		return 0;
 
-		qh->dw_align_buf = kmalloc(buf_size, GFP_ATOMIC | GFP_DMA);
-		if (!qh->dw_align_buf)
-			return -ENOMEM;
-		qh->dw_align_buf_size = buf_size;
-	}
+	/* Allocate a buffer with enough padding for alignment */
+	kmalloc_size = urb->transfer_buffer_length +
+		sizeof(struct dma_aligned_buffer) + DWC2_USB_DMA_ALIGN - 1;
 
-	if (chan->xfer_len) {
-		dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
-		usb_urb = urb->priv;
+	kmalloc_ptr = kmalloc(kmalloc_size, mem_flags);
+	if (!kmalloc_ptr)
+		return -ENOMEM;
 
-		if (usb_urb) {
-			if (usb_urb->transfer_flags &
-			    (URB_SETUP_MAP_SINGLE | URB_DMA_MAP_SG |
-			     URB_DMA_MAP_PAGE | URB_DMA_MAP_SINGLE)) {
-				hcd = dwc2_hsotg_to_hcd(hsotg);
-				usb_hcd_unmap_urb_for_dma(hcd, usb_urb);
-			}
-			if (!chan->ep_is_in)
-				memcpy(qh->dw_align_buf, bufptr,
-				       chan->xfer_len);
-		} else {
-			dev_warn(hsotg->dev, "no URB in dwc2_urb\n");
-		}
-	}
+	/* Position our struct dma_aligned_buffer such that data is aligned */
+	temp = PTR_ALIGN(kmalloc_ptr + 1, DWC2_USB_DMA_ALIGN) - 1;
+	temp->kmalloc_ptr = kmalloc_ptr;
+	temp->old_xfer_buffer = urb->transfer_buffer;
+	if (usb_urb_dir_out(urb))
+		memcpy(temp->data, urb->transfer_buffer,
+		       urb->transfer_buffer_length);
+	urb->transfer_buffer = temp->data;
 
-	qh->dw_align_buf_dma = dma_map_single(hsotg->dev,
-			qh->dw_align_buf, qh->dw_align_buf_size,
-			chan->ep_is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
-	if (dma_mapping_error(hsotg->dev, qh->dw_align_buf_dma)) {
-		dev_err(hsotg->dev, "can't map align_buf\n");
-		chan->align_buf = 0;
-		return -EINVAL;
-	}
+	urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER;
 
-	chan->align_buf = qh->dw_align_buf_dma;
 	return 0;
 }
 
+static int dwc2_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
+				      gfp_t mem_flags)
+{
+	int ret;
+
+	/* We assume setup_dma is always aligned; warn if not */
+	WARN_ON_ONCE(urb->setup_dma &&
+		     (urb->setup_dma & (DWC2_USB_DMA_ALIGN - 1)));
+
+	ret = dwc2_alloc_dma_aligned_buffer(urb, mem_flags);
+	if (ret)
+		return ret;
+
+	ret = usb_hcd_map_urb_for_dma(hcd, urb, mem_flags);
+	if (ret)
+		dwc2_free_dma_aligned_buffer(urb);
+
+	return ret;
+}
+
+static void dwc2_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
+{
+	usb_hcd_unmap_urb_for_dma(hcd, urb);
+	dwc2_free_dma_aligned_buffer(urb);
+}
+
 /**
  * dwc2_assign_and_init_hc() - Assigns transactions from a QTD to a free host
  * channel and initializes the host channel to perform the transactions. The
@@ -804,7 +824,6 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 	struct dwc2_host_chan *chan;
 	struct dwc2_hcd_urb *urb;
 	struct dwc2_qtd *qtd;
-	void *bufptr = NULL;
 
 	if (dbg_qh(qh))
 		dev_vdbg(hsotg->dev, "%s(%p,%p)\n", __func__, hsotg, qh);
@@ -866,16 +885,10 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		!dwc2_hcd_is_pipe_in(&urb->pipe_info))
 		urb->actual_length = urb->length;
 
-	if (hsotg->core_params->dma_enable > 0) {
+	if (hsotg->core_params->dma_enable > 0)
 		chan->xfer_dma = urb->dma + urb->actual_length;
-
-		/* For non-dword aligned case */
-		if (hsotg->core_params->dma_desc_enable <= 0 &&
-		    (chan->xfer_dma & 0x3))
-			bufptr = (u8 *)urb->buf + urb->actual_length;
-	} else {
+	else
 		chan->xfer_buf = (u8 *)urb->buf + urb->actual_length;
-	}
 
 	chan->xfer_len = urb->length - urb->actual_length;
 	chan->xfer_count = 0;
@@ -887,27 +900,7 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		chan->do_split = 0;
 
 	/* Set the transfer attributes */
-	bufptr = dwc2_hc_init_xfer(hsotg, chan, qtd, bufptr);
-
-	/* Non DWORD-aligned buffer case */
-	if (bufptr) {
-		dev_vdbg(hsotg->dev, "Non-aligned buffer\n");
-		if (dwc2_hc_setup_align_buf(hsotg, qh, chan, urb, bufptr)) {
-			dev_err(hsotg->dev,
-				"%s: Failed to allocate memory to handle non-dword aligned buffer\n",
-				__func__);
-			/* Add channel back to free list */
-			chan->align_buf = 0;
-			chan->multi_count = 0;
-			list_add_tail(&chan->hc_list_entry,
-				      &hsotg->free_hc_list);
-			qtd->in_process = 0;
-			qh->channel = NULL;
-			return -ENOMEM;
-		}
-	} else {
-		chan->align_buf = 0;
-	}
+	dwc2_hc_init_xfer(hsotg, chan, qtd);
 
 	if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
 	    chan->ep_type == USB_ENDPOINT_XFER_ISOC)
@@ -2971,6 +2964,9 @@ static struct hc_driver dwc2_hc_driver = {
 
 	.bus_suspend = _dwc2_hcd_suspend,
 	.bus_resume = _dwc2_hcd_resume,
+
+	.map_urb_for_dma	= dwc2_map_urb_for_dma,
+	.unmap_urb_for_dma	= dwc2_unmap_urb_for_dma,
 };
 
 /*
diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index 8f0a29cefdf7..42f2e4e233da 100644
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -75,8 +75,6 @@ struct dwc2_qh;
  *                      (micro)frame
  * @xfer_buf:           Pointer to current transfer buffer position
  * @xfer_dma:           DMA address of xfer_buf
- * @align_buf:          In Buffer DMA mode this will be used if xfer_buf is not
- *                      DWORD aligned
  * @xfer_len:           Total number of bytes to transfer
  * @xfer_count:         Number of bytes transferred so far
  * @start_pkt_count:    Packet count at start of transfer
@@ -133,7 +131,6 @@ struct dwc2_host_chan {
 
 	u8 *xfer_buf;
 	dma_addr_t xfer_dma;
-	dma_addr_t align_buf;
 	u32 xfer_len;
 	u32 xfer_count;
 	u16 start_pkt_count;
@@ -243,10 +240,6 @@ enum dwc2_transaction_type {
  * @frame_usecs:        Internal variable used by the microframe scheduler
  * @start_split_frame:  (Micro)frame at which last start split was initialized
  * @ntd:                Actual number of transfer descriptors in a list
- * @dw_align_buf:       Used instead of original buffer if its physical address
- *                      is not dword-aligned
- * @dw_align_buf_size:  Size of dw_align_buf
- * @dw_align_buf_dma:   DMA address for dw_align_buf
  * @qtd_list:           List of QTDs for this QH
  * @channel:            Host channel currently processing transfers for this QH
  * @qh_list_entry:      Entry for QH in either the periodic or non-periodic
@@ -279,9 +272,6 @@ struct dwc2_qh {
 	u16 frame_usecs[8];
 	u16 start_split_frame;
 	u16 ntd;
-	u8 *dw_align_buf;
-	int dw_align_buf_size;
-	dma_addr_t dw_align_buf_dma;
 	struct list_head qtd_list;
 	struct dwc2_host_chan *channel;
 	struct list_head qh_list_entry;
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index f8253803a050..352c98364317 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -472,18 +472,6 @@ static int dwc2_update_urb_state(struct dwc2_hsotg *hsotg,
 		xfer_length = urb->length - urb->actual_length;
 	}
 
-	/* Non DWORD-aligned buffer case handling */
-	if (chan->align_buf && xfer_length) {
-		dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
-		dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
-				chan->qh->dw_align_buf_size,
-				chan->ep_is_in ?
-				DMA_FROM_DEVICE : DMA_TO_DEVICE);
-		if (chan->ep_is_in)
-			memcpy(urb->buf + urb->actual_length,
-					chan->qh->dw_align_buf, xfer_length);
-	}
-
 	dev_vdbg(hsotg->dev, "urb->actual_length=%d xfer_length=%d\n",
 		 urb->actual_length, xfer_length);
 	urb->actual_length += xfer_length;
@@ -565,21 +553,6 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state(
 		frame_desc->status = 0;
 		frame_desc->actual_length = dwc2_get_actual_xfer_length(hsotg,
 					chan, chnum, qtd, halt_status, NULL);
-
-		/* Non DWORD-aligned buffer case handling */
-		if (chan->align_buf && frame_desc->actual_length) {
-			dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n",
-				 __func__);
-			dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
-					chan->qh->dw_align_buf_size,
-					chan->ep_is_in ?
-					DMA_FROM_DEVICE : DMA_TO_DEVICE);
-			if (chan->ep_is_in)
-				memcpy(urb->buf + frame_desc->offset +
-					qtd->isoc_split_offset,
-					chan->qh->dw_align_buf,
-					frame_desc->actual_length);
-		}
 		break;
 	case DWC2_HC_XFER_FRAME_OVERRUN:
 		urb->error_count++;
@@ -600,21 +573,6 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state(
 		frame_desc->actual_length = dwc2_get_actual_xfer_length(hsotg,
 					chan, chnum, qtd, halt_status, NULL);
 
-		/* Non DWORD-aligned buffer case handling */
-		if (chan->align_buf && frame_desc->actual_length) {
-			dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n",
-				 __func__);
-			dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
-					chan->qh->dw_align_buf_size,
-					chan->ep_is_in ?
-					DMA_FROM_DEVICE : DMA_TO_DEVICE);
-			if (chan->ep_is_in)
-				memcpy(urb->buf + frame_desc->offset +
-					qtd->isoc_split_offset,
-					chan->qh->dw_align_buf,
-					frame_desc->actual_length);
-		}
-
 		/* Skip whole frame */
 		if (chan->qh->do_split &&
 		    chan->ep_type == USB_ENDPOINT_XFER_ISOC && chan->ep_is_in &&
@@ -680,8 +638,6 @@ static void dwc2_deactivate_qh(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 	}
 
 no_qtd:
-	if (qh->channel)
-		qh->channel->align_buf = 0;
 	qh->channel = NULL;
 	dwc2_hcd_qh_deactivate(hsotg, qh, continue_split);
 }
@@ -946,14 +902,6 @@ static int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg,
 
 	frame_desc->actual_length += len;
 
-	if (chan->align_buf) {
-		dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
-		dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
-				chan->qh->dw_align_buf_size, DMA_FROM_DEVICE);
-		memcpy(qtd->urb->buf + frame_desc->offset +
-		       qtd->isoc_split_offset, chan->qh->dw_align_buf, len);
-	}
-
 	qtd->isoc_split_offset += len;
 
 	if (frame_desc->actual_length >= frame_desc->length) {
@@ -1176,19 +1124,6 @@ static void dwc2_update_urb_state_abn(struct dwc2_hsotg *hsotg,
 		xfer_length = urb->length - urb->actual_length;
 	}
 
-	/* Non DWORD-aligned buffer case handling */
-	if (chan->align_buf && xfer_length && chan->ep_is_in) {
-		dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
-		dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
-				chan->qh->dw_align_buf_size,
-				chan->ep_is_in ?
-				DMA_FROM_DEVICE : DMA_TO_DEVICE);
-		if (chan->ep_is_in)
-			memcpy(urb->buf + urb->actual_length,
-					chan->qh->dw_align_buf,
-					xfer_length);
-	}
-
 	urb->actual_length += xfer_length;
 
 	hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum));
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 27d402f680a3..e0933a9dfad7 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -232,13 +232,8 @@ struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
  */
 void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
-	if (qh->desc_list) {
+	if (qh->desc_list)
 		dwc2_hcd_qh_free_ddma(hsotg, qh);
-	} else {
-		/* kfree(NULL) is safe */
-		kfree(qh->dw_align_buf);
-		qh->dw_align_buf_dma = (dma_addr_t)0;
-	}
 	kfree(qh);
 }
 
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Thu Jan 28 18:19:58 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Thu, 28 Jan 2016 18:19:58 -0800
Subject: [PATCH v6 07/22] usb: dwc2: host: Add scheduler tracing
In-Reply-To: <1454034013-24657-1-git-send-email-dianders@chromium.org>
References: <1454034013-24657-1-git-send-email-dianders@chromium.org>
Message-ID: <1454034013-24657-8-git-send-email-dianders@chromium.org>

In preparation for future changes to the scheduler let's add some
tracing that makes it easy for us to see what's happening.  By default
this tracing will be off.

By changing "core.h" you can easily trace to ftrace, the console, or
nowhere.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Reviewed-by: Kever Yang <kever.yang at rock-chips.com>
Tested-by: Heiko Stuebner <heiko at sntech.de>
Tested-by: Stefan Wahren <stefan.wahren at i2se.com>
---
Changes in v6:
- Add Kever's Reviewed-by.
- Add Heiko's Tested-by.
- Add Stefan's Tested-by.

Changes in v5: None
Changes in v4:
- Retooled scheduler tracing a bit, so left off John's Ack from v3.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/core.h      | 20 ++++++++++++++++++++
 drivers/usb/dwc2/hcd.h       |  5 +++++
 drivers/usb/dwc2/hcd_intr.c  |  6 +++++-
 drivers/usb/dwc2/hcd_queue.c | 24 +++++++++++++++++++++++-
 4 files changed, 53 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 538cf38af0e4..18f9e4045643 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -44,6 +44,26 @@
 #include <linux/usb/phy.h>
 #include "hw.h"
 
+/*
+ * Suggested defines for tracers:
+ * - no_printk:    Disable tracing
+ * - pr_info:      Print this info to the console
+ * - trace_printk: Print this info to trace buffer (good for verbose logging)
+ */
+
+#define DWC2_TRACE_SCHEDULER		no_printk
+#define DWC2_TRACE_SCHEDULER_VB		no_printk
+
+/* Detailed scheduler tracing, but won't overwhelm console */
+#define dwc2_sch_dbg(hsotg, fmt, ...)					\
+	DWC2_TRACE_SCHEDULER(pr_fmt("%s: SCH: " fmt),			\
+			     dev_name(hsotg->dev), ##__VA_ARGS__)
+
+/* Verbose scheduler tracing */
+#define dwc2_sch_vdbg(hsotg, fmt, ...)					\
+	DWC2_TRACE_SCHEDULER_VB(pr_fmt("%s: SCH: " fmt),		\
+				dev_name(hsotg->dev), ##__VA_ARGS__)
+
 static inline u32 dwc2_readl(const void __iomem *addr)
 {
 	u32 value = __raw_readl(addr);
diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index 1b46e2e617cc..809bc4ff9116 100644
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -563,6 +563,11 @@ static inline u16 dwc2_frame_num_inc(u16 frame, u16 inc)
 	return (frame + inc) & HFNUM_MAX_FRNUM;
 }
 
+static inline u16 dwc2_frame_num_dec(u16 frame, u16 dec)
+{
+	return (frame + HFNUM_MAX_FRNUM + 1 - dec) & HFNUM_MAX_FRNUM;
+}
+
 static inline u16 dwc2_full_frame_num(u16 frame)
 {
 	return (frame & HFNUM_MAX_FRNUM) >> 3;
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index 577c91096a51..5d25a5ec9736 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -138,13 +138,17 @@ static void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
 	while (qh_entry != &hsotg->periodic_sched_inactive) {
 		qh = list_entry(qh_entry, struct dwc2_qh, qh_list_entry);
 		qh_entry = qh_entry->next;
-		if (dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number))
+		if (dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number)) {
+			dwc2_sch_vdbg(hsotg, "QH=%p ready fn=%04x, sch=%04x\n",
+				      qh, hsotg->frame_number, qh->sched_frame);
+
 			/*
 			 * Move QH to the ready list to be executed next
 			 * (micro)frame
 			 */
 			list_move_tail(&qh->qh_list_entry,
 				  &hsotg->periodic_sched_ready);
+		}
 	}
 	tr_type = dwc2_hcd_select_transactions(hsotg);
 	if (tr_type != DWC2_TRANSACTION_NONE)
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index bc632a72f611..0e9faa75593c 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -113,6 +113,9 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 		qh->sched_frame = dwc2_frame_num_inc(hsotg->frame_number,
 						     SCHEDULE_SLOP);
 		qh->interval = urb->interval;
+		dwc2_sch_dbg(hsotg, "QH=%p init sch=%04x, fn=%04x, int=%#x\n",
+			     qh, qh->sched_frame, hsotg->frame_number,
+			     qh->interval);
 #if 0
 		/* Increase interrupt polling rate for debugging */
 		if (qh->ep_type == USB_ENDPOINT_XFER_INT)
@@ -126,6 +129,11 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 			qh->interval *= 8;
 			qh->sched_frame |= 0x7;
 			qh->start_split_frame = qh->sched_frame;
+			dwc2_sch_dbg(hsotg,
+				     "QH=%p init*8 sch=%04x, fn=%04x, int=%#x\n",
+				     qh, qh->sched_frame, hsotg->frame_number,
+				     qh->interval);
+
 		}
 		dev_dbg(hsotg->dev, "interval=%d\n", qh->interval);
 	}
@@ -482,6 +490,8 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		if (frame >= 0) {
 			qh->sched_frame &= ~0x7;
 			qh->sched_frame |= (frame & 7);
+			dwc2_sch_dbg(hsotg, "QH=%p sched_p sch=%04x, uf=%d\n",
+				     qh, qh->sched_frame, frame);
 		}
 
 		if (status > 0)
@@ -583,10 +593,16 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 
 	if (!dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number) &&
 			!hsotg->frame_number) {
+		u16 new_frame;
+
 		dev_dbg(hsotg->dev,
 				"reset frame number counter\n");
-		qh->sched_frame = dwc2_frame_num_inc(hsotg->frame_number,
+		new_frame = dwc2_frame_num_inc(hsotg->frame_number,
 				SCHEDULE_SLOP);
+
+		dwc2_sch_vdbg(hsotg, "QH=%p reset sch=%04x=>%04x\n",
+			      qh, qh->sched_frame, new_frame);
+		qh->sched_frame = new_frame;
 	}
 
 	/* Add the new QH to the appropriate schedule */
@@ -652,6 +668,7 @@ static void dwc2_sched_periodic_split(struct dwc2_hsotg *hsotg,
 				      int sched_next_periodic_split)
 {
 	u16 incr;
+	u16 old_frame = qh->sched_frame;
 
 	if (sched_next_periodic_split) {
 		qh->sched_frame = frame_number;
@@ -677,6 +694,11 @@ static void dwc2_sched_periodic_split(struct dwc2_hsotg *hsotg,
 		qh->sched_frame |= 0x7;
 		qh->start_split_frame = qh->sched_frame;
 	}
+
+	dwc2_sch_vdbg(hsotg, "QH=%p next(%d) fn=%04x, sch=%04x=>%04x (%+d)\n",
+		      qh, sched_next_periodic_split, frame_number, old_frame,
+		      qh->sched_frame,
+		      dwc2_frame_num_dec(qh->sched_frame, old_frame));
 }
 
 /*
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Thu Jan 28 18:19:56 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Thu, 28 Jan 2016 18:19:56 -0800
Subject: [PATCH v6 05/22] usb: dwc2: host: Always add to the tail of queues
In-Reply-To: <1454034013-24657-1-git-send-email-dianders@chromium.org>
References: <1454034013-24657-1-git-send-email-dianders@chromium.org>
Message-ID: <1454034013-24657-6-git-send-email-dianders@chromium.org>

The queues the the dwc2 host controller used are truly queues.  That
means FIFO or first in first out.

Unfortunately though the code was iterating through these queues
starting from the head, some places in the code was adding things to the
queue by adding at the head instead of the tail.  That means last in
first out.  Doh.

Go through and just always add to the tail.

Doing this makes things much happier when I've got:
* 7-port USB 2.0 Single-TT hub
* - Microsoft 2.4 GHz Transceiver v7.0 dongle
* - Jabra speakerphone playing music

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Reviewed-by: Kever Yang <kever.yang at rock-chips.com>
Tested-by: Heiko Stuebner <heiko at sntech.de>
Tested-by: Stefan Wahren <stefan.wahren at i2se.com>
---
Changes in v6:
- Add Kever's Reviewed-by.
- Add Heiko's Tested-by.
- Add Stefan's Tested-by.

Changes in v5: None
Changes in v4:
- Always add to the tail of queues new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/hcd.c       | 11 ++++++-----
 drivers/usb/dwc2/hcd_ddma.c  |  4 ++--
 drivers/usb/dwc2/hcd_intr.c  |  4 ++--
 drivers/usb/dwc2/hcd_queue.c |  6 ++++--
 4 files changed, 14 insertions(+), 11 deletions(-)

diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index e2d2e9be366e..349194342c90 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -969,7 +969,8 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions(
 		 * periodic assigned schedule
 		 */
 		qh_ptr = qh_ptr->next;
-		list_move(&qh->qh_list_entry, &hsotg->periodic_sched_assigned);
+		list_move_tail(&qh->qh_list_entry,
+			       &hsotg->periodic_sched_assigned);
 		ret_val = DWC2_TRANSACTION_PERIODIC;
 	}
 
@@ -1002,8 +1003,8 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions(
 		 * non-periodic active schedule
 		 */
 		qh_ptr = qh_ptr->next;
-		list_move(&qh->qh_list_entry,
-			  &hsotg->non_periodic_sched_active);
+		list_move_tail(&qh->qh_list_entry,
+			       &hsotg->non_periodic_sched_active);
 
 		if (ret_val == DWC2_TRANSACTION_NONE)
 			ret_val = DWC2_TRANSACTION_NON_PERIODIC;
@@ -1176,8 +1177,8 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg)
 			 * Move the QH from the periodic assigned schedule to
 			 * the periodic queued schedule
 			 */
-			list_move(&qh->qh_list_entry,
-				  &hsotg->periodic_sched_queued);
+			list_move_tail(&qh->qh_list_entry,
+				       &hsotg->periodic_sched_queued);
 
 			/* done queuing high bandwidth */
 			hsotg->queuing_high_bandwidth = 0;
diff --git a/drivers/usb/dwc2/hcd_ddma.c b/drivers/usb/dwc2/hcd_ddma.c
index 36606fc33c0d..16b261cfa92d 100644
--- a/drivers/usb/dwc2/hcd_ddma.c
+++ b/drivers/usb/dwc2/hcd_ddma.c
@@ -1327,8 +1327,8 @@ void dwc2_hcd_complete_xfer_ddma(struct dwc2_hsotg *hsotg,
 			dwc2_hcd_qh_unlink(hsotg, qh);
 		} else {
 			/* Keep in assigned schedule to continue transfer */
-			list_move(&qh->qh_list_entry,
-				  &hsotg->periodic_sched_assigned);
+			list_move_tail(&qh->qh_list_entry,
+				       &hsotg->periodic_sched_assigned);
 			/*
 			 * If channel has been halted during giveback of urb
 			 * then prevent any new scheduling.
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index 99efc2bd1617..2c521c00e5e0 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -143,7 +143,7 @@ static void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
 			 * Move QH to the ready list to be executed next
 			 * (micro)frame
 			 */
-			list_move(&qh->qh_list_entry,
+			list_move_tail(&qh->qh_list_entry,
 				  &hsotg->periodic_sched_ready);
 	}
 	tr_type = dwc2_hcd_select_transactions(hsotg);
@@ -794,7 +794,7 @@ static void dwc2_halt_channel(struct dwc2_hsotg *hsotg,
 			 * halt to be queued when the periodic schedule is
 			 * processed.
 			 */
-			list_move(&chan->qh->qh_list_entry,
+			list_move_tail(&chan->qh->qh_list_entry,
 				  &hsotg->periodic_sched_assigned);
 
 			/*
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index e0933a9dfad7..bc632a72f611 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -732,9 +732,11 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 	     dwc2_frame_num_le(qh->sched_frame, frame_number)) ||
 	    (hsotg->core_params->uframe_sched <= 0 &&
 	     qh->sched_frame == frame_number))
-		list_move(&qh->qh_list_entry, &hsotg->periodic_sched_ready);
+		list_move_tail(&qh->qh_list_entry,
+			       &hsotg->periodic_sched_ready);
 	else
-		list_move(&qh->qh_list_entry, &hsotg->periodic_sched_inactive);
+		list_move_tail(&qh->qh_list_entry,
+			       &hsotg->periodic_sched_inactive);
 }
 
 /**
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Thu Jan 28 18:19:57 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Thu, 28 Jan 2016 18:19:57 -0800
Subject: [PATCH v6 06/22] usb: dwc2: host: fix split transfer schedule sequence
In-Reply-To: <1454034013-24657-1-git-send-email-dianders@chromium.org>
References: <1454034013-24657-1-git-send-email-dianders@chromium.org>
Message-ID: <1454034013-24657-7-git-send-email-dianders@chromium.org>

We're supposed to keep outstanding splits in order.  Keep track of a
list of the order of splits and process channel interrupts in that
order.

Without this change and the following setup:
* Rockchip rk3288 Chromebook, using port ff540000
  -> Pluggable 7-port Hub with Charging (powered)
     -> Microsoft Wireless Keyboard 2000 in port 1.
     -> Das Keyboard in port 2.

...I find that I get dropped keys on the Microsoft keyboard (I'm sure
there are other combinations that fail, but this documents my test).
Specifically I've been typing "hahahahahahaha" on the keyboard and often
see keys dropped or repeated.

After this change the above setup works properly.  This patch is based
on a previous patch proposed by Yunzhi Li ("usb: dwc2: hcd: fix periodic
transfer schedule sequence")

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Signed-off-by: Yunzhi Li <lyz at rock-chips.com>
Reviewed-by: Kever Yang <kever.yang at rock-chips.com>
Tested-by: Heiko Stuebner <heiko at sntech.de>
Tested-by: Kever Yang <kever.yang at rock-chips.com>
Tested-by: Stefan Wahren <stefan.wahren at i2se.com>
---
Changes in v6:
- Fix patch tags (hcd -> host)
- Add Kever's Reviewed-by.
- Add Kever's Tested-by.
- Add Heiko's Tested-by.
- Add Stefan's Tested-by.

Changes in v5:
- Move list maintenance to hcd.c to avoid gadget-only compile error

Changes in v4:
- fix split transfer schedule sequence new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/core.c     |  2 ++
 drivers/usb/dwc2/core.h     |  2 ++
 drivers/usb/dwc2/hcd.c      |  8 ++++++++
 drivers/usb/dwc2/hcd.h      |  2 ++
 drivers/usb/dwc2/hcd_intr.c | 17 +++++++++++++++++
 5 files changed, 31 insertions(+)

diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index 73f2771b7740..ed73b26818c0 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -1676,6 +1676,8 @@ void dwc2_hc_cleanup(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan)
 
 	chan->xfer_started = 0;
 
+	list_del_init(&chan->split_order_list_entry);
+
 	/*
 	 * Clear channel interrupt enables and any unhandled channel interrupt
 	 * conditions
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 7fb6434f4639..538cf38af0e4 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -657,6 +657,7 @@ struct dwc2_hregs_backup {
  *                      periodic_sched_ready because it must be rescheduled for
  *                      the next frame. Otherwise, the item moves to
  *                      periodic_sched_inactive.
+ * @split_order:        List keeping track of channels doing splits, in order.
  * @periodic_usecs:     Total bandwidth claimed so far for periodic transfers.
  *                      This value is in microseconds per (micro)frame. The
  *                      assumption is that all periodic transfers may occur in
@@ -780,6 +781,7 @@ struct dwc2_hsotg {
 	struct list_head periodic_sched_ready;
 	struct list_head periodic_sched_assigned;
 	struct list_head periodic_sched_queued;
+	struct list_head split_order;
 	u16 periodic_usecs;
 	u16 frame_usecs[8];
 	u16 frame_number;
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 349194342c90..0b6ebc7fff3f 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -1045,6 +1045,11 @@ static int dwc2_queue_transaction(struct dwc2_hsotg *hsotg,
 {
 	int retval = 0;
 
+	if (chan->do_split)
+		/* Put ourselves on the list to keep order straight */
+		list_move_tail(&chan->split_order_list_entry,
+			       &hsotg->split_order);
+
 	if (hsotg->core_params->dma_enable > 0) {
 		if (hsotg->core_params->dma_desc_enable > 0) {
 			if (!chan->xfer_started ||
@@ -3153,6 +3158,8 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
 	INIT_LIST_HEAD(&hsotg->periodic_sched_assigned);
 	INIT_LIST_HEAD(&hsotg->periodic_sched_queued);
 
+	INIT_LIST_HEAD(&hsotg->split_order);
+
 	/*
 	 * Create a host channel descriptor for each host channel implemented
 	 * in the controller. Initialize the channel descriptor array.
@@ -3166,6 +3173,7 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
 		if (channel == NULL)
 			goto error3;
 		channel->hc_num = i;
+		INIT_LIST_HEAD(&channel->split_order_list_entry);
 		hsotg->hc_ptr_array[i] = channel;
 	}
 
diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index 42f2e4e233da..1b46e2e617cc 100644
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -106,6 +106,7 @@ struct dwc2_qh;
  * @hc_list_entry:      For linking to list of host channels
  * @desc_list_addr:     Current QH's descriptor list DMA address
  * @desc_list_sz:       Current QH's descriptor list size
+ * @split_order_list_entry: List entry for keeping track of the order of splits
  *
  * This structure represents the state of a single host channel when acting in
  * host mode. It contains the data items needed to transfer packets to an
@@ -158,6 +159,7 @@ struct dwc2_host_chan {
 	struct list_head hc_list_entry;
 	dma_addr_t desc_list_addr;
 	u32 desc_list_sz;
+	struct list_head split_order_list_entry;
 };
 
 struct dwc2_hcd_pipe_info {
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index 2c521c00e5e0..577c91096a51 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -2067,6 +2067,7 @@ static void dwc2_hc_intr(struct dwc2_hsotg *hsotg)
 {
 	u32 haint;
 	int i;
+	struct dwc2_host_chan *chan, *chan_tmp;
 
 	haint = dwc2_readl(hsotg->regs + HAINT);
 	if (dbg_perio()) {
@@ -2075,6 +2076,22 @@ static void dwc2_hc_intr(struct dwc2_hsotg *hsotg)
 		dev_vdbg(hsotg->dev, "HAINT=%08x\n", haint);
 	}
 
+	/*
+	 * According to USB 2.0 spec section 11.18.8, a host must
+	 * issue complete-split transactions in a microframe for a
+	 * set of full-/low-speed endpoints in the same relative
+	 * order as the start-splits were issued in a microframe for.
+	 */
+	list_for_each_entry_safe(chan, chan_tmp, &hsotg->split_order,
+				 split_order_list_entry) {
+		int hc_num = chan->hc_num;
+
+		if (haint & (1 << hc_num)) {
+			dwc2_hc_n_intr(hsotg, hc_num);
+			haint &= ~(1 << hc_num);
+		}
+	}
+
 	for (i = 0; i < hsotg->core_params->host_channels; i++) {
 		if (haint & (1 << i))
 			dwc2_hc_n_intr(hsotg, i);
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Thu Jan 28 18:20:01 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Thu, 28 Jan 2016 18:20:01 -0800
Subject: [PATCH v6 10/22] usb: dwc2: host: Properly set the HFIR
In-Reply-To: <1454034013-24657-1-git-send-email-dianders@chromium.org>
References: <1454034013-24657-1-git-send-email-dianders@chromium.org>
Message-ID: <1454034013-24657-11-git-send-email-dianders@chromium.org>

According to the most up to date version of the dwc2 databook, the FRINT
field of the HFIR register should be programmed to:
* 125 us * (PHY clock freq for HS) - 1
* 1000 us * (PHY clock freq for FS/LS) - 1

This is opposed to older versions of the doc that claimed it should be:
* 125 us * (PHY clock freq for HS)
* 1000 us * (PHY clock freq for FS/LS)

In case you didn't spot it, the difference is the "- 1".

Let's add the "- 1" to match the newest user manual.  It's presumed that
the "- 1" should have always been there and that this was always a
documentation error.  If some hardware needs the "- 1" and other
hardware doesn't, we'll have to add a configuration parameter for it in
the future.

I checked things before and after this patch on rk3288 using a Total
Phase Beagle 5000 analyzer.

Before this patch, a low speed mouse shows constant Frame Timing Jitter
errors.  After this patch errors have gone away.

Before this patch SOF packets move forward about 1 us per 4 ms.  After
this patch the SOF packets move backward about 1 us per 255 ms.  Some
specific SOF timestamps from the analyzer are below.

Before:
  6.603.790
  6.603.916
  6.604.041
  6.604.166
  ...
  6.607.541
  6.607.667
  6.607.792
  6.607.917
  ...
  6.611.417
  6.611.543
  6.611.668
  6.611.793

After:
  6.215.159
  6.215.284
  6.215.408
  6.215.533
  6.215.658
  ...
  6.470.658
  6.470.783
  6.470.907
  ...
  6.726.032
  6.726.157
  6.725.281
  6.725.406

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Tested-by: Heiko Stuebner <heiko at sntech.de>
---
Changes in v6:
- Incorporated Properly set the HFIR patch to big series in v6
- Add Heiko's Tested-by.

Changes in v5: None
Changes in v4: None
Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/core.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index ed73b26818c0..a5db20f12ee4 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -2245,10 +2245,10 @@ u32 dwc2_calc_frame_interval(struct dwc2_hsotg *hsotg)
 
 	if ((hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT == HPRT0_SPD_HIGH_SPEED)
 		/* High speed case */
-		return 125 * clock;
+		return 125 * clock - 1;
 	else
 		/* FS/LS case */
-		return 1000 * clock;
+		return 1000 * clock - 1;
 }
 
 /**
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Thu Jan 28 18:20:02 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Thu, 28 Jan 2016 18:20:02 -0800
Subject: [PATCH v6 11/22] usb: dwc2: host: There's not really a TT for the
 root hub
In-Reply-To: <1454034013-24657-1-git-send-email-dianders@chromium.org>
References: <1454034013-24657-1-git-send-email-dianders@chromium.org>
Message-ID: <1454034013-24657-12-git-send-email-dianders@chromium.org>

I find that when I plug a full speed (NOT high speed) hub into a dwc2
port and then I plug a bunch of devices into that full speed hub that
dwc2 goes bat guano crazy.  Specifically, it just spews errors like this
in the console:
  usb usb1: clear tt 1 (9043) error -22

The specific test case I used looks like this:
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=dwc2/1p, 480M
    |__ Port 1: Dev 17, If 0, Class=Hub, Driver=hub/4p, 12M
        |__ Port 2: Dev 19, If 0, ..., Driver=usbhid, 1.5M
        |__ Port 4: Dev 20, If 0, ..., Driver=usbhid, 12M
        |__ Port 4: Dev 20, If 1, ..., Driver=usbhid, 12M
        |__ Port 4: Dev 20, If 2, ..., Driver=usbhid, 12M

Showing VID/PID:
 Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
 Bus 001 Device 017: ID 03eb:3301 Atmel Corp. at43301 4-Port Hub
 Bus 001 Device 020: ID 045e:0745 Microsoft Corp. Nano Transceiver ...
 Bus 001 Device 019: ID 046d:c404 Logitech, Inc. TrackMan Wheel

I spent a bunch of time trying to figure out why there are errors to
begin with.  I believe that the issue may be a hardware issue where the
transceiver sometimes accidentally sends a PREAMBLE packet if you send a
packet to a full speed device right after one to a low speed device.
Luckily the USB driver retries and the second time things work OK.

In any case, things kinda seem work despite the errors, except for the
"clear tt" spew mucking up my console.  Chalk it up for a win for
retries and robust protocols.

So getting back to the "clear tt" problem, it appears that we get those
because there's not actually a TT here to clear.  It's my understanding
that when dwc2 operates in low speed or full speed mode that there's no
real TT out there.  That makes all these attempts to "clear the TT"
somewhat meaningless and also causes the spew in the log.

Let's just skip all the useless TT clears.  Eventually we should root
cause the errors, but even if we do this is still a proper fix and is
likely to avoid the "clear tt" error in the future.

Note that hooking up a Full Speed USB Audio Device (Jabra 510) to this
same hub with the keyboard / trackball shows that even audio works over
this janky connection.  As a point to note, this particular change (skip
bogus TT clears) compared to just commenting out the dev_err() in
hub_tt_work() actually produces better audio.

Note: don't ask me where I got a full speed USB hub or whether the
massive amount of dust that accumulated on it while it was in my junk
box affected its funtionality.  Just smile and nod.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Changes in v6:
- There's not really a TT for the root hub new for v6

Changes in v5: None
Changes in v4: None
Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/hcd_intr.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index 5d25a5ec9736..fe44870f84eb 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -87,6 +87,7 @@ static void dwc2_hc_handle_tt_clear(struct dwc2_hsotg *hsotg,
 				    struct dwc2_host_chan *chan,
 				    struct dwc2_qtd *qtd)
 {
+	struct usb_device *root_hub = dwc2_hsotg_to_hcd(hsotg)->self.root_hub;
 	struct urb *usb_urb;
 
 	if (!chan->qh)
@@ -102,6 +103,15 @@ static void dwc2_hc_handle_tt_clear(struct dwc2_hsotg *hsotg,
 	if (!usb_urb || !usb_urb->dev || !usb_urb->dev->tt)
 		return;
 
+	/*
+	 * The root hub doesn't really have a TT, but Linux thinks it
+	 * does because how could you have a "high speed hub" that
+	 * directly talks directly to low speed devices without a TT?
+	 * It's all lies.  Lies, I tell you.
+	 */
+	if (usb_urb->dev->tt->hub == root_hub)
+		return;
+
 	if (qtd->urb->status != -EPIPE && qtd->urb->status != -EREMOTEIO) {
 		chan->qh->tt_buffer_dirty = 1;
 		if (usb_hub_clear_tt_buffer(usb_urb))
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Thu Jan 28 18:19:59 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Thu, 28 Jan 2016 18:19:59 -0800
Subject: [PATCH v6 08/22] usb: dwc2: host: Add a delay before releasing
 periodic bandwidth
In-Reply-To: <1454034013-24657-1-git-send-email-dianders@chromium.org>
References: <1454034013-24657-1-git-send-email-dianders@chromium.org>
Message-ID: <1454034013-24657-9-git-send-email-dianders@chromium.org>

We'd like to be able to use HCD_BH in order to speed up the dwc2 host
interrupt handler quite a bit.  However, according to the kernel doc for
usb_submit_urb() (specifically the part about "Reserved Bandwidth
Transfers"), we need to keep a reservation active as long as a device
driver keeps submitting.  That was easy to do when we gave back the URB
in the interrupt context: we just looked at when our queue was empty and
released the reserved bandwidth then.  ...but now we need a little more
complexity.

We'll follow EHCI's lead in commit 9118f9eb4f1e ("USB: EHCI: improve
interrupt qh unlink") and add a 5ms delay.  Since we don't have a whole
timer infrastructure in dwc2, we'll just add a timer per QH.  The
overhead for this is very small.

Note that the dwc2 scheduler is pretty broken (see future patches to fix
it).  This patch attempts to replicate all old behavior and just add the
proper delay.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Tested-by: Heiko Stuebner <heiko at sntech.de>
Tested-by: Stefan Wahren <stefan.wahren at i2se.com>
---
Changes in v6:
- Add Heiko's Tested-by.
- Add Stefan's Tested-by.

Changes in v5: None
Changes in v4:
- Moved periodic bandwidth release delay patch earlier again.

Changes in v3:
- Moved periodic bandwidth release delay patch later in the series.

Changes in v2:
- Periodic bandwidth release delay new for V2

 drivers/usb/dwc2/hcd.h       |   6 ++
 drivers/usb/dwc2/hcd_queue.c | 237 +++++++++++++++++++++++++++++++++----------
 2 files changed, 187 insertions(+), 56 deletions(-)

diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index 809bc4ff9116..79473ea35bd6 100644
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -215,6 +215,7 @@ enum dwc2_transaction_type {
 /**
  * struct dwc2_qh - Software queue head structure
  *
+ * @hsotg:              The HCD state structure for the DWC OTG controller
  * @ep_type:            Endpoint type. One of the following values:
  *                       - USB_ENDPOINT_XFER_CONTROL
  *                       - USB_ENDPOINT_XFER_BULK
@@ -252,13 +253,16 @@ enum dwc2_transaction_type {
  * @n_bytes:            Xfer Bytes array. Each element corresponds to a transfer
  *                      descriptor and indicates original XferSize value for the
  *                      descriptor
+ * @unreserve_timer:    Timer for releasing periodic reservation.
  * @tt_buffer_dirty     True if clear_tt_buffer_complete is pending
+ * @unreserve_pending:  True if we planned to unreserve but haven't yet.
  *
  * A Queue Head (QH) holds the static characteristics of an endpoint and
  * maintains a list of transfers (QTDs) for that endpoint. A QH structure may
  * be entered in either the non-periodic or periodic schedule.
  */
 struct dwc2_qh {
+	struct dwc2_hsotg *hsotg;
 	u8 ep_type;
 	u8 ep_is_in;
 	u16 maxp;
@@ -281,7 +285,9 @@ struct dwc2_qh {
 	dma_addr_t desc_list_dma;
 	u32 desc_list_sz;
 	u32 *n_bytes;
+	struct timer_list unreserve_timer;
 	unsigned tt_buffer_dirty:1;
+	unsigned unreserve_pending:1;
 };
 
 /**
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 0e9faa75593c..b9e4867e1afd 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -53,6 +53,94 @@
 #include "core.h"
 #include "hcd.h"
 
+/* Wait this long before releasing periodic reservation */
+#define DWC2_UNRESERVE_DELAY (msecs_to_jiffies(5))
+
+/**
+ * dwc2_do_unreserve() - Actually release the periodic reservation
+ *
+ * This function actually releases the periodic bandwidth that was reserved
+ * by the given qh.
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller
+ * @qh:    QH for the periodic transfer.
+ */
+static void dwc2_do_unreserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+	assert_spin_locked(&hsotg->lock);
+
+	WARN_ON(!qh->unreserve_pending);
+
+	/* No more unreserve pending--we're doing it */
+	qh->unreserve_pending = false;
+
+	if (WARN_ON(!list_empty(&qh->qh_list_entry)))
+		list_del_init(&qh->qh_list_entry);
+
+	/* Update claimed usecs per (micro)frame */
+	hsotg->periodic_usecs -= qh->usecs;
+
+	if (hsotg->core_params->uframe_sched > 0) {
+		int i;
+
+		for (i = 0; i < 8; i++) {
+			hsotg->frame_usecs[i] += qh->frame_usecs[i];
+			qh->frame_usecs[i] = 0;
+		}
+	} else {
+		/* Release periodic channel reservation */
+		hsotg->periodic_channels--;
+	}
+}
+
+/**
+ * dwc2_unreserve_timer_fn() - Timer function to release periodic reservation
+ *
+ * According to the kernel doc for usb_submit_urb() (specifically the part about
+ * "Reserved Bandwidth Transfers"), we need to keep a reservation active as
+ * long as a device driver keeps submitting.  Since we're using HCD_BH to give
+ * back the URB we need to give the driver a little bit of time before we
+ * release the reservation.  This worker is called after the appropriate
+ * delay.
+ *
+ * @work: Pointer to a qh unreserve_work.
+ */
+static void dwc2_unreserve_timer_fn(unsigned long data)
+{
+	struct dwc2_qh *qh = (struct dwc2_qh *)data;
+	struct dwc2_hsotg *hsotg = qh->hsotg;
+	unsigned long flags;
+
+	/*
+	 * Wait for the lock, or for us to be scheduled again.  We
+	 * could be scheduled again if:
+	 * - We started executing but didn't get the lock yet.
+	 * - A new reservation came in, but cancel didn't take effect
+	 *   because we already started executing.
+	 * - The timer has been kicked again.
+	 * In that case cancel and wait for the next call.
+	 */
+	while (!spin_trylock_irqsave(&hsotg->lock, flags)) {
+		if (timer_pending(&qh->unreserve_timer))
+			return;
+	}
+
+	/*
+	 * Might be no more unreserve pending if:
+	 * - We started executing but didn't get the lock yet.
+	 * - A new reservation came in, but cancel didn't take effect
+	 *   because we already started executing.
+	 *
+	 * We can't put this in the loop above because unreserve_pending needs
+	 * to be accessed under lock, so we can only check it once we got the
+	 * lock.
+	 */
+	if (qh->unreserve_pending)
+		dwc2_do_unreserve(hsotg, qh);
+
+	spin_unlock_irqrestore(&hsotg->lock, flags);
+}
+
 /**
  * dwc2_qh_init() - Initializes a QH structure
  *
@@ -71,6 +159,9 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 	dev_vdbg(hsotg->dev, "%s()\n", __func__);
 
 	/* Initialize QH */
+	qh->hsotg = hsotg;
+	setup_timer(&qh->unreserve_timer, dwc2_unreserve_timer_fn,
+		    (unsigned long)qh);
 	qh->ep_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
 	qh->ep_is_in = dwc2_hcd_is_pipe_in(&urb->pipe_info) ? 1 : 0;
 
@@ -240,6 +331,15 @@ struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
  */
 void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
+	/* Make sure any unreserve work is finished. */
+	if (del_timer_sync(&qh->unreserve_timer)) {
+		unsigned long flags;
+
+		spin_lock_irqsave(&hsotg->lock, flags);
+		dwc2_do_unreserve(hsotg, qh);
+		spin_unlock_irqrestore(&hsotg->lock, flags);
+	}
+
 	if (qh->desc_list)
 		dwc2_hcd_qh_free_ddma(hsotg, qh);
 	kfree(qh);
@@ -477,51 +577,74 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
 	int status;
 
-	if (hsotg->core_params->uframe_sched > 0) {
-		int frame = -1;
-
-		status = dwc2_find_uframe(hsotg, qh);
-		if (status == 0)
-			frame = 7;
-		else if (status > 0)
-			frame = status - 1;
-
-		/* Set the new frame up */
-		if (frame >= 0) {
-			qh->sched_frame &= ~0x7;
-			qh->sched_frame |= (frame & 7);
-			dwc2_sch_dbg(hsotg, "QH=%p sched_p sch=%04x, uf=%d\n",
-				     qh, qh->sched_frame, frame);
+	status = dwc2_check_max_xfer_size(hsotg, qh);
+	if (status) {
+		dev_dbg(hsotg->dev,
+			"%s: Channel max transfer size too small for periodic transfer\n",
+			__func__);
+		return status;
+	}
+
+	/* Cancel pending unreserve; if canceled OK, unreserve was pending */
+	if (del_timer(&qh->unreserve_timer))
+		WARN_ON(!qh->unreserve_pending);
+
+	/*
+	 * Only need to reserve if there's not an unreserve pending, since if an
+	 * unreserve is pending then by definition our old reservation is still
+	 * valid.  Unreserve might still be pending even if we didn't cancel if
+	 * dwc2_unreserve_timer_fn() already started.  Code in the timer handles
+	 * that case.
+	 */
+	if (!qh->unreserve_pending) {
+		if (hsotg->core_params->uframe_sched > 0) {
+			int frame = -1;
+
+			status = dwc2_find_uframe(hsotg, qh);
+			if (status == 0)
+				frame = 7;
+			else if (status > 0)
+				frame = status - 1;
+
+			/* Set the new frame up */
+			if (frame >= 0) {
+				qh->sched_frame &= ~0x7;
+				qh->sched_frame |= (frame & 7);
+				dwc2_sch_dbg(hsotg,
+					     "QH=%p sched_p sch=%04x, uf=%d\n",
+					     qh, qh->sched_frame, frame);
+			}
+
+			if (status > 0)
+				status = 0;
+		} else {
+			status = dwc2_periodic_channel_available(hsotg);
+			if (status) {
+				dev_info(hsotg->dev,
+					"%s: No host channel available for periodic transfer\n",
+					__func__);
+				return status;
+			}
+
+			status = dwc2_check_periodic_bandwidth(hsotg, qh);
 		}
 
-		if (status > 0)
-			status = 0;
-	} else {
-		status = dwc2_periodic_channel_available(hsotg);
 		if (status) {
-			dev_info(hsotg->dev,
-				 "%s: No host channel available for periodic transfer\n",
-				 __func__);
+			dev_dbg(hsotg->dev,
+				"%s: Insufficient periodic bandwidth for periodic transfer\n",
+				__func__);
 			return status;
 		}
 
-		status = dwc2_check_periodic_bandwidth(hsotg, qh);
-	}
+		if (hsotg->core_params->uframe_sched <= 0)
+			/* Reserve periodic channel */
+			hsotg->periodic_channels++;
 
-	if (status) {
-		dev_dbg(hsotg->dev,
-			"%s: Insufficient periodic bandwidth for periodic transfer\n",
-			__func__);
-		return status;
+		/* Update claimed usecs per (micro)frame */
+		hsotg->periodic_usecs += qh->usecs;
 	}
 
-	status = dwc2_check_max_xfer_size(hsotg, qh);
-	if (status) {
-		dev_dbg(hsotg->dev,
-			"%s: Channel max transfer size too small for periodic transfer\n",
-			__func__);
-		return status;
-	}
+	qh->unreserve_pending = 0;
 
 	if (hsotg->core_params->dma_desc_enable > 0)
 		/* Don't rely on SOF and start in ready schedule */
@@ -531,13 +654,6 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		list_add_tail(&qh->qh_list_entry,
 			      &hsotg->periodic_sched_inactive);
 
-	if (hsotg->core_params->uframe_sched <= 0)
-		/* Reserve periodic channel */
-		hsotg->periodic_channels++;
-
-	/* Update claimed usecs per (micro)frame */
-	hsotg->periodic_usecs += qh->usecs;
-
 	return status;
 }
 
@@ -551,22 +667,31 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 static void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg,
 				     struct dwc2_qh *qh)
 {
-	int i;
+	bool did_modify;
 
-	list_del_init(&qh->qh_list_entry);
+	assert_spin_locked(&hsotg->lock);
 
-	/* Update claimed usecs per (micro)frame */
-	hsotg->periodic_usecs -= qh->usecs;
+	/*
+	 * Schedule the unreserve to happen in a little bit.  Cases here:
+	 * - Unreserve worker might be sitting there waiting to grab the lock.
+	 *   In this case it will notice it's been schedule again and will
+	 *   quit.
+	 * - Unreserve worker might not be scheduled.
+	 *
+	 * We should never already be scheduled since dwc2_schedule_periodic()
+	 * should have canceled the scheduled unreserve timer (hence the
+	 * warning on did_modify).
+	 *
+	 * We add + 1 to the timer to guarantee that at least 1 jiffy has
+	 * passed (otherwise if the jiffy counter might tick right after we
+	 * read it and we'll get no delay).
+	 */
+	did_modify = mod_timer(&qh->unreserve_timer,
+			       jiffies + DWC2_UNRESERVE_DELAY + 1);
+	WARN_ON(did_modify);
+	qh->unreserve_pending = 1;
 
-	if (hsotg->core_params->uframe_sched > 0) {
-		for (i = 0; i < 8; i++) {
-			hsotg->frame_usecs[i] += qh->frame_usecs[i];
-			qh->frame_usecs[i] = 0;
-		}
-	} else {
-		/* Release periodic channel reservation */
-		hsotg->periodic_channels--;
-	}
+	list_del_init(&qh->qh_list_entry);
 }
 
 /**
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Thu Jan 28 18:20:06 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Thu, 28 Jan 2016 18:20:06 -0800
Subject: [PATCH v6 15/22] usb: dwc2: host: Split code out to make
 dwc2_do_reserve()
In-Reply-To: <1454034013-24657-1-git-send-email-dianders@chromium.org>
References: <1454034013-24657-1-git-send-email-dianders@chromium.org>
Message-ID: <1454034013-24657-16-git-send-email-dianders@chromium.org>

This no-op change splits code out of dwc2_schedule_periodic() into a
dwc2_do_reserve() function.  This makes it a little easier to follow the
logic.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Tested-by: Heiko Stuebner <heiko at sntech.de>
Tested-by: Stefan Wahren <stefan.wahren at i2se.com>
---
Changes in v6:
- Add Heiko's Tested-by.
- Add Stefan's Tested-by.

Changes in v5: None
Changes in v4:
- Split code out to make dwc2_do_reserve() new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/hcd_queue.c | 112 ++++++++++++++++++++++++++-----------------
 1 file changed, 67 insertions(+), 45 deletions(-)

diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 8a2067bc1e62..9ce407e5017d 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -245,6 +245,70 @@ static int dwc2_find_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 }
 
 /**
+ * dwc2_do_reserve() - Make a periodic reservation
+ *
+ * Try to allocate space in the periodic schedule.  Depending on parameters
+ * this might use the microframe scheduler or the dumb scheduler.
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller
+ * @qh:    QH for the periodic transfer.
+ *
+ * Returns: 0 upon success; error upon failure.
+ */
+static int dwc2_do_reserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+	int status;
+
+	if (hsotg->core_params->uframe_sched > 0) {
+		int frame = -1;
+
+		status = dwc2_find_uframe(hsotg, qh);
+		if (status == 0)
+			frame = 7;
+		else if (status > 0)
+			frame = status - 1;
+
+		/* Set the new frame up */
+		if (frame >= 0) {
+			qh->next_active_frame &= ~0x7;
+			qh->next_active_frame |= (frame & 7);
+			dwc2_sch_dbg(hsotg,
+				     "QH=%p sched_p nxt=%04x, uf=%d\n",
+				     qh, qh->next_active_frame, frame);
+		}
+
+		if (status > 0)
+			status = 0;
+	} else {
+		status = dwc2_periodic_channel_available(hsotg);
+		if (status) {
+			dev_info(hsotg->dev,
+				 "%s: No host channel available for periodic transfer\n",
+				 __func__);
+			return status;
+		}
+
+		status = dwc2_check_periodic_bandwidth(hsotg, qh);
+	}
+
+	if (status) {
+		dev_dbg(hsotg->dev,
+			"%s: Insufficient periodic bandwidth for periodic transfer\n",
+			__func__);
+		return status;
+	}
+
+	if (hsotg->core_params->uframe_sched <= 0)
+		/* Reserve periodic channel */
+		hsotg->periodic_channels++;
+
+	/* Update claimed usecs per (micro)frame */
+	hsotg->periodic_usecs += qh->host_us;
+
+	return 0;
+}
+
+/**
  * dwc2_do_unreserve() - Actually release the periodic reservation
  *
  * This function actually releases the periodic bandwidth that was reserved
@@ -393,51 +457,9 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 	 * that case.
 	 */
 	if (!qh->unreserve_pending) {
-		if (hsotg->core_params->uframe_sched > 0) {
-			int frame = -1;
-
-			status = dwc2_find_uframe(hsotg, qh);
-			if (status == 0)
-				frame = 7;
-			else if (status > 0)
-				frame = status - 1;
-
-			/* Set the new frame up */
-			if (frame >= 0) {
-				qh->next_active_frame &= ~0x7;
-				qh->next_active_frame |= (frame & 7);
-				dwc2_sch_dbg(hsotg,
-					     "QH=%p sched_p nxt=%04x, uf=%d\n",
-					     qh, qh->next_active_frame, frame);
-			}
-
-			if (status > 0)
-				status = 0;
-		} else {
-			status = dwc2_periodic_channel_available(hsotg);
-			if (status) {
-				dev_info(hsotg->dev,
-					"%s: No host channel available for periodic transfer\n",
-					__func__);
-				return status;
-			}
-
-			status = dwc2_check_periodic_bandwidth(hsotg, qh);
-		}
-
-		if (status) {
-			dev_dbg(hsotg->dev,
-				"%s: Insufficient periodic bandwidth for periodic transfer\n",
-				__func__);
+		status = dwc2_do_reserve(hsotg, qh);
+		if (status)
 			return status;
-		}
-
-		if (hsotg->core_params->uframe_sched <= 0)
-			/* Reserve periodic channel */
-			hsotg->periodic_channels++;
-
-		/* Update claimed usecs per (micro)frame */
-		hsotg->periodic_usecs += qh->host_us;
 	}
 
 	qh->unreserve_pending = 0;
@@ -450,7 +472,7 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		list_add_tail(&qh->qh_list_entry,
 			      &hsotg->periodic_sched_inactive);
 
-	return status;
+	return 0;
 }
 
 /**
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Thu Jan 28 18:20:10 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Thu, 28 Jan 2016 18:20:10 -0800
Subject: [PATCH v6 19/22] usb: dwc2: host: Add
 dwc2_hcd_get_future_frame_number() call
In-Reply-To: <1454034013-24657-1-git-send-email-dianders@chromium.org>
References: <1454034013-24657-1-git-send-email-dianders@chromium.org>
Message-ID: <1454034013-24657-20-git-send-email-dianders@chromium.org>

As we start getting more exact about our scheduling it's becoming more
and more important to know exactly how far through the current frame we
are.  This lets us make decisions about whether there's still time left
to start a new transaction in the current frame.

We'll add dwc2_hcd_get_future_frame_number() which will tell you what
the frame number will be a certain number of microseconds (us) from
now.  We can use this information to help decide if there's enough time
left in the frame for a transaction that will take a certain duration.

This is expected to be used by a future change ("usb: dwc2: host:
Properly set even/odd frame").

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Tested-by: Heiko Stuebner <heiko at sntech.de>
Tested-by: Stefan Wahren <stefan.wahren at i2se.com>
---
Changes in v6:
- Add Heiko's Tested-by.
- Add Stefan's Tested-by.

Changes in v5: None
Changes in v4:
- Add dwc2_hcd_get_future_frame_number() call new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/core.h |  4 ++++
 drivers/usb/dwc2/hcd.c  | 29 +++++++++++++++++++++++++++++
 2 files changed, 33 insertions(+)

diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 64d45a2053bb..52cbea28d0e9 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -1235,12 +1235,16 @@ static inline int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg,
 
 #if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
 extern int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg);
+extern int dwc2_hcd_get_future_frame_number(struct dwc2_hsotg *hsotg, int us);
 extern void dwc2_hcd_connect(struct dwc2_hsotg *hsotg);
 extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force);
 extern void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
 #else
 static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
 { return 0; }
+static inline int dwc2_hcd_get_future_frame_number(struct dwc2_hsotg *hsotg,
+						   int us)
+{ return 0; }
 static inline void dwc2_hcd_connect(struct dwc2_hsotg *hsotg) {}
 static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force) {}
 static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {}
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index f48da015fa5e..8edd0b45f41c 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -1947,6 +1947,35 @@ int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
 	return (hfnum & HFNUM_FRNUM_MASK) >> HFNUM_FRNUM_SHIFT;
 }
 
+int dwc2_hcd_get_future_frame_number(struct dwc2_hsotg *hsotg, int us)
+{
+	u32 hprt = dwc2_readl(hsotg->regs + HPRT0);
+	u32 hfir = dwc2_readl(hsotg->regs + HFIR);
+	u32 hfnum = dwc2_readl(hsotg->regs + HFNUM);
+	unsigned int us_per_frame;
+	unsigned int frame_number;
+	unsigned int remaining;
+	unsigned int interval;
+	unsigned int phy_clks;
+
+	/* High speed has 125 us per (micro) frame; others are 1 ms per */
+	us_per_frame = (hprt & HPRT0_SPD_MASK) ? 1000 : 125;
+
+	/* Extract fields */
+	frame_number = (hfnum & HFNUM_FRNUM_MASK) >> HFNUM_FRNUM_SHIFT;
+	remaining = (hfnum & HFNUM_FRREM_MASK) >> HFNUM_FRREM_SHIFT;
+	interval = (hfir & HFIR_FRINT_MASK) >> HFIR_FRINT_SHIFT;
+
+	/*
+	 * Number of phy clocks since the last tick of the frame number after
+	 * "us" has passed.
+	 */
+	phy_clks = (interval - remaining) +
+		   DIV_ROUND_UP(interval * us, us_per_frame);
+
+	return dwc2_frame_num_inc(frame_number, phy_clks / interval);
+}
+
 int dwc2_hcd_is_b_host(struct dwc2_hsotg *hsotg)
 {
 	return hsotg->op_state == OTG_STATE_B_HOST;
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Thu Jan 28 18:20:12 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Thu, 28 Jan 2016 18:20:12 -0800
Subject: [PATCH v6 21/22] usb: dwc2: host: Totally redo the microframe
 scheduler
In-Reply-To: <1454034013-24657-1-git-send-email-dianders@chromium.org>
References: <1454034013-24657-1-git-send-email-dianders@chromium.org>
Message-ID: <1454034013-24657-22-git-send-email-dianders@chromium.org>

This totally reimplements the microframe scheduler in dwc2 to attempt to
handle periodic splits properly.  The old code didn't even try, so this
was a significant effort since periodic splits are one of the most
complicated things in USB.

I've attempted to keep the old "don't use the microframe" schduler
around for now, but not sure it's needed.  It has also only been lightly
tested.

I think it's pretty certain that this scheduler isn't perfect and might
have some bugs, but it seems much better than what was there before.
With this change my stressful USB test (USB webcam + USB audio + some
keyboards) crackles less.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Tested-by: Heiko Stuebner <heiko at sntech.de>
Tested-by: Stefan Wahren <stefan.wahren at i2se.com>
---
Changes in v6:
- Removed incorrect limit on number of channels (Heiko Stuebner).
- Fixed order of operations bug in debug print.
- Add Heiko's Tested-by.
- Add Stefan's Tested-by.

Changes in v5:
- Moved defines outside of ifdef to avoid gadget-only compile error.

Changes in v4:
- Figured out what the microframe scheduler was supposed to do.
- Microframe rewrite is totally different from v3, hopefully more right.
- Microframe rewrite is later in the series now.

Changes in v3:
- The uframe scheduler patch is folded into optimization series.
- Optimize uframe scheduler "single uframe" case a little.
- uframe scheduler now atop logging patches.
- uframe scheduler now before delayed bandwidth release patches.
- Add defines like EARLY_FRAME_USEC
- Reorder dwc2_deschedule_periodic() in prep for future patches.
- uframe scheduler now shows real usefulness w/ future patches!
- Assuming single_tt is new for v3; not terribly well tested (yet).
- Keep track and use our uframe new for v3.

Changes in v2:
- Totally rewrote uframe scheduler again after writing test code.
- uframe scheduler atop delayed bandwidth release patches.

 drivers/usb/dwc2/core.h      |   85 ++-
 drivers/usb/dwc2/hcd.c       |   87 +++-
 drivers/usb/dwc2/hcd.h       |   79 ++-
 drivers/usb/dwc2/hcd_queue.c | 1170 ++++++++++++++++++++++++++++++++++++------
 4 files changed, 1250 insertions(+), 171 deletions(-)

diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 52cbea28d0e9..115925909390 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -592,6 +592,84 @@ struct dwc2_hregs_backup {
 	bool valid;
 };
 
+/*
+ * Constants related to high speed periodic scheduling
+ *
+ * We have a periodic schedule that is DWC2_HS_SCHEDULE_UFRAMES long.  From a
+ * reservation point of view it's assumed that the schedule goes right back to
+ * the beginning after the end of the schedule.
+ *
+ * What does that mean for scheduling things with a long interval?  It means
+ * we'll reserve time for them in every possible microframe that they could
+ * ever be scheduled in.  ...but we'll still only actually schedule them as
+ * often as they were requested.
+ *
+ * We keep our schedule in a "bitmap" structure.  This simplifies having
+ * to keep track of and merge intervals: we just let the bitmap code do most
+ * of the heavy lifting.  In a way scheduling is much like memory allocation.
+ *
+ * We schedule 100us per uframe or 80% of 125us (the maximum amount you're
+ * supposed to schedule for periodic transfers).  That's according to spec.
+ *
+ * Note that though we only schedule 80% of each microframe, the bitmap that we
+ * keep the schedule in is tightly packed (AKA it doesn't have 100us worth of
+ * space for each uFrame).
+ *
+ * Requirements:
+ * - DWC2_HS_SCHEDULE_UFRAMES must even divide 0x4000 (HFNUM_MAX_FRNUM + 1)
+ * - DWC2_HS_SCHEDULE_UFRAMES must be 8 times DWC2_LS_SCHEDULE_FRAMES (probably
+ *   could be any multiple of 8 times DWC2_LS_SCHEDULE_FRAMES, but there might
+ *   be bugs).  The 8 comes from the USB spec: number of microframes per frame.
+ */
+#define DWC2_US_PER_UFRAME		125
+#define DWC2_HS_PERIODIC_US_PER_UFRAME	100
+
+#define DWC2_HS_SCHEDULE_UFRAMES	8
+#define DWC2_HS_SCHEDULE_US		(DWC2_HS_SCHEDULE_UFRAMES * \
+					 DWC2_HS_PERIODIC_US_PER_UFRAME)
+
+/*
+ * Constants related to low speed scheduling
+ *
+ * For high speed we schedule every 1us.  For low speed that's a bit overkill,
+ * so we make up a unit called a "slice" that's worth 25us.  There are 40
+ * slices in a full frame and we can schedule 36 of those (90%) for periodic
+ * transfers.
+ *
+ * Our low speed schedule can be as short as 1 frame or could be longer.  When
+ * we only schedule 1 frame it means that we'll need to reserve a time every
+ * frame even for things that only transfer very rarely, so something that runs
+ * every 2048 frames will get time reserved in every frame.  Our low speed
+ * schedule can be longer and we'll be able to handle more overlap, but that
+ * will come at increased memory cost and increased time to schedule.
+ *
+ * Note: one other advantage of a short low speed schedule is that if we mess
+ * up and miss scheduling we can jump in and use any of the slots that we
+ * happened to reserve.
+ *
+ * With 25 us per slice and 1 frame in the schedule, we only need 4 bytes for
+ * the schedule.  There will be one schedule per TT.
+ *
+ * Requirements:
+ * - DWC2_US_PER_SLICE must evenly divide DWC2_LS_PERIODIC_US_PER_FRAME.
+ */
+#define DWC2_US_PER_SLICE	25
+#define DWC2_SLICES_PER_UFRAME	(DWC2_US_PER_UFRAME / DWC2_US_PER_SLICE)
+
+#define DWC2_ROUND_US_TO_SLICE(us) \
+				(DIV_ROUND_UP((us), DWC2_US_PER_SLICE) * \
+				 DWC2_US_PER_SLICE)
+
+#define DWC2_LS_PERIODIC_US_PER_FRAME \
+				900
+#define DWC2_LS_PERIODIC_SLICES_PER_FRAME \
+				(DWC2_LS_PERIODIC_US_PER_FRAME / \
+				 DWC2_US_PER_SLICE)
+
+#define DWC2_LS_SCHEDULE_FRAMES	1
+#define DWC2_LS_SCHEDULE_SLICES	(DWC2_LS_SCHEDULE_FRAMES * \
+				 DWC2_LS_PERIODIC_SLICES_PER_FRAME)
+
 /**
  * struct dwc2_hsotg - Holds the state of the driver, including the non-periodic
  * and periodic schedules
@@ -682,7 +760,9 @@ struct dwc2_hregs_backup {
  *                      This value is in microseconds per (micro)frame. The
  *                      assumption is that all periodic transfers may occur in
  *                      the same (micro)frame.
- * @frame_usecs:        Internal variable used by the microframe scheduler
+ * @hs_periodic_bitmap: Bitmap used by the microframe scheduler any time the
+ *                      host is in high speed mode; low speed schedules are
+ *                      stored elsewhere since we need one per TT.
  * @frame_number:       Frame number read from the core at SOF. The value ranges
  *                      from 0 to HFNUM_MAX_FRNUM.
  * @periodic_qh_count:  Count of periodic QHs, if using several eps. Used for
@@ -803,7 +883,8 @@ struct dwc2_hsotg {
 	struct list_head periodic_sched_queued;
 	struct list_head split_order;
 	u16 periodic_usecs;
-	u16 frame_usecs[8];
+	unsigned long hs_periodic_bitmap[
+		DIV_ROUND_UP(DWC2_HS_SCHEDULE_US, BITS_PER_LONG)];
 	u16 frame_number;
 	u16 periodic_qh_count;
 	bool bus_suspended;
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 8edd0b45f41c..2b5a706e7c32 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -2252,6 +2252,90 @@ void dwc2_host_hub_info(struct dwc2_hsotg *hsotg, void *context, int *hub_addr,
 	*hub_port = urb->dev->ttport;
 }
 
+/**
+ * dwc2_host_get_tt_info() - Get the dwc2_tt associated with context
+ *
+ * This will get the dwc2_tt structure (and ttport) associated with the given
+ * context (which is really just a struct urb pointer).
+ *
+ * The first time this is called for a given TT we allocate memory for our
+ * structure.  When everyone is done and has called dwc2_host_put_tt_info()
+ * then the refcount for the structure will go to 0 and we'll free it.
+ *
+ * @hsotg:     The HCD state structure for the DWC OTG controller.
+ * @qh:        The QH structure.
+ * @context:   The priv pointer from a struct dwc2_hcd_urb.
+ * @mem_flags: Flags for allocating memory.
+ * @ttport:    We'll return this device's port number here.  That's used to
+ *             reference into the bitmap if we're on a multi_tt hub.
+ *
+ * Return: a pointer to a struct dwc2_tt.  Don't forget to call
+ *         dwc2_host_put_tt_info()!  Returns NULL upon memory alloc failure.
+ */
+
+struct dwc2_tt *dwc2_host_get_tt_info(struct dwc2_hsotg *hsotg, void *context,
+				      gfp_t mem_flags, int *ttport)
+{
+	struct urb *urb = context;
+	struct dwc2_tt *dwc_tt = NULL;
+
+	if (urb->dev->tt) {
+		*ttport = urb->dev->ttport;
+
+		dwc_tt = urb->dev->tt->hcpriv;
+		if (dwc_tt == NULL) {
+			size_t bitmap_size;
+
+			/*
+			 * For single_tt we need one schedule.  For multi_tt
+			 * we need one per port.
+			 */
+			bitmap_size = DWC2_ELEMENTS_PER_LS_BITMAP *
+				      sizeof(dwc_tt->periodic_bitmaps[0]);
+			if (urb->dev->tt->multi)
+				bitmap_size *= urb->dev->tt->hub->maxchild;
+
+			dwc_tt = kzalloc(sizeof(*dwc_tt) + bitmap_size,
+					 mem_flags);
+			if (dwc_tt == NULL)
+				return NULL;
+
+			dwc_tt->usb_tt = urb->dev->tt;
+			dwc_tt->usb_tt->hcpriv = dwc_tt;
+		}
+
+		dwc_tt->refcount++;
+	}
+
+	return dwc_tt;
+}
+
+/**
+ * dwc2_host_put_tt_info() - Put the dwc2_tt from dwc2_host_get_tt_info()
+ *
+ * Frees resources allocated by dwc2_host_get_tt_info() if all current holders
+ * of the structure are done.
+ *
+ * It's OK to call this with NULL.
+ *
+ * @hsotg:     The HCD state structure for the DWC OTG controller.
+ * @dwc_tt:    The pointer returned by dwc2_host_get_tt_info.
+ */
+void dwc2_host_put_tt_info(struct dwc2_hsotg *hsotg, struct dwc2_tt *dwc_tt)
+{
+	/* Model kfree and make put of NULL a no-op */
+	if (dwc_tt == NULL)
+		return;
+
+	WARN_ON(dwc_tt->refcount < 1);
+
+	dwc_tt->refcount--;
+	if (!dwc_tt->refcount) {
+		dwc_tt->usb_tt->hcpriv = NULL;
+		kfree(dwc_tt);
+	}
+}
+
 int dwc2_host_get_speed(struct dwc2_hsotg *hsotg, void *context)
 {
 	struct urb *urb = context;
@@ -3197,9 +3281,6 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
 		hsotg->hc_ptr_array[i] = channel;
 	}
 
-	if (hsotg->core_params->uframe_sched > 0)
-		dwc2_hcd_init_usecs(hsotg);
-
 	/* Initialize hsotg start work */
 	INIT_DELAYED_WORK(&hsotg->start_work, dwc2_hcd_start_func);
 
diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index fd266ac53a28..140b1511a131 100644
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -212,6 +212,43 @@ enum dwc2_transaction_type {
 	DWC2_TRANSACTION_ALL,
 };
 
+/* The number of elements per LS bitmap (per port on multi_tt) */
+#define DWC2_ELEMENTS_PER_LS_BITMAP	DIV_ROUND_UP(DWC2_LS_SCHEDULE_SLICES, \
+						     BITS_PER_LONG)
+
+/**
+ * struct dwc2_tt - dwc2 data associated with a usb_tt
+ *
+ * @refcount:           Number of Queue Heads (QHs) holding a reference.
+ * @usb_tt:             Pointer back to the official usb_tt.
+ * @periodic_bitmaps:   Bitmap for which parts of the 1ms frame are accounted
+ *                      for already.  Each is DWC2_ELEMENTS_PER_LS_BITMAP
+ *			elements (so sizeof(long) times that in bytes).
+ *
+ * This structure is stored in the hcpriv of the official usb_tt.
+ */
+struct dwc2_tt {
+	int refcount;
+	struct usb_tt *usb_tt;
+	unsigned long periodic_bitmaps[];
+};
+
+/**
+ * struct dwc2_hs_transfer_time - Info about a transfer on the high speed bus.
+ *
+ * @start_schedule_usecs:  The start time on the main bus schedule.  Note that
+ *                         the main bus schedule is tightly packed and this
+ *			   time should be interpreted as tightly packed (so
+ *			   uFrame 0 starts at 0 us, uFrame 1 starts at 100 us
+ *			   instead of 125 us).
+ * @duration_us:           How long this transfer goes.
+ */
+
+struct dwc2_hs_transfer_time {
+	u32 start_schedule_us;
+	u16 duration_us;
+};
+
 /**
  * struct dwc2_qh - Software queue head structure
  *
@@ -237,18 +274,33 @@ enum dwc2_transaction_type {
  * @td_first:           Index of first activated isochronous transfer descriptor
  * @td_last:            Index of last activated isochronous transfer descriptor
  * @host_us:            Bandwidth in microseconds per transfer as seen by host
+ * @device_us:          Bandwidth in microseconds per transfer as seen by device
  * @host_interval:      Interval between transfers as seen by the host.  If
  *                      the host is high speed and the device is low speed this
  *                      will be 8 times device interval.
- * @next_active_frame:  (Micro)frame before we next need to put something on
+ * @device_interval:    Interval between transfers as seen by the device.
+ *                      interval.
+ * @next_active_frame:  (Micro)frame _before_ we next need to put something on
  *                      the bus.  We'll move the qh to active here.  If the
  *                      host is in high speed mode this will be a uframe.  If
  *                      the host is in low speed mode this will be a full frame.
  * @start_active_frame: If we are partway through a split transfer, this will be
  *			what next_active_frame was when we started.  Otherwise
  *			it should always be the same as next_active_frame.
- * @assigned_uframe:    The uframe (0 -7) assigned by dwc2_find_uframe().
- * @frame_usecs:        Internal variable used by the microframe scheduler
+ * @num_hs_transfers:   Number of transfers in hs_transfers.
+ *                      Normally this is 1 but can be more than one for splits.
+ *                      Always >= 1 unless the host is in low/full speed mode.
+ * @hs_transfers:       Transfers that are scheduled as seen by the high speed
+ *                      bus.  Not used if host is in low or full speed mode (but
+ *                      note that it IS USED if the device is low or full speed
+ *                      as long as the HOST is in high speed mode).
+ * @ls_start_schedule_slice: Start time (in slices) on the low speed bus
+ *                           schedule that's being used by this device.  This
+ *			     will be on the periodic_bitmap in a
+ *                           "struct dwc2_tt".  Not used if this device is high
+ *                           speed.  Note that this is in "schedule slice" which
+ *                           is tightly packed.
+ * @ls_duration_us:     Duration on the low speed bus schedule.
  * @ntd:                Actual number of transfer descriptors in a list
  * @qtd_list:           List of QTDs for this QH
  * @channel:            Host channel currently processing transfers for this QH
@@ -261,8 +313,12 @@ enum dwc2_transaction_type {
  *                      descriptor and indicates original XferSize value for the
  *                      descriptor
  * @unreserve_timer:    Timer for releasing periodic reservation.
+ * @dwc2_tt:            Pointer to our tt info (or NULL if no tt).
+ * @ttport:             Port number within our tt.
  * @tt_buffer_dirty     True if clear_tt_buffer_complete is pending
  * @unreserve_pending:  True if we planned to unreserve but haven't yet.
+ * @schedule_low_speed: True if we have a low/full speed component (either the
+ *			host is in low/full speed mode or do_split).
  *
  * A Queue Head (QH) holds the static characteristics of an endpoint and
  * maintains a list of transfers (QTDs) for that endpoint. A QH structure may
@@ -280,11 +336,14 @@ struct dwc2_qh {
 	u8 td_first;
 	u8 td_last;
 	u16 host_us;
+	u16 device_us;
 	u16 host_interval;
+	u16 device_interval;
 	u16 next_active_frame;
 	u16 start_active_frame;
-	u16 assigned_uframe;
-	u16 frame_usecs[8];
+	s16 num_hs_transfers;
+	struct dwc2_hs_transfer_time hs_transfers[DWC2_HS_SCHEDULE_UFRAMES];
+	u32 ls_start_schedule_slice;
 	u16 ntd;
 	struct list_head qtd_list;
 	struct dwc2_host_chan *channel;
@@ -294,8 +353,11 @@ struct dwc2_qh {
 	u32 desc_list_sz;
 	u32 *n_bytes;
 	struct timer_list unreserve_timer;
+	struct dwc2_tt *dwc_tt;
+	int ttport;
 	unsigned tt_buffer_dirty:1;
 	unsigned unreserve_pending:1;
+	unsigned schedule_low_speed:1;
 };
 
 /**
@@ -462,7 +524,6 @@ extern void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg,
 
 /* Schedule Queue Functions */
 /* Implemented in hcd_queue.c */
-extern void dwc2_hcd_init_usecs(struct dwc2_hsotg *hsotg);
 extern struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
 					  struct dwc2_hcd_urb *urb,
 					  gfp_t mem_flags);
@@ -728,6 +789,12 @@ extern void dwc2_host_start(struct dwc2_hsotg *hsotg);
 extern void dwc2_host_disconnect(struct dwc2_hsotg *hsotg);
 extern void dwc2_host_hub_info(struct dwc2_hsotg *hsotg, void *context,
 			       int *hub_addr, int *hub_port);
+extern struct dwc2_tt *dwc2_host_get_tt_info(struct dwc2_hsotg *hsotg,
+					     void *context, gfp_t mem_flags,
+					     int *ttport);
+
+extern void dwc2_host_put_tt_info(struct dwc2_hsotg *hsotg,
+				  struct dwc2_tt *dwc_tt);
 extern int dwc2_host_get_speed(struct dwc2_hsotg *hsotg, void *context);
 extern void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
 			       int status);
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 5f909747b5a4..5ea460d886ba 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -136,116 +136,933 @@ static int dwc2_check_periodic_bandwidth(struct dwc2_hsotg *hsotg,
 }
 
 /**
- * Microframe scheduler
- * track the total use in hsotg->frame_usecs
- * keep each qh use in qh->frame_usecs
- * when surrendering the qh then donate the time back
+ * pmap_schedule() - Schedule time in a periodic bitmap (pmap).
+ *
+ * @map:             The bitmap representing the schedule; will be updated
+ *                   upon success.
+ * @bits_per_period: The schedule represents several periods.  This is how many
+ *                   bits are in each period.  It's assumed that the beginning
+ *                   of the schedule will repeat after its end.
+ * @periods_in_map:  The number of periods in the schedule.
+ * @num_bits:        The number of bits we need per period we want to reserve
+ *                   in this function call.
+ * @interval:        How often we need to be scheduled for the reservation this
+ *                   time.  1 means every period.  2 means every other period.
+ *                   ...you get the picture?
+ * @start:           The bit number to start at.  Normally 0.  Must be within
+ *                   the interval or we return failure right away.
+ * @only_one_period: Normally we'll allow picking a start anywhere within the
+ *                   first interval, since we can still make all repetition
+ *                   requirements by doing that.  However, if you pass true
+ *                   here then we'll return failure if we can't fit within
+ *                   the period that "start" is in.
+ *
+ * The idea here is that we want to schedule time for repeating events that all
+ * want the same resource.  The resource is divided into fixed-sized periods
+ * and the events want to repeat every "interval" periods.  The schedule
+ * granularity is one bit.
+ *
+ * To keep things "simple", we'll represent our schedule with a bitmap that
+ * contains a fixed number of periods.  This gets rid of a lot of complexity
+ * but does mean that we need to handle things specially (and non-ideally) if
+ * the number of the periods in the schedule doesn't match well with the
+ * intervals that we're trying to schedule.
+ *
+ * Here's an explanation of the scheme we'll implement, assuming 8 periods.
+ * - If interval is 1, we need to take up space in each of the 8
+ *   periods we're scheduling.  Easy.
+ * - If interval is 2, we need to take up space in half of the
+ *   periods.  Again, easy.
+ * - If interval is 3, we actually need to fall back to interval 1.
+ *   Why?  Because we might need time in any period.  AKA for the
+ *   first 8 periods, we'll be in slot 0, 3, 6.  Then we'll be
+ *   in slot 1, 4, 7.  Then we'll be in 2, 5.  Then we'll be back to
+ *   0, 3, and 6.  Since we could be in any frame we need to reserve
+ *   for all of them.  Sucks, but that's what you gotta do.  Note that
+ *   if we were instead scheduling 8 * 3 = 24 we'd do much better, but
+ *   then we need more memory and time to do scheduling.
+ * - If interval is 4, easy.
+ * - If interval is 5, we again need interval 1.  The schedule will be
+ *   0, 5, 2, 7, 4, 1, 6, 3, 0
+ * - If interval is 6, we need interval 2.  0, 6, 4, 2.
+ * - If interval is 7, we need interval 1.
+ * - If interval is 8, we need interval 8.
+ *
+ * If you do the math, you'll see that we need to pretend that interval is
+ * equal to the greatest_common_divisor(interval, periods_in_map).
+ *
+ * Note that at the moment this function tends to front-pack the schedule.
+ * In some cases that's really non-ideal (it's hard to schedule things that
+ * need to repeat every period).  In other cases it's perfect (you can easily
+ * schedule bigger, less often repeating things).
+ *
+ * Here's the algorithm in action (8 periods, 5 bits per period):
+ *  |**   |     |**   |     |**   |     |**   |     |   OK 2 bits, intv 2 at 0
+ *  |*****|  ***|*****|  ***|*****|  ***|*****|  ***|   OK 3 bits, intv 3 at 2
+ *  |*****|* ***|*****|  ***|*****|* ***|*****|  ***|   OK 1 bits, intv 4 at 5
+ *  |**   |*    |**   |     |**   |*    |**   |     | Remv 3 bits, intv 3 at 2
+ *  |***  |*    |***  |     |***  |*    |***  |     |   OK 1 bits, intv 6 at 2
+ *  |**** |*  * |**** |   * |**** |*  * |**** |   * |   OK 1 bits, intv 1 at 3
+ *  |**** |**** |**** | *** |**** |**** |**** | *** |   OK 2 bits, intv 2 at 6
+ *  |*****|*****|*****| ****|*****|*****|*****| ****|   OK 1 bits, intv 1 at 4
+ *  |*****|*****|*****| ****|*****|*****|*****| ****| FAIL 1 bits, intv 1
+ *  |  ***|*****|  ***| ****|  ***|*****|  ***| ****| Remv 2 bits, intv 2 at 0
+ *  |  ***| ****|  ***| ****|  ***| ****|  ***| ****| Remv 1 bits, intv 4 at 5
+ *  |   **| ****|   **| ****|   **| ****|   **| ****| Remv 1 bits, intv 6 at 2
+ *  |    *| ** *|    *| ** *|    *| ** *|    *| ** *| Remv 1 bits, intv 1 at 3
+ *  |    *|    *|    *|    *|    *|    *|    *|    *| Remv 2 bits, intv 2 at 6
+ *  |     |     |     |     |     |     |     |     | Remv 1 bits, intv 1 at 4
+ *  |**   |     |**   |     |**   |     |**   |     |   OK 2 bits, intv 2 at 0
+ *  |***  |     |**   |     |***  |     |**   |     |   OK 1 bits, intv 4 at 2
+ *  |*****|     |** **|     |*****|     |** **|     |   OK 2 bits, intv 2 at 3
+ *  |*****|*    |** **|     |*****|*    |** **|     |   OK 1 bits, intv 4 at 5
+ *  |*****|***  |** **| **  |*****|***  |** **| **  |   OK 2 bits, intv 2 at 6
+ *  |*****|*****|** **| ****|*****|*****|** **| ****|   OK 2 bits, intv 2 at 8
+ *  |*****|*****|*****| ****|*****|*****|*****| ****|   OK 1 bits, intv 4 at 12
+ *
+ * This function is pretty generic and could be easily abstracted if anything
+ * needed similar scheduling.
+ *
+ * Returns either -ENOSPC or a >= 0 start bit which should be passed to the
+ * unschedule routine.  The map bitmap will be updated on a non-error result.
  */
-static const unsigned short max_uframe_usecs[] = {
-	100, 100, 100, 100, 100, 100, 30, 0
-};
+static int pmap_schedule(unsigned long *map, int bits_per_period,
+			 int periods_in_map, int num_bits,
+			 int interval, int start, bool only_one_period)
+{
+	int interval_bits;
+	int to_reserve;
+	int first_end;
+	int i;
+
+	if (num_bits > bits_per_period)
+		return -ENOSPC;
+
+	/* Adjust interval as per description */
+	interval = gcd(interval, periods_in_map);
+
+	interval_bits = bits_per_period * interval;
+	to_reserve = periods_in_map / interval;
+
+	/* If start has gotten us past interval then we can't schedule */
+	if (start >= interval_bits)
+		return -ENOSPC;
+
+	if (only_one_period)
+		/* Must fit within same period as start; end at begin of next */
+		first_end = (start / bits_per_period + 1) * bits_per_period;
+	else
+		/* Can fit anywhere in the first interval */
+		first_end = interval_bits;
+
+	/*
+	 * We'll try to pick the first repetition, then see if that time
+	 * is free for each of the subsequent repetitions.  If it's not
+	 * we'll adjust the start time for the next search of the first
+	 * repetition.
+	 */
+	while (start + num_bits <= first_end) {
+		int end;
+
+		/* Need to stay within this period */
+		end = (start / bits_per_period + 1) * bits_per_period;
+
+		/* Look for num_bits us in this microframe starting at start */
+		start = bitmap_find_next_zero_area(map, end, start, num_bits,
+						   0);
+
+		/*
+		 * We should get start >= end if we fail.  We might be
+		 * able to check the next microframe depending on the
+		 * interval, so continue on (start already updated).
+		 */
+		if (start >= end) {
+			start = end;
+			continue;
+		}
+
+		/* At this point we have a valid point for first one */
+		for (i = 1; i < to_reserve; i++) {
+			int ith_start = start + interval_bits * i;
+			int ith_end = end + interval_bits * i;
+			int ret;
+
+			/* Use this as a dumb "check if bits are 0" */
+			ret = bitmap_find_next_zero_area(
+				map, ith_start + num_bits, ith_start, num_bits,
+				0);
+
+			/* We got the right place, continue checking */
+			if (ret == ith_start)
+				continue;
+
+			/* Move start up for next time and exit for loop */
+			ith_start = bitmap_find_next_zero_area(
+				map, ith_end, ith_start, num_bits, 0);
+			if (ith_start >= ith_end)
+				/* Need a while new period next time */
+				start = end;
+			else
+				start = ith_start - interval_bits * i;
+			break;
+		}
+
+		/* If didn't exit the for loop with a break, we have success */
+		if (i == to_reserve)
+			break;
+	}
+
+	if (start + num_bits > first_end)
+		return -ENOSPC;
 
-void dwc2_hcd_init_usecs(struct dwc2_hsotg *hsotg)
+	for (i = 0; i < to_reserve; i++) {
+		int ith_start = start + interval_bits * i;
+
+		bitmap_set(map, ith_start, num_bits);
+	}
+
+	return start;
+}
+
+/**
+ * pmap_unschedule() - Undo work done by pmap_schedule()
+ *
+ * @map:             See pmap_schedule().
+ * @bits_per_period: See pmap_schedule().
+ * @periods_in_map:  See pmap_schedule().
+ * @num_bits:        The number of bits that was passed to schedule.
+ * @interval:        The interval that was passed to schedule.
+ * @start:           The return value from pmap_schedule().
+ */
+static void pmap_unschedule(unsigned long *map, int bits_per_period,
+			    int periods_in_map, int num_bits,
+			    int interval, int start)
 {
+	int interval_bits;
+	int to_release;
 	int i;
 
-	for (i = 0; i < 8; i++)
-		hsotg->frame_usecs[i] = max_uframe_usecs[i];
+	/* Adjust interval as per description in pmap_schedule() */
+	interval = gcd(interval, periods_in_map);
+
+	interval_bits = bits_per_period * interval;
+	to_release = periods_in_map / interval;
+
+	for (i = 0; i < to_release; i++) {
+		int ith_start = start + interval_bits * i;
+
+		bitmap_clear(map, ith_start, num_bits);
+	}
 }
 
-static int dwc2_find_single_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+/*
+ * cat_printf() - A printf() + strcat() helper
+ *
+ * This is useful for concatenating a bunch of strings where each string is
+ * constructed using printf.
+ *
+ * @buf:   The destination buffer; will be updated to point after the printed
+ *         data.
+ * @size:  The number of bytes in the buffer (includes space for '\0').
+ * @fmt:   The format for printf.
+ * @...:   The args for printf.
+ */
+static void cat_printf(char **buf, size_t *size, const char *fmt, ...)
 {
-	unsigned short utime = qh->host_us;
+	va_list args;
 	int i;
 
-	for (i = 0; i < 8; i++) {
-		/* At the start hsotg->frame_usecs[i] = max_uframe_usecs[i] */
-		if (utime <= hsotg->frame_usecs[i]) {
-			hsotg->frame_usecs[i] -= utime;
-			qh->frame_usecs[i] += utime;
-			return i;
-		}
+	if (*size == 0)
+		return;
+
+	va_start(args, fmt);
+	i = vsnprintf(*buf, *size, fmt, args);
+	va_end(args);
+
+	if (i >= *size) {
+		(*buf)[*size - 1] = '\0';
+		*buf += *size;
+		*size = 0;
+	} else {
+		*buf += i;
+		*size -= i;
 	}
-	return -ENOSPC;
 }
 
 /*
- * use this for FS apps that can span multiple uframes
+ * pmap_print() - Print the given periodic map
+ *
+ * Will attempt to print out the periodic schedule.
+ *
+ * @map:             See pmap_schedule().
+ * @bits_per_period: See pmap_schedule().
+ * @periods_in_map:  See pmap_schedule().
+ * @period_name:     The name of 1 period, like "uFrame"
+ * @units:           The name of the units, like "us".
+ * @print_fn:        The function to call for printing.
+ * @print_data:      Opaque data to pass to the print function.
  */
-static int dwc2_find_multi_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+static void pmap_print(unsigned long *map, int bits_per_period,
+		       int periods_in_map, const char *period_name,
+		       const char *units,
+		       void (*print_fn)(const char *str, void *data),
+		       void *print_data)
 {
-	unsigned short utime = qh->host_us;
-	unsigned short xtime;
-	int t_left;
+	int period;
+
+	for (period = 0; period < periods_in_map; period++) {
+		char tmp[64];
+		char *buf = tmp;
+		size_t buf_size = sizeof(tmp);
+		int period_start = period * bits_per_period;
+		int period_end = period_start + bits_per_period;
+		int start = 0;
+		int count = 0;
+		bool printed = false;
+		int i;
+
+		for (i = period_start; i < period_end + 1; i++) {
+			/* Handle case when ith bit is set */
+			if (i < period_end &&
+			    bitmap_find_next_zero_area(map, i + 1,
+						       i, 1, 0) != i) {
+				if (count == 0)
+					start = i - period_start;
+				count++;
+				continue;
+			}
+
+			/* ith bit isn't set; don't care if count == 0 */
+			if (count == 0)
+				continue;
+
+			if (!printed)
+				cat_printf(&buf, &buf_size, "%s %d: ",
+					   period_name, period);
+			else
+				cat_printf(&buf, &buf_size, ", ");
+			printed = true;
+
+			cat_printf(&buf, &buf_size, "%d %s -%3d %s", start,
+				   units, start + count - 1, units);
+			count = 0;
+		}
+
+		if (printed)
+			print_fn(tmp, print_data);
+	}
+}
+
+/**
+ * dwc2_get_ls_map() - Get the map used for the given qh
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller.
+ * @qh:    QH for the periodic transfer.
+ *
+ * We'll always get the periodic map out of our TT.  Note that even if we're
+ * running the host straight in low speed / full speed mode it appears as if
+ * a TT is allocated for us, so we'll use it.  If that ever changes we can
+ * add logic here to get a map out of "hsotg" if !qh->do_split.
+ *
+ * Returns: the map or NULL if a map couldn't be found.
+ */
+static unsigned long *dwc2_get_ls_map(struct dwc2_hsotg *hsotg,
+				      struct dwc2_qh *qh)
+{
+	unsigned long *map;
+
+	/* Don't expect to be missing a TT and be doing low speed scheduling */
+	if (WARN_ON(!qh->dwc_tt))
+		return NULL;
+
+	/* Get the map and adjust if this is a multi_tt hub */
+	map = qh->dwc_tt->periodic_bitmaps;
+	if (qh->dwc_tt->usb_tt->multi)
+		map += DWC2_ELEMENTS_PER_LS_BITMAP * qh->ttport;
+
+	return map;
+}
+
+struct dwc2_qh_print_data {
+	struct dwc2_hsotg *hsotg;
+	struct dwc2_qh *qh;
+};
+
+/**
+ * dwc2_qh_print() - Helper function for dwc2_qh_schedule_print()
+ *
+ * @str:  The string to print
+ * @data: A pointer to a struct dwc2_qh_print_data
+ */
+static void dwc2_qh_print(const char *str, void *data)
+{
+	struct dwc2_qh_print_data *print_data = data;
+
+	dwc2_sch_dbg(print_data->hsotg, "QH=%p ...%s\n", print_data->qh, str);
+}
+
+/**
+ * dwc2_qh_schedule_print() - Print the periodic schedule
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller.
+ * @qh:    QH to print.
+ */
+static void dwc2_qh_schedule_print(struct dwc2_hsotg *hsotg,
+				   struct dwc2_qh *qh)
+{
+	struct dwc2_qh_print_data print_data = { hsotg, qh };
 	int i;
-	int j;
-	int k;
 
-	for (i = 0; i < 8; i++) {
-		if (hsotg->frame_usecs[i] <= 0)
+	/*
+	 * The printing functions are quite slow and inefficient.
+	 * If we don't have tracing turned on, don't run unless the special
+	 * define is turned on.
+	 */
+#ifndef DWC2_PRINT_SCHEDULE
+	return;
+#endif
+
+	if (qh->schedule_low_speed) {
+		unsigned long *map = dwc2_get_ls_map(hsotg, qh);
+
+		dwc2_sch_dbg(hsotg, "QH=%p LS/FS trans: %d=>%d us @ %d us",
+			     qh, qh->device_us,
+			     DWC2_ROUND_US_TO_SLICE(qh->device_us),
+			     DWC2_US_PER_SLICE * qh->ls_start_schedule_slice);
+
+		if (map) {
+			dwc2_sch_dbg(hsotg,
+				     "QH=%p Whole low/full speed map %p now:\n",
+				     qh, map);
+			pmap_print(map, DWC2_LS_PERIODIC_SLICES_PER_FRAME,
+				   DWC2_LS_SCHEDULE_FRAMES, "Frame ", "slices",
+				   dwc2_qh_print, &print_data);
+		}
+	}
+
+	for (i = 0; i < qh->num_hs_transfers; i++) {
+		struct dwc2_hs_transfer_time *trans_time = qh->hs_transfers + i;
+		int uframe = trans_time->start_schedule_us /
+			     DWC2_HS_PERIODIC_US_PER_UFRAME;
+		int rel_us = trans_time->start_schedule_us %
+			     DWC2_HS_PERIODIC_US_PER_UFRAME;
+
+		dwc2_sch_dbg(hsotg,
+			     "QH=%p HS trans #%d: %d us @ uFrame %d + %d us\n",
+			     qh, i, trans_time->duration_us, uframe, rel_us);
+	}
+	if (qh->num_hs_transfers) {
+		dwc2_sch_dbg(hsotg, "QH=%p Whole high speed map now:\n", qh);
+		pmap_print(hsotg->hs_periodic_bitmap,
+			   DWC2_HS_PERIODIC_US_PER_UFRAME,
+			   DWC2_HS_SCHEDULE_UFRAMES, "uFrame", "us",
+			   dwc2_qh_print, &print_data);
+	}
+
+}
+
+/**
+ * dwc2_ls_pmap_schedule() - Schedule a low speed QH
+ *
+ * @hsotg:        The HCD state structure for the DWC OTG controller.
+ * @qh:           QH for the periodic transfer.
+ * @search_slice: We'll start trying to schedule at the passed slice.
+ *                Remember that slices are the units of the low speed
+ *                schedule (think 25us or so).
+ *
+ * Wraps pmap_schedule() with the right parameters for low speed scheduling.
+ *
+ * Normally we schedule low speed devices on the map associated with the TT.
+ *
+ * Returns: 0 for success or an error code.
+ */
+static int dwc2_ls_pmap_schedule(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
+				 int search_slice)
+{
+	int slices = DIV_ROUND_UP(qh->device_us, DWC2_US_PER_SLICE);
+	unsigned long *map = dwc2_get_ls_map(hsotg, qh);
+	int slice;
+
+	if (map == NULL)
+		return -EINVAL;
+
+	/*
+	 * Schedule on the proper low speed map with our low speed scheduling
+	 * parameters.  Note that we use the "device_interval" here since
+	 * we want the low speed interval and the only way we'd be in this
+	 * function is if the device is low speed.
+	 *
+	 * If we happen to be doing low speed and high speed scheduling for the
+	 * same transaction (AKA we have a split) we always do low speed first.
+	 * That means we can always pass "false" for only_one_period (that
+	 * parameters is only useful when we're trying to get one schedule to
+	 * match what we already planned in the other schedule).
+	 */
+	slice = pmap_schedule(map, DWC2_LS_PERIODIC_SLICES_PER_FRAME,
+			      DWC2_LS_SCHEDULE_FRAMES, slices,
+			      qh->device_interval, search_slice, false);
+
+	if (slice < 0)
+		return slice;
+
+	qh->ls_start_schedule_slice = slice;
+	return 0;
+}
+
+/**
+ * dwc2_ls_pmap_unschedule() - Undo work done by dwc2_ls_pmap_schedule()
+ *
+ * @hsotg:       The HCD state structure for the DWC OTG controller.
+ * @qh:          QH for the periodic transfer.
+ */
+static void dwc2_ls_pmap_unschedule(struct dwc2_hsotg *hsotg,
+				    struct dwc2_qh *qh)
+{
+	int slices = DIV_ROUND_UP(qh->device_us, DWC2_US_PER_SLICE);
+	unsigned long *map = dwc2_get_ls_map(hsotg, qh);
+
+	/* Schedule should have failed, so no worries about no error code */
+	if (map == NULL)
+		return;
+
+	pmap_unschedule(map, DWC2_LS_PERIODIC_SLICES_PER_FRAME,
+			DWC2_LS_SCHEDULE_FRAMES, slices, qh->device_interval,
+			qh->ls_start_schedule_slice);
+}
+
+/**
+ * dwc2_hs_pmap_schedule - Schedule in the main high speed schedule
+ *
+ * This will schedule something on the main dwc2 schedule.
+ *
+ * We'll start looking in qh->hs_transfers[index].start_schedule_us.  We'll
+ * update this with the result upon success.  We also use the duration from
+ * the same structure.
+ *
+ * @hsotg:           The HCD state structure for the DWC OTG controller.
+ * @qh:              QH for the periodic transfer.
+ * @only_one_period: If true we will limit ourselves to just looking at
+ *                   one period (aka one 100us chunk).  This is used if we have
+ *                   already scheduled something on the low speed schedule and
+ *                   need to find something that matches on the high speed one.
+ * @index:           The index into qh->hs_transfers that we're working with.
+ *
+ * Returns: 0 for success or an error code.  Upon success the
+ *          dwc2_hs_transfer_time specified by "index" will be updated.
+ */
+static int dwc2_hs_pmap_schedule(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
+				 bool only_one_period, int index)
+{
+	struct dwc2_hs_transfer_time *trans_time = qh->hs_transfers + index;
+	int us;
+
+	us = pmap_schedule(hsotg->hs_periodic_bitmap,
+			   DWC2_HS_PERIODIC_US_PER_UFRAME,
+			   DWC2_HS_SCHEDULE_UFRAMES, trans_time->duration_us,
+			   qh->host_interval, trans_time->start_schedule_us,
+			   only_one_period);
+
+	if (us < 0)
+		return us;
+
+	trans_time->start_schedule_us = us;
+	return 0;
+}
+
+/**
+ * dwc2_ls_pmap_unschedule() - Undo work done by dwc2_hs_pmap_schedule()
+ *
+ * @hsotg:       The HCD state structure for the DWC OTG controller.
+ * @qh:          QH for the periodic transfer.
+ */
+static void dwc2_hs_pmap_unschedule(struct dwc2_hsotg *hsotg,
+				    struct dwc2_qh *qh, int index)
+{
+	struct dwc2_hs_transfer_time *trans_time = qh->hs_transfers + index;
+
+	pmap_unschedule(hsotg->hs_periodic_bitmap,
+			DWC2_HS_PERIODIC_US_PER_UFRAME,
+			DWC2_HS_SCHEDULE_UFRAMES, trans_time->duration_us,
+			qh->host_interval, trans_time->start_schedule_us);
+}
+
+/**
+ * dwc2_uframe_schedule_split - Schedule a QH for a periodic split xfer.
+ *
+ * This is the most complicated thing in USB.  We have to find matching time
+ * in both the global high speed schedule for the port and the low speed
+ * schedule for the TT associated with the given device.
+ *
+ * Being here means that the host must be running in high speed mode and the
+ * device is in low or full speed mode (and behind a hub).
+ *
+ * @hsotg:       The HCD state structure for the DWC OTG controller.
+ * @qh:          QH for the periodic transfer.
+ */
+static int dwc2_uframe_schedule_split(struct dwc2_hsotg *hsotg,
+				      struct dwc2_qh *qh)
+{
+	int bytecount = dwc2_hb_mult(qh->maxp) * dwc2_max_packet(qh->maxp);
+	int ls_search_slice;
+	int err = 0;
+	int host_interval_in_sched;
+
+	/*
+	 * The interval (how often to repeat) in the actual host schedule.
+	 * See pmap_schedule() for gcd() explanation.
+	 */
+	host_interval_in_sched = gcd(qh->host_interval,
+				     DWC2_HS_SCHEDULE_UFRAMES);
+
+	/*
+	 * We always try to find space in the low speed schedule first, then
+	 * try to find high speed time that matches.  If we don't, we'll bump
+	 * up the place we start searching in the low speed schedule and try
+	 * again.  To start we'll look right at the beginning of the low speed
+	 * schedule.
+	 *
+	 * Note that this will tend to front-load the high speed schedule.
+	 * We may eventually want to try to avoid this by either considering
+	 * both schedules together or doing some sort of round robin.
+	 */
+	ls_search_slice = 0;
+
+	while (ls_search_slice < DWC2_LS_SCHEDULE_SLICES) {
+		int start_s_uframe;
+		int ssplit_s_uframe;
+		int second_s_uframe;
+		int rel_uframe;
+		int first_count;
+		int middle_count;
+		int end_count;
+		int first_data_bytes;
+		int other_data_bytes;
+		int i;
+
+		if (qh->schedule_low_speed) {
+			err = dwc2_ls_pmap_schedule(hsotg, qh, ls_search_slice);
+
+			/*
+			 * If we got an error here there's no other magic we
+			 * can do, so bail.  All the looping above is only
+			 * helpful to redo things if we got a low speed slot
+			 * and then couldn't find a matching high speed slot.
+			 */
+			if (err)
+				return err;
+		} else {
+			/* Must be missing the tt structure?  Why? */
+			WARN_ON_ONCE(1);
+		}
+
+		/*
+		 * This will give us a number 0 - 7 if
+		 * DWC2_LS_SCHEDULE_FRAMES == 1, or 0 - 15 if == 2, or ...
+		 */
+		start_s_uframe = qh->ls_start_schedule_slice /
+				 DWC2_SLICES_PER_UFRAME;
+
+		/* Get a number that's always 0 - 7 */
+		rel_uframe = (start_s_uframe % 8);
+
+		/*
+		 * If we were going to start in uframe 7 then we would need to
+		 * issue a start split in uframe 6, which spec says is not OK.
+		 * Move on to the next full frame (assuming there is one).
+		 *
+		 * See 11.18.4 Host Split Transaction Scheduling Requirements
+		 * bullet 1.
+		 */
+		if (rel_uframe == 7) {
+			if (qh->schedule_low_speed)
+				dwc2_ls_pmap_unschedule(hsotg, qh);
+			ls_search_slice =
+				(qh->ls_start_schedule_slice /
+				 DWC2_LS_PERIODIC_SLICES_PER_FRAME + 1) *
+				DWC2_LS_PERIODIC_SLICES_PER_FRAME;
 			continue;
+		}
 
 		/*
-		 * we need n consecutive slots so use j as a start slot
-		 * j plus j+1 must be enough time (for now)
+		 * For ISOC in:
+		 * - start split            (frame -1)
+		 * - complete split w/ data (frame +1)
+		 * - complete split w/ data (frame +2)
+		 * - ...
+		 * - complete split w/ data (frame +num_data_packets)
+		 * - complete split w/ data (frame +num_data_packets+1)
+		 * - complete split w/ data (frame +num_data_packets+2, max 8)
+		 *   ...though if frame was "0" then max is 7...
+		 *
+		 * For ISOC out we might need to do:
+		 * - start split w/ data    (frame -1)
+		 * - start split w/ data    (frame +0)
+		 * - ...
+		 * - start split w/ data    (frame +num_data_packets-2)
+		 *
+		 * For INTERRUPT in we might need to do:
+		 * - start split            (frame -1)
+		 * - complete split w/ data (frame +1)
+		 * - complete split w/ data (frame +2)
+		 * - complete split w/ data (frame +3, max 8)
+		 *
+		 * For INTERRUPT out we might need to do:
+		 * - start split w/ data    (frame -1)
+		 * - complete split         (frame +1)
+		 * - complete split         (frame +2)
+		 * - complete split         (frame +3, max 8)
+		 *
+		 * Start adjusting!
 		 */
-		xtime = hsotg->frame_usecs[i];
-		for (j = i + 1; j < 8; j++) {
-			/*
-			 * if we add this frame remaining time to xtime we may
-			 * be OK, if not we need to test j for a complete frame
-			 */
-			if (xtime + hsotg->frame_usecs[j] < utime) {
-				if (hsotg->frame_usecs[j] <
-							max_uframe_usecs[j])
-					continue;
+		ssplit_s_uframe = (start_s_uframe +
+				   host_interval_in_sched - 1) %
+				  host_interval_in_sched;
+		if (qh->ep_type == USB_ENDPOINT_XFER_ISOC && !qh->ep_is_in)
+			second_s_uframe = start_s_uframe;
+		else
+			second_s_uframe = start_s_uframe + 1;
+
+		/* First data transfer might not be all 188 bytes. */
+		first_data_bytes = 188 -
+			DIV_ROUND_UP(188 * (qh->ls_start_schedule_slice %
+					    DWC2_SLICES_PER_UFRAME),
+				     DWC2_SLICES_PER_UFRAME);
+		if (first_data_bytes > bytecount)
+			first_data_bytes = bytecount;
+		other_data_bytes = bytecount - first_data_bytes;
+
+		/*
+		 * For now, skip OUT xfers where first xfer is partial
+		 *
+		 * Main dwc2 code assumes:
+		 * - INT transfers never get split in two.
+		 * - ISOC transfers can always transfer 188 bytes the first
+		 *   time.
+		 *
+		 * Until that code is fixed, try again if the first transfer
+		 * couldn't transfer everything.
+		 *
+		 * This code can be removed if/when the rest of dwc2 handles
+		 * the above cases.  Until it's fixed we just won't be able
+		 * to schedule quite as tightly.
+		 */
+		if (!qh->ep_is_in &&
+		    (first_data_bytes != min_t(int, 188, bytecount))) {
+			dwc2_sch_dbg(hsotg,
+				     "QH=%p avoiding broken 1st xfer (%d, %d)\n",
+				     qh, first_data_bytes, bytecount);
+			if (qh->schedule_low_speed)
+				dwc2_ls_pmap_unschedule(hsotg, qh);
+			ls_search_slice = (start_s_uframe + 1) *
+				DWC2_SLICES_PER_UFRAME;
+			continue;
+		}
+
+		/* Start by assuming transfers for the bytes */
+		qh->num_hs_transfers = 1 + DIV_ROUND_UP(other_data_bytes, 188);
+
+		/*
+		 * Everything except ISOC OUT has extra transfers.  Rules are
+		 * complicated.  See 11.18.4 Host Split Transaction Scheduling
+		 * Requirements bullet 3.
+		 */
+		if (qh->ep_type == USB_ENDPOINT_XFER_INT) {
+			if (rel_uframe == 6)
+				qh->num_hs_transfers += 2;
+			else
+				qh->num_hs_transfers += 3;
+
+			if (qh->ep_is_in) {
+				/*
+				 * First is start split, middle/end is data.
+				 * Allocate full data bytes for all data.
+				 */
+				first_count = 4;
+				middle_count = bytecount;
+				end_count = bytecount;
+			} else {
+				/*
+				 * First is data, middle/end is complete.
+				 * First transfer and second can have data.
+				 * Rest should just have complete split.
+				 */
+				first_count = first_data_bytes;
+				middle_count = max_t(int, 4, other_data_bytes);
+				end_count = 4;
 			}
-			if (xtime >= utime) {
-				t_left = utime;
-				for (k = i; k < 8; k++) {
-					t_left -= hsotg->frame_usecs[k];
-					if (t_left <= 0) {
-						qh->frame_usecs[k] +=
-							hsotg->frame_usecs[k]
-								+ t_left;
-						hsotg->frame_usecs[k] = -t_left;
-						return i;
-					} else {
-						qh->frame_usecs[k] +=
-							hsotg->frame_usecs[k];
-						hsotg->frame_usecs[k] = 0;
-					}
-				}
+		} else {
+			if (qh->ep_is_in) {
+				int last;
+
+				/* Account for the start split */
+				qh->num_hs_transfers++;
+
+				/* Calculate "L" value from spec */
+				last = rel_uframe + qh->num_hs_transfers + 1;
+
+				/* Start with basic case */
+				if (last <= 6)
+					qh->num_hs_transfers += 2;
+				else
+					qh->num_hs_transfers += 1;
+
+				/* Adjust downwards */
+				if (last >= 6 && rel_uframe == 0)
+					qh->num_hs_transfers--;
+
+				/* 1st = start; rest can contain data */
+				first_count = 4;
+				middle_count = min_t(int, 188, bytecount);
+				end_count = middle_count;
+			} else {
+				/* All contain data, last might be smaller */
+				first_count = first_data_bytes;
+				middle_count = min_t(int, 188,
+						     other_data_bytes);
+				end_count = other_data_bytes % 188;
 			}
-			/* add the frame time to x time */
-			xtime += hsotg->frame_usecs[j];
-			/* we must have a fully available next frame or break */
-			if (xtime < utime &&
-			   hsotg->frame_usecs[j] == max_uframe_usecs[j])
-				continue;
 		}
+
+		/* Assign durations per uFrame */
+		qh->hs_transfers[0].duration_us = HS_USECS_ISO(first_count);
+		for (i = 1; i < qh->num_hs_transfers - 1; i++)
+			qh->hs_transfers[i].duration_us =
+				HS_USECS_ISO(middle_count);
+		if (qh->num_hs_transfers > 1)
+			qh->hs_transfers[qh->num_hs_transfers - 1].duration_us =
+				HS_USECS_ISO(end_count);
+
+		/*
+		 * Assign start us.  The call below to dwc2_hs_pmap_schedule()
+		 * will start with these numbers but may adjust within the same
+		 * microframe.
+		 */
+		qh->hs_transfers[0].start_schedule_us =
+			ssplit_s_uframe * DWC2_HS_PERIODIC_US_PER_UFRAME;
+		for (i = 1; i < qh->num_hs_transfers; i++)
+			qh->hs_transfers[i].start_schedule_us =
+				((second_s_uframe + i - 1) %
+				 DWC2_HS_SCHEDULE_UFRAMES) *
+				DWC2_HS_PERIODIC_US_PER_UFRAME;
+
+		/* Try to schedule with filled in hs_transfers above */
+		for (i = 0; i < qh->num_hs_transfers; i++) {
+			err = dwc2_hs_pmap_schedule(hsotg, qh, true, i);
+			if (err)
+				break;
+		}
+
+		/* If we scheduled all w/out breaking out then we're all good */
+		if (i == qh->num_hs_transfers)
+			break;
+
+		for (; i >= 0; i--)
+			dwc2_hs_pmap_unschedule(hsotg, qh, i);
+
+		if (qh->schedule_low_speed)
+			dwc2_ls_pmap_unschedule(hsotg, qh);
+
+		/* Try again starting in the next microframe */
+		ls_search_slice = (start_s_uframe + 1) * DWC2_SLICES_PER_UFRAME;
 	}
-	return -ENOSPC;
+
+	if (ls_search_slice >= DWC2_LS_SCHEDULE_SLICES)
+		return -ENOSPC;
+
+	return 0;
 }
 
-static int dwc2_find_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+/**
+ * dwc2_uframe_schedule_hs - Schedule a QH for a periodic high speed xfer.
+ *
+ * Basically this just wraps dwc2_hs_pmap_schedule() to provide a clean
+ * interface.
+ *
+ * @hsotg:       The HCD state structure for the DWC OTG controller.
+ * @qh:          QH for the periodic transfer.
+ */
+static int dwc2_uframe_schedule_hs(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+	/* In non-split host and device time are the same */
+	WARN_ON(qh->host_us != qh->device_us);
+	WARN_ON(qh->host_interval != qh->device_interval);
+	WARN_ON(qh->num_hs_transfers != 1);
+
+	/* We'll have one transfer; init start to 0 before calling scheduler */
+	qh->hs_transfers[0].start_schedule_us = 0;
+	qh->hs_transfers[0].duration_us = qh->host_us;
+
+	return dwc2_hs_pmap_schedule(hsotg, qh, false, 0);
+}
+
+/**
+ * dwc2_uframe_schedule_ls - Schedule a QH for a periodic low/full speed xfer.
+ *
+ * Basically this just wraps dwc2_ls_pmap_schedule() to provide a clean
+ * interface.
+ *
+ * @hsotg:       The HCD state structure for the DWC OTG controller.
+ * @qh:          QH for the periodic transfer.
+ */
+static int dwc2_uframe_schedule_ls(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+	/* In non-split host and device time are the same */
+	WARN_ON(qh->host_us != qh->device_us);
+	WARN_ON(qh->host_interval != qh->device_interval);
+	WARN_ON(!qh->schedule_low_speed);
+
+	/* Run on the main low speed schedule (no split = no hub = no TT) */
+	return dwc2_ls_pmap_schedule(hsotg, qh, 0);
+}
+
+/**
+ * dwc2_uframe_schedule - Schedule a QH for a periodic xfer.
+ *
+ * Calls one of the 3 sub-function depending on what type of transfer this QH
+ * is for.  Also adds some printing.
+ *
+ * @hsotg:       The HCD state structure for the DWC OTG controller.
+ * @qh:          QH for the periodic transfer.
+ */
+static int dwc2_uframe_schedule(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
 	int ret;
 
-	if (qh->dev_speed == USB_SPEED_HIGH) {
-		/* if this is a hs transaction we need a full frame */
-		ret = dwc2_find_single_uframe(hsotg, qh);
-	} else {
-		/*
-		 * if this is a fs transaction we may need a sequence
-		 * of frames
-		 */
-		ret = dwc2_find_multi_uframe(hsotg, qh);
-	}
+	if (qh->dev_speed == USB_SPEED_HIGH)
+		ret = dwc2_uframe_schedule_hs(hsotg, qh);
+	else if (!qh->do_split)
+		ret = dwc2_uframe_schedule_ls(hsotg, qh);
+	else
+		ret = dwc2_uframe_schedule_split(hsotg, qh);
+
+	if (ret)
+		dwc2_sch_dbg(hsotg, "QH=%p Failed to schedule %d\n", qh, ret);
+	else
+		dwc2_qh_schedule_print(hsotg, qh);
+
 	return ret;
 }
 
 /**
+ * dwc2_uframe_unschedule - Undoes dwc2_uframe_schedule().
+ *
+ * @hsotg:       The HCD state structure for the DWC OTG controller.
+ * @qh:          QH for the periodic transfer.
+ */
+static void dwc2_uframe_unschedule(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+	int i;
+
+	for (i = 0; i < qh->num_hs_transfers; i++)
+		dwc2_hs_pmap_unschedule(hsotg, qh, i);
+
+	if (qh->schedule_low_speed)
+		dwc2_ls_pmap_unschedule(hsotg, qh);
+
+	dwc2_sch_dbg(hsotg, "QH=%p Unscheduled\n", qh);
+}
+
+/**
  * dwc2_pick_first_frame() - Choose 1st frame for qh that's already scheduled
  *
  * Takes a qh that has already been scheduled (which means we know we have the
@@ -265,6 +1082,7 @@ static void dwc2_pick_first_frame(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 	u16 frame_number;
 	u16 earliest_frame;
 	u16 next_active_frame;
+	u16 relative_frame;
 	u16 interval;
 
 	/*
@@ -292,8 +1110,36 @@ static void dwc2_pick_first_frame(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		goto exit;
 	}
 
-	/* Adjust interval as per high speed schedule which has 8 uFrame */
-	interval = gcd(qh->host_interval, 8);
+	if (qh->dev_speed == USB_SPEED_HIGH || qh->do_split) {
+		/*
+		 * We're either at high speed or we're doing a split (which
+		 * means we're talking high speed to a hub).  In any case
+		 * the first frame should be based on when the first scheduled
+		 * event is.
+		 */
+		WARN_ON(qh->num_hs_transfers < 1);
+
+		relative_frame = qh->hs_transfers[0].start_schedule_us /
+				 DWC2_HS_PERIODIC_US_PER_UFRAME;
+
+		/* Adjust interval as per high speed schedule */
+		interval = gcd(qh->host_interval, DWC2_HS_SCHEDULE_UFRAMES);
+
+	} else {
+		/*
+		 * Low or full speed directly on dwc2.  Just about the same
+		 * as high speed but on a different schedule and with slightly
+		 * different adjustments.  Note that this works because when
+		 * the host and device are both low speed then frames in the
+		 * controller tick at low speed.
+		 */
+		relative_frame = qh->ls_start_schedule_slice /
+				 DWC2_LS_PERIODIC_SLICES_PER_FRAME;
+		interval = gcd(qh->host_interval, DWC2_LS_SCHEDULE_FRAMES);
+	}
+
+	/* Scheduler messed up if frame is past interval */
+	WARN_ON(relative_frame >= interval);
 
 	/*
 	 * We know interval must divide (HFNUM_MAX_FRNUM + 1) now that we've
@@ -310,7 +1156,7 @@ static void dwc2_pick_first_frame(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 	 * scheduled for.
 	 */
 	next_active_frame = dwc2_frame_num_inc(next_active_frame,
-					       qh->assigned_uframe);
+					       relative_frame);
 
 	/*
 	 * We actually need 1 frame before since the next_active_frame is
@@ -351,9 +1197,7 @@ static int dwc2_do_reserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 	int status;
 
 	if (hsotg->core_params->uframe_sched > 0) {
-		status = dwc2_find_uframe(hsotg, qh);
-		if (status >= 0)
-			qh->assigned_uframe = status;
+		status = dwc2_uframe_schedule(hsotg, qh);
 	} else {
 		status = dwc2_periodic_channel_available(hsotg);
 		if (status) {
@@ -410,12 +1254,7 @@ static void dwc2_do_unreserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 	hsotg->periodic_usecs -= qh->host_us;
 
 	if (hsotg->core_params->uframe_sched > 0) {
-		int i;
-
-		for (i = 0; i < 8; i++) {
-			hsotg->frame_usecs[i] += qh->frame_usecs[i];
-			qh->frame_usecs[i] = 0;
-		}
+		dwc2_uframe_unschedule(hsotg, qh);
 	} else {
 		/* Release periodic channel reservation */
 		hsotg->periodic_channels--;
@@ -606,88 +1445,81 @@ static void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg,
  * @qh:    The QH to init
  * @urb:   Holds the information about the device/endpoint needed to initialize
  *         the QH
+ * @mem_flags: Flags for allocating memory.
  */
 static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
-			 struct dwc2_hcd_urb *urb)
+			 struct dwc2_hcd_urb *urb, gfp_t mem_flags)
 {
-	int dev_speed, hub_addr, hub_port;
+	int dev_speed = dwc2_host_get_speed(hsotg, urb->priv);
+	u8 ep_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
+	bool ep_is_in = !!dwc2_hcd_is_pipe_in(&urb->pipe_info);
+	bool ep_is_isoc = (ep_type == USB_ENDPOINT_XFER_ISOC);
+	bool ep_is_int = (ep_type == USB_ENDPOINT_XFER_INT);
+	u32 hprt = dwc2_readl(hsotg->regs + HPRT0);
+	u32 prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
+	bool do_split = (prtspd == HPRT0_SPD_HIGH_SPEED &&
+			 dev_speed != USB_SPEED_HIGH);
+	int maxp = dwc2_hcd_get_mps(&urb->pipe_info);
+	int bytecount = dwc2_hb_mult(maxp) * dwc2_max_packet(maxp);
 	char *speed, *type;
 
-	dev_vdbg(hsotg->dev, "%s()\n", __func__);
-
 	/* Initialize QH */
 	qh->hsotg = hsotg;
 	setup_timer(&qh->unreserve_timer, dwc2_unreserve_timer_fn,
 		    (unsigned long)qh);
-	qh->ep_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
-	qh->ep_is_in = dwc2_hcd_is_pipe_in(&urb->pipe_info) ? 1 : 0;
+	qh->ep_type = ep_type;
+	qh->ep_is_in = ep_is_in;
 
 	qh->data_toggle = DWC2_HC_PID_DATA0;
-	qh->maxp = dwc2_hcd_get_mps(&urb->pipe_info);
+	qh->maxp = maxp;
 	INIT_LIST_HEAD(&qh->qtd_list);
 	INIT_LIST_HEAD(&qh->qh_list_entry);
 
-	/* FS/LS Endpoint on HS Hub, NOT virtual root hub */
-	dev_speed = dwc2_host_get_speed(hsotg, urb->priv);
+	qh->do_split = do_split;
+	qh->dev_speed = dev_speed;
+
+	if (ep_is_int || ep_is_isoc) {
+		/* Compute scheduling parameters once and save them */
+		int host_speed = do_split ? USB_SPEED_HIGH : dev_speed;
+		struct dwc2_tt *dwc_tt = dwc2_host_get_tt_info(hsotg, urb->priv,
+							       mem_flags,
+							       &qh->ttport);
+		int device_ns;
 
-	dwc2_host_hub_info(hsotg, urb->priv, &hub_addr, &hub_port);
+		qh->dwc_tt = dwc_tt;
 
-	if ((dev_speed == USB_SPEED_LOW || dev_speed == USB_SPEED_FULL) &&
-	    hub_addr != 0 && hub_addr != 1) {
-		dev_vdbg(hsotg->dev,
-			 "QH init: EP %d: TT found at hub addr %d, for port %d\n",
-			 dwc2_hcd_get_ep_num(&urb->pipe_info), hub_addr,
-			 hub_port);
-		qh->do_split = 1;
-	}
+		qh->host_us = NS_TO_US(usb_calc_bus_time(host_speed, ep_is_in,
+				       ep_is_isoc, bytecount));
+		device_ns = usb_calc_bus_time(dev_speed, ep_is_in,
+					      ep_is_isoc, bytecount);
 
-	if (qh->ep_type == USB_ENDPOINT_XFER_INT ||
-	    qh->ep_type == USB_ENDPOINT_XFER_ISOC) {
-		/* Compute scheduling parameters once and save them */
-		u32 hprt, prtspd;
-
-		/* Todo: Account for split transfers in the bus time */
-		int bytecount =
-			dwc2_hb_mult(qh->maxp) * dwc2_max_packet(qh->maxp);
-
-		qh->host_us = NS_TO_US(usb_calc_bus_time(qh->do_split ?
-			      USB_SPEED_HIGH : dev_speed, qh->ep_is_in,
-			      qh->ep_type == USB_ENDPOINT_XFER_ISOC,
-			      bytecount));
-
-		qh->host_interval = urb->interval;
-		dwc2_sch_dbg(hsotg, "QH=%p init nxt=%04x, fn=%04x, int=%#x\n",
-			     qh, qh->next_active_frame, hsotg->frame_number,
-			     qh->host_interval);
-#if 0
-		/* Increase interrupt polling rate for debugging */
-		if (qh->ep_type == USB_ENDPOINT_XFER_INT)
-			qh->host_interval = 8;
-#endif
-		hprt = dwc2_readl(hsotg->regs + HPRT0);
-		prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
-		if (prtspd == HPRT0_SPD_HIGH_SPEED &&
-		    (dev_speed == USB_SPEED_LOW ||
-		     dev_speed == USB_SPEED_FULL)) {
-			qh->host_interval *= 8;
-			dwc2_sch_dbg(hsotg,
-				     "QH=%p init*8 nxt=%04x, fn=%04x, int=%#x\n",
-				     qh, qh->next_active_frame,
-				     hsotg->frame_number, qh->host_interval);
+		if (do_split && dwc_tt)
+			device_ns += dwc_tt->usb_tt->think_time;
+		qh->device_us = NS_TO_US(device_ns);
 
-		}
-		dev_dbg(hsotg->dev, "interval=%d\n", qh->host_interval);
-	}
 
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH Initialized\n");
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - qh = %p\n", qh);
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Device Address = %d\n",
-		 dwc2_hcd_get_dev_addr(&urb->pipe_info));
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Endpoint %d, %s\n",
-		 dwc2_hcd_get_ep_num(&urb->pipe_info),
-		 dwc2_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT");
+		qh->device_interval = urb->interval;
+		qh->host_interval = urb->interval * (do_split ? 8 : 1);
 
-	qh->dev_speed = dev_speed;
+		/*
+		 * Schedule low speed if we're running the host in low or
+		 * full speed OR if we've got a "TT" to deal with to access this
+		 * device.
+		 */
+		qh->schedule_low_speed = prtspd != HPRT0_SPD_HIGH_SPEED ||
+					 dwc_tt;
+
+		if (do_split) {
+			/* We won't know num transfers until we schedule */
+			qh->num_hs_transfers = -1;
+		} else if (dev_speed == USB_SPEED_HIGH) {
+			qh->num_hs_transfers = 1;
+		} else {
+			qh->num_hs_transfers = 0;
+		}
+
+		/* We'll schedule later when we have something to do */
+	}
 
 	switch (dev_speed) {
 	case USB_SPEED_LOW:
@@ -703,7 +1535,6 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 		speed = "?";
 		break;
 	}
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Speed = %s\n", speed);
 
 	switch (qh->ep_type) {
 	case USB_ENDPOINT_XFER_ISOC:
@@ -723,13 +1554,21 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 		break;
 	}
 
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Type = %s\n", type);
-
-	if (qh->ep_type == USB_ENDPOINT_XFER_INT) {
-		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - usecs = %d\n",
-			 qh->host_us);
-		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - interval = %d\n",
-			 qh->host_interval);
+	dwc2_sch_dbg(hsotg, "QH=%p Init %s, %s speed, %d bytes:\n", qh, type,
+		     speed, bytecount);
+	dwc2_sch_dbg(hsotg, "QH=%p ...addr=%d, ep=%d, %s\n", qh,
+		     dwc2_hcd_get_dev_addr(&urb->pipe_info),
+		     dwc2_hcd_get_ep_num(&urb->pipe_info),
+		     ep_is_in ? "IN" : "OUT");
+	if (ep_is_int || ep_is_isoc) {
+		dwc2_sch_dbg(hsotg,
+			     "QH=%p ...duration: host=%d us, device=%d us\n",
+			     qh, qh->host_us, qh->device_us);
+		dwc2_sch_dbg(hsotg, "QH=%p ...interval: host=%d, device=%d\n",
+			     qh, qh->host_interval, qh->device_interval);
+		if (qh->schedule_low_speed)
+			dwc2_sch_dbg(hsotg, "QH=%p ...low speed schedule=%p\n",
+				     qh, dwc2_get_ls_map(hsotg, qh));
 	}
 }
 
@@ -757,7 +1596,7 @@ struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
 	if (!qh)
 		return NULL;
 
-	dwc2_qh_init(hsotg, qh, urb);
+	dwc2_qh_init(hsotg, qh, urb, mem_flags);
 
 	if (hsotg->core_params->dma_desc_enable > 0 &&
 	    dwc2_hcd_qh_init_ddma(hsotg, qh, mem_flags) < 0) {
@@ -789,6 +1628,7 @@ void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		dwc2_do_unreserve(hsotg, qh);
 		spin_unlock_irqrestore(&hsotg->lock, flags);
 	}
+	dwc2_host_put_tt_info(hsotg, qh->dwc_tt);
 
 	if (qh->desc_list)
 		dwc2_hcd_qh_free_ddma(hsotg, qh);
@@ -904,6 +1744,8 @@ static int dwc2_next_for_periodic_split(struct dwc2_hsotg *hsotg,
 	u16 incr;
 
 	/*
+	 * See dwc2_uframe_schedule_split() for split scheduling.
+	 *
 	 * Basically: increment 1 normally, but 2 right after the start split
 	 * (except for ISOC out).
 	 */
@@ -1006,9 +1848,17 @@ static int dwc2_next_periodic_start(struct dwc2_hsotg *hsotg,
 	if (qh->start_active_frame == qh->next_active_frame ||
 	    dwc2_frame_num_gt(prev_frame_number, qh->start_active_frame)) {
 		u16 ideal_start = qh->start_active_frame;
+		int periods_in_map;
 
-		/* Adjust interval as per gcd with plan length. */
-		interval = gcd(interval, 8);
+		/*
+		 * Adjust interval as per gcd with map size.
+		 * See pmap_schedule() for more details here.
+		 */
+		if (qh->do_split || qh->dev_speed == USB_SPEED_HIGH)
+			periods_in_map = DWC2_HS_SCHEDULE_UFRAMES;
+		else
+			periods_in_map = DWC2_LS_SCHEDULE_FRAMES;
+		interval = gcd(interval, periods_in_map);
 
 		do {
 			qh->start_active_frame = dwc2_frame_num_inc(
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Thu Jan 28 18:20:13 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Thu, 28 Jan 2016 18:20:13 -0800
Subject: [PATCH v6 22/22] usb: dwc2: host: If using uframe scheduler,
 end splits better
In-Reply-To: <1454034013-24657-1-git-send-email-dianders@chromium.org>
References: <1454034013-24657-1-git-send-email-dianders@chromium.org>
Message-ID: <1454034013-24657-23-git-send-email-dianders@chromium.org>

The microframe scheduler figured out exactly how many transfers we need
for a split transaction.  Let's use this knowledge to know when to end
things.

Without this I found that certain devices would just keep responding
with tons of NYET resonses on their INT_IN endpoint.  These would just
keep going and going and eventually we'd decide to terminate the
transfer (because the whole frame changed), but by that time the
scheduler would decide that we "missed" the start of the next transfer.
I can also imagine that if we blow past the end of our scheduled time we
may mess up other things that were scheduled to happen.

No known test cases are improved by this patch except that the scheduler
code doesn't yell about MISSES constantly anymore.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Tested-by: Heiko Stuebner <heiko at sntech.de>
Tested-by: Stefan Wahren <stefan.wahren at i2se.com>
---
Changes in v6:
- Add Heiko's Tested-by.
- Add Stefan's Tested-by.

Changes in v5: None
Changes in v4:
- If using uframe scheduler, end splits better new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/hcd_intr.c | 48 +++++++++++++++++++++++++++++++++++++++------
 1 file changed, 42 insertions(+), 6 deletions(-)

diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index dc285667233a..f7a325a8c741 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -1363,14 +1363,50 @@ static void dwc2_hc_nyet_intr(struct dwc2_hsotg *hsotg,
 
 		if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
 		    chan->ep_type == USB_ENDPOINT_XFER_ISOC) {
-			int frnum = dwc2_hcd_get_frame_number(hsotg);
+			struct dwc2_qh *qh = chan->qh;
+			bool past_end;
+
+			if (hsotg->core_params->uframe_sched <= 0) {
+				int frnum = dwc2_hcd_get_frame_number(hsotg);
+
+				/* Don't have num_hs_transfers; simple logic */
+				past_end = dwc2_full_frame_num(frnum) !=
+				     dwc2_full_frame_num(qh->next_active_frame);
+			} else {
+				int end_frnum;
 
-			if (dwc2_full_frame_num(frnum) !=
-			    dwc2_full_frame_num(chan->qh->next_active_frame)) {
 				/*
-				 * No longer in the same full speed frame.
-				 * Treat this as a transaction error.
-				 */
+				* Figure out the end frame based on schedule.
+				*
+				* We don't want to go on trying again and again
+				* forever.  Let's stop when we've done all the
+				* transfers that were scheduled.
+				*
+				* We're going to be comparing start_active_frame
+				* and next_active_frame, both of which are 1
+				* before the time the packet goes on the wire,
+				* so that cancels out.  Basically if had 1
+				* transfer and we saw 1 NYET then we're done.
+				* We're getting a NYET here so if next >=
+				* (start + num_transfers) we're done. The
+				* complexity is that for all but ISOC_OUT we
+				* skip one slot.
+				*/
+				end_frnum = dwc2_frame_num_inc(
+					qh->start_active_frame,
+					qh->num_hs_transfers);
+
+				if (qh->ep_type != USB_ENDPOINT_XFER_ISOC ||
+				    qh->ep_is_in)
+					end_frnum =
+					       dwc2_frame_num_inc(end_frnum, 1);
+
+				past_end = dwc2_frame_num_le(
+					end_frnum, qh->next_active_frame);
+			}
+
+			if (past_end) {
+				/* Treat this as a transaction error. */
 #if 0
 				/*
 				 * Todo: Fix system performance so this can
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Thu Jan 28 18:20:11 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Thu, 28 Jan 2016 18:20:11 -0800
Subject: [PATCH v6 20/22] usb: dwc2: host: Properly set even/odd frame
In-Reply-To: <1454034013-24657-1-git-send-email-dianders@chromium.org>
References: <1454034013-24657-1-git-send-email-dianders@chromium.org>
Message-ID: <1454034013-24657-21-git-send-email-dianders@chromium.org>

When setting up ISO and INT transfers dwc2 needs to specify whether the
transfer is for an even or an odd frame (or microframe if the controller
is running in high speed mode).

The controller appears to use this as a simple way to figure out if a
transfer should happen right away (in the current microframe) or should
happen at the start of the next microframe.  Said another way:

- If you set "odd" and the current frame number is odd it appears that
  the controller will try to transfer right away.  Same thing if you set
  "even" and the current frame number is even.
- If the oddness you set and the oddness of the frame number are
  _different_, the transfer will be delayed until the frame number
  changes.

As I understand it, the above technique allows you to plan ahead of time
where possible by always working on the next frame.  ...but it still
allows you to properly respond immediately to things that happened in
the previous frame.

The old dwc2_hc_set_even_odd_frame() didn't really handle this concept.
It always looked at the frame number and setup the transfer to happen in
the next frame.  In some cases that meant that certain transactions
would be transferred in the wrong frame.

We'll try our best to set the even / odd to do the transfer in the
scheduled frame.  If that fails then we'll do an ugly "schedule ASAP".
We'll also modify the scheduler code to handle this and not try to
schedule a second transfer for the same frame.

Note that this change relies on the work to redo the microframe
scheduler.  It can work atop ("usb: dwc2: host: Manage frame nums better
in scheduler") but it works even better after ("usb: dwc2: host: Totally
redo the microframe scheduler").

With this change my stressful USB test (USB webcam + USB audio +
keyboards) has less audio crackling than before.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Tested-by: Heiko Stuebner <heiko at sntech.de>
Tested-by: Stefan Wahren <stefan.wahren at i2se.com>
---
Changes in v6:
- Add Heiko's Tested-by.
- Add Stefan's Tested-by.

Changes in v5: None
Changes in v4:
- Properly set even/odd frame new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/core.c      | 92 +++++++++++++++++++++++++++++++++++++++++++-
 drivers/usb/dwc2/hcd_queue.c | 11 +++++-
 2 files changed, 100 insertions(+), 3 deletions(-)

diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index a5db20f12ee4..c143f26bd9d9 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -1703,9 +1703,97 @@ static void dwc2_hc_set_even_odd_frame(struct dwc2_hsotg *hsotg,
 {
 	if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
 	    chan->ep_type == USB_ENDPOINT_XFER_ISOC) {
-		/* 1 if _next_ frame is odd, 0 if it's even */
-		if (!(dwc2_hcd_get_frame_number(hsotg) & 0x1))
+		int host_speed;
+		int xfer_ns;
+		int xfer_us;
+		int bytes_in_fifo;
+		u16 fifo_space;
+		u16 frame_number;
+		u16 wire_frame;
+
+		/*
+		 * Try to figure out if we're an even or odd frame. If we set
+		 * even and the current frame number is even the the transfer
+		 * will happen immediately.  Similar if both are odd. If one is
+		 * even and the other is odd then the transfer will happen when
+		 * the frame number ticks.
+		 *
+		 * There's a bit of a balancing act to get this right.
+		 * Sometimes we may want to send data in the current frame (AK
+		 * right away).  We might want to do this if the frame number
+		 * _just_ ticked, but we might also want to do this in order
+		 * to continue a split transaction that happened late in a
+		 * microframe (so we didn't know to queue the next transfer
+		 * until the frame number had ticked).  The problem is that we
+		 * need a lot of knowledge to know if there's actually still
+		 * time to send things or if it would be better to wait until
+		 * the next frame.
+		 *
+		 * We can look at how much time is left in the current frame
+		 * and make a guess about whether we'll have time to transfer.
+		 * We'll do that.
+		 */
+
+		/* Get speed host is running at */
+		host_speed = (chan->speed != USB_SPEED_HIGH &&
+			      !chan->do_split) ? chan->speed : USB_SPEED_HIGH;
+
+		/* See how many bytes are in the periodic FIFO right now */
+		fifo_space = (dwc2_readl(hsotg->regs + HPTXSTS) &
+			      TXSTS_FSPCAVAIL_MASK) >> TXSTS_FSPCAVAIL_SHIFT;
+		bytes_in_fifo = sizeof(u32) *
+				(hsotg->core_params->host_perio_tx_fifo_size -
+				 fifo_space);
+
+		/*
+		 * Roughly estimate bus time for everything in the periodic
+		 * queue + our new transfer.  This is "rough" because we're
+		 * using a function that makes takes into account IN/OUT
+		 * and INT/ISO and we're just slamming in one value for all
+		 * transfers.  This should be an over-estimate and that should
+		 * be OK, but we can probably tighten it.
+		 */
+		xfer_ns = usb_calc_bus_time(host_speed, false, false,
+					    chan->xfer_len + bytes_in_fifo);
+		xfer_us = NS_TO_US(xfer_ns);
+
+		/* See what frame number we'll be at by the time we finish */
+		frame_number = dwc2_hcd_get_future_frame_number(hsotg, xfer_us);
+
+		/* This is when we were scheduled to be on the wire */
+		wire_frame = dwc2_frame_num_inc(chan->qh->next_active_frame, 1);
+
+		/*
+		 * If we'd finish _after_ the frame we're scheduled in then
+		 * it's hopeless.  Just schedule right away and hope for the
+		 * best.  Note that it _might_ be wise to call back into the
+		 * scheduler to pick a better frame, but this is better than
+		 * nothing.
+		 */
+		if (dwc2_frame_num_gt(frame_number, wire_frame)) {
+			dwc2_sch_vdbg(hsotg,
+				      "QH=%p EO MISS fr=%04x=>%04x (%+d)\n",
+				      chan->qh, wire_frame, frame_number,
+				      dwc2_frame_num_dec(frame_number,
+							 wire_frame));
+			wire_frame = frame_number;
+
+			/*
+			 * We picked a different frame number; communicate this
+			 * back to the scheduler so it doesn't try to schedule
+			 * another in the same frame.
+			 *
+			 * Remember that next_active_frame is 1 before the wire
+			 * frame.
+			 */
+			chan->qh->next_active_frame =
+				dwc2_frame_num_dec(frame_number, 1);
+		}
+
+		if (wire_frame & 1)
 			*hcchar |= HCCHAR_ODDFRM;
+		else
+			*hcchar &= ~HCCHAR_ODDFRM;
 	}
 }
 
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 3abb34a5fc5b..5f909747b5a4 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -985,6 +985,14 @@ static int dwc2_next_periodic_start(struct dwc2_hsotg *hsotg,
 	 *   and next_active_frame are always 1 frame before we want things
 	 *   to be active and we assume we can still get scheduled in the
 	 *   current frame number.
+	 * - It's possible for start_active_frame (now incremented) to be
+	 *   next_active_frame if we got an EO MISS (even_odd miss) which
+	 *   basically means that we detected there wasn't enough time for
+	 *   the last packet and dwc2_hc_set_even_odd_frame() rescheduled us
+	 *   at the last second.  We want to make sure we don't schedule
+	 *   another transfer for the same frame.  My test webcam doesn't seem
+	 *   terribly upset by missing a transfer but really doesn't like when
+	 *   we do two transfers in the same frame.
 	 * - Some misses are expected.  Specifically, in order to work
 	 *   perfectly dwc2 really needs quite spectacular interrupt latency
 	 *   requirements.  It needs to be able to handle its interrupts
@@ -995,7 +1003,8 @@ static int dwc2_next_periodic_start(struct dwc2_hsotg *hsotg,
 	 *   guarantee that a system will have interrupt latency < 125 us, so
 	 *   we have to be robust to some misses.
 	 */
-	if (dwc2_frame_num_gt(prev_frame_number, qh->start_active_frame)) {
+	if (qh->start_active_frame == qh->next_active_frame ||
+	    dwc2_frame_num_gt(prev_frame_number, qh->start_active_frame)) {
 		u16 ideal_start = qh->start_active_frame;
 
 		/* Adjust interval as per gcd with plan length. */
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Thu Jan 28 18:20:09 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Thu, 28 Jan 2016 18:20:09 -0800
Subject: [PATCH v6 18/22] usb: dwc2: host: Schedule periodic right away if
 it's time
In-Reply-To: <1454034013-24657-1-git-send-email-dianders@chromium.org>
References: <1454034013-24657-1-git-send-email-dianders@chromium.org>
Message-ID: <1454034013-24657-19-git-send-email-dianders@chromium.org>

In dwc2_hcd_qh_deactivate() we will put some things on the
periodic_sched_ready list.  These things won't be taken off the ready
list until the next SOF, which might be a little late.  Let's put them
on right away.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Tested-by: Heiko Stuebner <heiko at sntech.de>
Tested-by: Stefan Wahren <stefan.wahren at i2se.com>
---
Changes in v6:
- Add Heiko's Tested-by.
- Add Stefan's Tested-by.

Changes in v5: None
Changes in v4:
- Schedule periodic right away if it's time new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/hcd_queue.c | 18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 9b3c435339ee..3abb34a5fc5b 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -1080,12 +1080,26 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 	 * Note: we purposely use the frame_number from the "hsotg" structure
 	 * since we know SOF interrupt will handle future frames.
 	 */
-	if (dwc2_frame_num_le(qh->next_active_frame, hsotg->frame_number))
+	if (dwc2_frame_num_le(qh->next_active_frame, hsotg->frame_number)) {
+		enum dwc2_transaction_type tr_type;
+
+		/*
+		 * We're bypassing the SOF handler which is normally what puts
+		 * us on the ready list because we're in a hurry and need to
+		 * try to catch up.
+		 */
+		dwc2_sch_vdbg(hsotg, "QH=%p IMM ready fn=%04x, nxt=%04x\n",
+			      qh, frame_number, qh->next_active_frame);
 		list_move_tail(&qh->qh_list_entry,
 			       &hsotg->periodic_sched_ready);
-	else
+
+		tr_type = dwc2_hcd_select_transactions(hsotg);
+		if (tr_type != DWC2_TRANSACTION_NONE)
+			dwc2_hcd_queue_transactions(hsotg, tr_type);
+	} else {
 		list_move_tail(&qh->qh_list_entry,
 			       &hsotg->periodic_sched_inactive);
+	}
 }
 
 /**
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Thu Jan 28 18:20:08 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Thu, 28 Jan 2016 18:20:08 -0800
Subject: [PATCH v6 17/22] usb: dwc2: host: Manage frame nums better in
 scheduler
In-Reply-To: <1454034013-24657-1-git-send-email-dianders@chromium.org>
References: <1454034013-24657-1-git-send-email-dianders@chromium.org>
Message-ID: <1454034013-24657-18-git-send-email-dianders@chromium.org>

The dwc2 scheduler (contained in hcd_queue.c) was a bit confusing in the
way it initted / kept track of which frames a QH was going to be active
in.  Let's clean things up a little bit in preparation for a rewrite of
the microframe scheduler.

Specifically:
* Old code would pick a frame number in dwc2_qh_init() and would try to
  pick it "in a slightly future (micro)frame".  As far as I can tell the
  reason for this was that there was a delay between dwc2_qh_init() and
  when we actually wanted to dwc2_hcd_qh_add().  ...but apparently this
  attempt to be slightly in the future wasn't enough because
  dwc2_hcd_qh_add() then had code to reset things if the frame _wasn't_
  in the future.  There's no reason not to just pick the frame later.
  For non-periodic QH we now pick the frame in dwc2_hcd_qh_add().  For
  periodic QH we pick the frame at dwc2_schedule_periodic() time.
* The old "dwc2_qh_init() actually assigned to "hsotg->frame_number".
  This doesn't seem like a great idea since that variable is supposed to
  be used to keep track of which SOF the interrupt handler has seen.
  Let's be clean: anyone who wants the current frame number (instead of
  the one as of the last interrupt) should ask for it.
* The old code wasn't terribly consistent about trying to use the frame
  that the microframe scheduler assigned to it.  In
  dwc2_sched_periodic_split() when it was scheduling the first frame it
  always "ORed" in 0x7 (!).  Since the frame goes on the wire 1 uFrame
  after next_active_frame it meant that the SSPLIT would always try for
  uFrame 0 and the transaction would happen on the low speed bus during
  uFrame 1.  This is irregardless of what the microframe scheduler
  said.
* The old code assumed it would get called to schedule the next in a
  periodic split very quickly.  That is if next_active_frame was
  0 (transfer on wire in uFrame 1) it assumed it was getting called to
  schedule the next uFrame during uFrame 1 too (so it could queue
  something up for uFrame 2).  It should be possible to actually queue
  something up for uFrame 2 while in uFrame 2 (AKA queue up ASAP).  To
  do this, code needs to look at the previously scheduled frame when
  deciding when to next be active, not look at the current frame number.
* If there was no microframe scheduler, the old code would check for
  whether we should be active using "qh->next_active_frame ==
  frame_number".  This seemed like a race waiting to happen.  ...plus
  there's no way that you wouldn't want to schedule if next_active_frame
  was actually less than frame number.

Note that this change doesn't make 100% sense on its own since it's
expecting some sanity in the frame numbers assigned by the microframe
scheduler and (as per the future patch which rewries it) I think that
the current microframe scheduler is quite insane.  However, it seems
like splitting this up from the microframe scheduler patch makes things
into smaller chunks and hopefully adds to clarity rather than reduces
it.  The two patches could certainly be squashed.  Not that in the very
least, I don't see any obvious bad behavior introduced with just this
patch.

I've attempted to keep the config parameter to disable the microframe
scheduler in tact in this change, though I'm not sure it's worth it.
Obviously the code is touched a lot so it's possible I regressed
something when the microframe scheduler is disabled, though I did some
basic testing and it seemed to work OK.  I'm still not 100% sure why you
wouldn't want the microframe scheduler (presuming it works), so maybe a
future patch (or a future version of this patch?) could remove that
parameter.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Tested-by: Heiko Stuebner <heiko at sntech.de>
Tested-by: Stefan Wahren <stefan.wahren at i2se.com>
---
Changes in v6:
- Fix bug where periodic things get scheduled too quick (Alan Stern)
- Add Heiko's Tested-by.
- Add Stefan's Tested-by.

Changes in v5: None
Changes in v4:
- Manage frame nums better in scheduler new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/hcd.h       |  10 +-
 drivers/usb/dwc2/hcd_queue.c | 351 ++++++++++++++++++++++++++++++++-----------
 2 files changed, 272 insertions(+), 89 deletions(-)

diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index 10c35585a2bd..fd266ac53a28 100644
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -244,8 +244,11 @@ enum dwc2_transaction_type {
  *                      the bus.  We'll move the qh to active here.  If the
  *                      host is in high speed mode this will be a uframe.  If
  *                      the host is in low speed mode this will be a full frame.
+ * @start_active_frame: If we are partway through a split transfer, this will be
+ *			what next_active_frame was when we started.  Otherwise
+ *			it should always be the same as next_active_frame.
+ * @assigned_uframe:    The uframe (0 -7) assigned by dwc2_find_uframe().
  * @frame_usecs:        Internal variable used by the microframe scheduler
- * @start_split_frame:  (Micro)frame at which last start split was initialized
  * @ntd:                Actual number of transfer descriptors in a list
  * @qtd_list:           List of QTDs for this QH
  * @channel:            Host channel currently processing transfers for this QH
@@ -279,8 +282,9 @@ struct dwc2_qh {
 	u16 host_us;
 	u16 host_interval;
 	u16 next_active_frame;
+	u16 start_active_frame;
+	u16 assigned_uframe;
 	u16 frame_usecs[8];
-	u16 start_split_frame;
 	u16 ntd;
 	struct list_head qtd_list;
 	struct dwc2_host_chan *channel;
@@ -746,7 +750,7 @@ do {									\
 	_qtd_ = list_entry((_qh_)->qtd_list.next, struct dwc2_qtd,	\
 			   qtd_list_entry);				\
 	if (usb_pipeint(_qtd_->urb->pipe) &&				\
-	    (_qh_)->start_split_frame != 0 && !_qtd_->complete_split) {	\
+	    (_qh_)->start_active_frame != 0 && !_qtd_->complete_split) { \
 		_hfnum_.d32 = dwc2_readl((_hcd_)->regs + HFNUM);	\
 		switch (_hfnum_.b.frnum & 0x7) {			\
 		case 7:							\
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 9ce407e5017d..9b3c435339ee 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -38,6 +38,7 @@
  * This file contains the functions to manage Queue Heads and Queue
  * Transfer Descriptors for Host mode
  */
+#include <linux/gcd.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/spinlock.h>
@@ -245,6 +246,96 @@ static int dwc2_find_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 }
 
 /**
+ * dwc2_pick_first_frame() - Choose 1st frame for qh that's already scheduled
+ *
+ * Takes a qh that has already been scheduled (which means we know we have the
+ * bandwdith reserved for us) and set the next_active_frame and the
+ * start_active_frame.
+ *
+ * This is expected to be called on qh's that weren't previously actively
+ * running.  It just picks the next frame that we can fit into without any
+ * thought about the past.
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller
+ * @qh:    QH for a periodic endpoint
+ *
+ */
+static void dwc2_pick_first_frame(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+	u16 frame_number;
+	u16 earliest_frame;
+	u16 next_active_frame;
+	u16 interval;
+
+	/*
+	 * Use the real frame number rather than the cached value as of the
+	 * last SOF to give us a little extra slop.
+	 */
+	frame_number = dwc2_hcd_get_frame_number(hsotg);
+
+	/*
+	 * We wouldn't want to start any earlier than the next frame just in
+	 * case the frame number ticks as we're doing this calculation.
+	 *
+	 * NOTE: if we could quantify how long till we actually get scheduled
+	 * we might be able to avoid the "+ 1" by looking at the upper part of
+	 * HFNUM (the FRREM field).  For now we'll just use the + 1 though.
+	 */
+	earliest_frame = dwc2_frame_num_inc(frame_number, 1);
+	next_active_frame = earliest_frame;
+
+	/* Get the "no microframe schduler" out of the way... */
+	if (hsotg->core_params->uframe_sched <= 0) {
+		if (qh->do_split)
+			/* Splits are active at microframe 0 minus 1 */
+			next_active_frame |= 0x7;
+		goto exit;
+	}
+
+	/* Adjust interval as per high speed schedule which has 8 uFrame */
+	interval = gcd(qh->host_interval, 8);
+
+	/*
+	 * We know interval must divide (HFNUM_MAX_FRNUM + 1) now that we've
+	 * done the gcd(), so it's safe to move to the beginning of the current
+	 * interval like this.
+	 *
+	 * After this we might be before earliest_frame, but don't worry,
+	 * we'll fix it...
+	 */
+	next_active_frame = (next_active_frame / interval) * interval;
+
+	/*
+	 * Actually choose to start at the frame number we've been
+	 * scheduled for.
+	 */
+	next_active_frame = dwc2_frame_num_inc(next_active_frame,
+					       qh->assigned_uframe);
+
+	/*
+	 * We actually need 1 frame before since the next_active_frame is
+	 * the frame number we'll be put on the ready list and we won't be on
+	 * the bus until 1 frame later.
+	 */
+	next_active_frame = dwc2_frame_num_dec(next_active_frame, 1);
+
+	/*
+	 * By now we might actually be before the earliest_frame.  Let's move
+	 * up intervals until we're not.
+	 */
+	while (dwc2_frame_num_gt(earliest_frame, next_active_frame))
+		next_active_frame = dwc2_frame_num_inc(next_active_frame,
+						       interval);
+
+exit:
+	qh->next_active_frame = next_active_frame;
+	qh->start_active_frame = next_active_frame;
+
+	dwc2_sch_vdbg(hsotg, "QH=%p First fn=%04x nxt=%04x\n",
+		     qh, frame_number, qh->next_active_frame);
+}
+
+/**
  * dwc2_do_reserve() - Make a periodic reservation
  *
  * Try to allocate space in the periodic schedule.  Depending on parameters
@@ -260,25 +351,9 @@ static int dwc2_do_reserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 	int status;
 
 	if (hsotg->core_params->uframe_sched > 0) {
-		int frame = -1;
-
 		status = dwc2_find_uframe(hsotg, qh);
-		if (status == 0)
-			frame = 7;
-		else if (status > 0)
-			frame = status - 1;
-
-		/* Set the new frame up */
-		if (frame >= 0) {
-			qh->next_active_frame &= ~0x7;
-			qh->next_active_frame |= (frame & 7);
-			dwc2_sch_dbg(hsotg,
-				     "QH=%p sched_p nxt=%04x, uf=%d\n",
-				     qh, qh->next_active_frame, frame);
-		}
-
-		if (status > 0)
-			status = 0;
+		if (status >= 0)
+			qh->assigned_uframe = status;
 	} else {
 		status = dwc2_periodic_channel_available(hsotg);
 		if (status) {
@@ -305,6 +380,8 @@ static int dwc2_do_reserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 	/* Update claimed usecs per (micro)frame */
 	hsotg->periodic_usecs += qh->host_us;
 
+	dwc2_pick_first_frame(hsotg, qh);
+
 	return 0;
 }
 
@@ -460,6 +537,16 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		status = dwc2_do_reserve(hsotg, qh);
 		if (status)
 			return status;
+	} else {
+		/*
+		 * It might have been a while, so make sure that frame_number
+		 * is still good.  Note: we could also try to use the similar
+		 * dwc2_next_periodic_start() but that schedules much more
+		 * tightly and we might need to hurry and queue things up.
+		 */
+		if (dwc2_frame_num_le(qh->next_active_frame,
+				      hsotg->frame_number))
+			dwc2_pick_first_frame(hsotg, qh);
 	}
 
 	qh->unreserve_pending = 0;
@@ -520,7 +607,6 @@ static void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg,
  * @urb:   Holds the information about the device/endpoint needed to initialize
  *         the QH
  */
-#define SCHEDULE_SLOP 10
 static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 			 struct dwc2_hcd_urb *urb)
 {
@@ -569,11 +655,6 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 			      qh->ep_type == USB_ENDPOINT_XFER_ISOC,
 			      bytecount));
 
-		/* Ensure frame_number corresponds to the reality */
-		hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
-		/* Start in a slightly future (micro)frame */
-		qh->next_active_frame = dwc2_frame_num_inc(hsotg->frame_number,
-						     SCHEDULE_SLOP);
 		qh->host_interval = urb->interval;
 		dwc2_sch_dbg(hsotg, "QH=%p init nxt=%04x, fn=%04x, int=%#x\n",
 			     qh, qh->next_active_frame, hsotg->frame_number,
@@ -589,8 +670,6 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 		    (dev_speed == USB_SPEED_LOW ||
 		     dev_speed == USB_SPEED_FULL)) {
 			qh->host_interval *= 8;
-			qh->next_active_frame |= 0x7;
-			qh->start_split_frame = qh->next_active_frame;
 			dwc2_sch_dbg(hsotg,
 				     "QH=%p init*8 nxt=%04x, fn=%04x, int=%#x\n",
 				     qh, qh->next_active_frame,
@@ -738,22 +817,12 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		/* QH already in a schedule */
 		return 0;
 
-	if (!dwc2_frame_num_le(qh->next_active_frame, hsotg->frame_number) &&
-			!hsotg->frame_number) {
-		u16 new_frame;
-
-		dev_dbg(hsotg->dev,
-				"reset frame number counter\n");
-		new_frame = dwc2_frame_num_inc(hsotg->frame_number,
-				SCHEDULE_SLOP);
-
-		dwc2_sch_vdbg(hsotg, "QH=%p reset nxt=%04x=>%04x\n",
-			      qh, qh->next_active_frame, new_frame);
-		qh->next_active_frame = new_frame;
-	}
-
 	/* Add the new QH to the appropriate schedule */
 	if (dwc2_qh_is_non_per(qh)) {
+		/* Schedule right away */
+		qh->start_active_frame = hsotg->frame_number;
+		qh->next_active_frame = qh->start_active_frame;
+
 		/* Always start in inactive schedule */
 		list_add_tail(&qh->qh_list_entry,
 			      &hsotg->non_periodic_sched_inactive);
@@ -807,46 +876,145 @@ void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 	}
 }
 
-/*
- * Schedule the next continuing periodic split transfer
+/**
+ * dwc2_next_for_periodic_split() - Set next_active_frame midway thru a split.
+ *
+ * This is called for setting next_active_frame for periodic splits for all but
+ * the first packet of the split.  Confusing?  I thought so...
+ *
+ * Periodic splits are single low/full speed transfers that we end up splitting
+ * up into several high speed transfers.  They always fit into one full (1 ms)
+ * frame but might be split over several microframes (125 us each).  We to put
+ * each of the parts on a very specific high speed frame.
+ *
+ * This function figures out where the next active uFrame needs to be.
+ *
+ * @hsotg:        The HCD state structure
+ * @qh:           QH for the periodic transfer.
+ * @frame_number: The current frame number.
+ *
+ * Return: number missed by (or 0 if we didn't miss).
  */
-static void dwc2_sched_periodic_split(struct dwc2_hsotg *hsotg,
-				      struct dwc2_qh *qh, u16 frame_number,
-				      int sched_next_periodic_split)
+static int dwc2_next_for_periodic_split(struct dwc2_hsotg *hsotg,
+					 struct dwc2_qh *qh, u16 frame_number)
 {
-	u16 incr;
 	u16 old_frame = qh->next_active_frame;
+	u16 prev_frame_number = dwc2_frame_num_dec(frame_number, 1);
+	int missed = 0;
+	u16 incr;
+
+	/*
+	 * Basically: increment 1 normally, but 2 right after the start split
+	 * (except for ISOC out).
+	 */
+	if (old_frame == qh->start_active_frame &&
+	    !(qh->ep_type == USB_ENDPOINT_XFER_ISOC && !qh->ep_is_in))
+		incr = 2;
+	else
+		incr = 1;
+
+	qh->next_active_frame = dwc2_frame_num_inc(old_frame, incr);
 
-	if (sched_next_periodic_split) {
+	/*
+	 * Note that it's OK for frame_number to be 1 frame past
+	 * next_active_frame.  Remember that next_active_frame is supposed to
+	 * be 1 frame _before_ when we want to be scheduled.  If we're 1 frame
+	 * past it just means schedule ASAP.
+	 *
+	 * It's _not_ OK, however, if we're more than one frame past.
+	 */
+	if (dwc2_frame_num_gt(prev_frame_number, qh->next_active_frame)) {
+		/*
+		 * OOPS, we missed.  That's actually pretty bad since
+		 * the hub will be unhappy; try ASAP I guess.
+		 */
+		missed = dwc2_frame_num_dec(prev_frame_number,
+					    qh->next_active_frame);
 		qh->next_active_frame = frame_number;
-		incr = dwc2_frame_num_inc(qh->start_split_frame, 1);
-		if (dwc2_frame_num_le(frame_number, incr)) {
-			/*
-			 * Allow one frame to elapse after start split
-			 * microframe before scheduling complete split, but
-			 * DON'T if we are doing the next start split in the
-			 * same frame for an ISOC out
-			 */
-			if (qh->ep_type != USB_ENDPOINT_XFER_ISOC ||
-			    qh->ep_is_in != 0) {
-				qh->next_active_frame = dwc2_frame_num_inc(
-					qh->next_active_frame, 1);
-			}
-		}
-	} else {
-		qh->next_active_frame =
-			dwc2_frame_num_inc(qh->start_split_frame,
-					   qh->host_interval);
-		if (dwc2_frame_num_le(qh->next_active_frame, frame_number))
-			qh->next_active_frame = frame_number;
-		qh->next_active_frame |= 0x7;
-		qh->start_split_frame = qh->next_active_frame;
 	}
 
-	dwc2_sch_vdbg(hsotg, "QH=%p next(%d) fn=%04x, nxt=%04x=>%04x (%+d)\n",
-		      qh, sched_next_periodic_split, frame_number, old_frame,
-		      qh->next_active_frame,
-		      dwc2_frame_num_dec(qh->next_active_frame, old_frame));
+	return missed;
+}
+
+/**
+ * dwc2_next_periodic_start() - Set next_active_frame for next transfer start
+ *
+ * This is called for setting next_active_frame for a periodic transfer for
+ * all cases other than midway through a periodic split.  This will also update
+ * start_active_frame.
+ *
+ * Since we _always_ keep start_active_frame as the start of the previous
+ * transfer this is normally pretty easy: we just add our interval to
+ * start_active_frame and we've got our answer.
+ *
+ * The tricks come into play if we miss.  In that case we'll look for the next
+ * slot we can fit into.
+ *
+ * @hsotg:        The HCD state structure
+ * @qh:           QH for the periodic transfer.
+ * @frame_number: The current frame number.
+ *
+ * Return: number missed by (or 0 if we didn't miss).
+ */
+static int dwc2_next_periodic_start(struct dwc2_hsotg *hsotg,
+				     struct dwc2_qh *qh, u16 frame_number)
+{
+	int missed = 0;
+	u16 interval = qh->host_interval;
+	u16 prev_frame_number = dwc2_frame_num_dec(frame_number, 1);
+
+	qh->start_active_frame = dwc2_frame_num_inc(qh->start_active_frame,
+						    interval);
+
+	/*
+	 * The dwc2_frame_num_gt() function used below won't work terribly well
+	 * with if we just incremented by a really large intervals since the
+	 * frame counter only goes to 0x3fff.  It's terribly unlikely that we
+	 * will have missed in this case anyway.  Just go to exit.  If we want
+	 * to try to do better we'll need to keep track of a bigger counter
+	 * somewhere in the driver and handle overflows.
+	 */
+	if (interval >= 0x1000)
+		goto exit;
+
+	/*
+	 * Test for misses, which is when it's too late to schedule.
+	 *
+	 * A few things to note:
+	 * - We compare against prev_frame_number since start_active_frame
+	 *   and next_active_frame are always 1 frame before we want things
+	 *   to be active and we assume we can still get scheduled in the
+	 *   current frame number.
+	 * - Some misses are expected.  Specifically, in order to work
+	 *   perfectly dwc2 really needs quite spectacular interrupt latency
+	 *   requirements.  It needs to be able to handle its interrupts
+	 *   completely within 125 us of them being asserted. That not only
+	 *   means that the dwc2 interrupt handler needs to be fast but it
+	 *   means that nothing else in the system has to block dwc2 for a long
+	 *   time.  We can help with the dwc2 parts of this, but it's hard to
+	 *   guarantee that a system will have interrupt latency < 125 us, so
+	 *   we have to be robust to some misses.
+	 */
+	if (dwc2_frame_num_gt(prev_frame_number, qh->start_active_frame)) {
+		u16 ideal_start = qh->start_active_frame;
+
+		/* Adjust interval as per gcd with plan length. */
+		interval = gcd(interval, 8);
+
+		do {
+			qh->start_active_frame = dwc2_frame_num_inc(
+				qh->start_active_frame, interval);
+		} while (dwc2_frame_num_gt(prev_frame_number,
+					   qh->start_active_frame));
+
+		missed = dwc2_frame_num_dec(qh->start_active_frame,
+					    ideal_start);
+	}
+
+exit:
+	qh->next_active_frame = qh->start_active_frame;
+
+	return missed;
 }
 
 /*
@@ -865,7 +1033,9 @@ static void dwc2_sched_periodic_split(struct dwc2_hsotg *hsotg,
 void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 			    int sched_next_periodic_split)
 {
+	u16 old_frame = qh->next_active_frame;
 	u16 frame_number;
+	int missed;
 
 	if (dbg_qh(qh))
 		dev_vdbg(hsotg->dev, "%s()\n", __func__);
@@ -878,30 +1048,39 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 		return;
 	}
 
+	/*
+	 * Use the real frame number rather than the cached value as of the
+	 * last SOF just to get us a little closer to reality.  Note that
+	 * means we don't actually know if we've already handled the SOF
+	 * interrupt for this frame.
+	 */
 	frame_number = dwc2_hcd_get_frame_number(hsotg);
 
-	if (qh->do_split) {
-		dwc2_sched_periodic_split(hsotg, qh, frame_number,
-					  sched_next_periodic_split);
-	} else {
-		qh->next_active_frame = dwc2_frame_num_inc(
-			qh->next_active_frame, qh->host_interval);
-		if (dwc2_frame_num_le(qh->next_active_frame, frame_number))
-			qh->next_active_frame = frame_number;
-	}
+	if (sched_next_periodic_split)
+		missed = dwc2_next_for_periodic_split(hsotg, qh, frame_number);
+	else
+		missed = dwc2_next_periodic_start(hsotg, qh, frame_number);
+
+	dwc2_sch_vdbg(hsotg,
+		     "QH=%p next(%d) fn=%04x, sch=%04x=>%04x (%+d) miss=%d %s\n",
+		     qh, sched_next_periodic_split, frame_number, old_frame,
+		     qh->next_active_frame,
+		     dwc2_frame_num_dec(qh->next_active_frame, old_frame),
+		missed, missed ? "MISS" : "");
 
 	if (list_empty(&qh->qtd_list)) {
 		dwc2_hcd_qh_unlink(hsotg, qh);
 		return;
 	}
+
 	/*
 	 * Remove from periodic_sched_queued and move to
 	 * appropriate queue
+	 *
+	 * Note: we purposely use the frame_number from the "hsotg" structure
+	 * since we know SOF interrupt will handle future frames.
 	 */
-	if ((hsotg->core_params->uframe_sched > 0 &&
-	     dwc2_frame_num_le(qh->next_active_frame, frame_number)) ||
-	    (hsotg->core_params->uframe_sched <= 0 &&
-	     qh->next_active_frame == frame_number))
+	if (dwc2_frame_num_le(qh->next_active_frame, hsotg->frame_number))
 		list_move_tail(&qh->qh_list_entry,
 			       &hsotg->periodic_sched_ready);
 	else
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Thu Jan 28 18:20:07 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Thu, 28 Jan 2016 18:20:07 -0800
Subject: [PATCH v6 16/22] usb: dwc2: host: Add scheduler logging for missed
 SOFs
In-Reply-To: <1454034013-24657-1-git-send-email-dianders@chromium.org>
References: <1454034013-24657-1-git-send-email-dianders@chromium.org>
Message-ID: <1454034013-24657-17-git-send-email-dianders@chromium.org>

We'll use the new "scheduler verbose debugging" macro to log missed
SOFs.  This is fast enough (assuming you configure it to use the ftrace
buffer) that we can do it without worrying about the speed hit.  The
overhead hit if the scheduler tracing is set to "no_printk" should be
near zero.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Tested-by: Heiko Stuebner <heiko at sntech.de>
Tested-by: Stefan Wahren <stefan.wahren at i2se.com>
---
Changes in v6:
- Add Heiko's Tested-by.
- Add Stefan's Tested-by.

Changes in v5: None
Changes in v4:
- Add scheduler logging for missed SOFs new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/core.h     |  3 ++-
 drivers/usb/dwc2/hcd.c      |  2 +-
 drivers/usb/dwc2/hcd_intr.c | 12 ++++++++----
 3 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 18f9e4045643..64d45a2053bb 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -809,9 +809,10 @@ struct dwc2_hsotg {
 	bool bus_suspended;
 	bool new_connection;
 
+	u16 last_frame_num;
+
 #ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
 #define FRAME_NUM_ARRAY_SIZE 1000
-	u16 last_frame_num;
 	u16 *frame_num_array;
 	u16 *last_frame_num_array;
 	int frame_num_idx;
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index fd731347daf7..f48da015fa5e 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -3084,8 +3084,8 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
 			FRAME_NUM_ARRAY_SIZE, GFP_KERNEL);
 	if (!hsotg->last_frame_num_array)
 		goto error1;
-	hsotg->last_frame_num = HFNUM_MAX_FRNUM;
 #endif
+	hsotg->last_frame_num = HFNUM_MAX_FRNUM;
 
 	/* Check if the bus driver or platform code has setup a dma_mask */
 	if (hsotg->core_params->dma_enable > 0 &&
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index d929db5e7f3f..dc285667233a 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -55,12 +55,16 @@
 /* This function is for debug only */
 static void dwc2_track_missed_sofs(struct dwc2_hsotg *hsotg)
 {
-#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
 	u16 curr_frame_number = hsotg->frame_number;
+	u16 expected = dwc2_frame_num_inc(hsotg->last_frame_num, 1);
+
+	if (expected != curr_frame_number)
+		dwc2_sch_vdbg(hsotg, "MISSED SOF %04x != %04x\n",
+			expected, curr_frame_number);
 
+#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
 	if (hsotg->frame_num_idx < FRAME_NUM_ARRAY_SIZE) {
-		if (((hsotg->last_frame_num + 1) & HFNUM_MAX_FRNUM) !=
-		    curr_frame_number) {
+		if (expected != curr_frame_number) {
 			hsotg->frame_num_array[hsotg->frame_num_idx] =
 					curr_frame_number;
 			hsotg->last_frame_num_array[hsotg->frame_num_idx] =
@@ -79,8 +83,8 @@ static void dwc2_track_missed_sofs(struct dwc2_hsotg *hsotg)
 		}
 		hsotg->dumped_frame_num_array = 1;
 	}
-	hsotg->last_frame_num = curr_frame_number;
 #endif
+	hsotg->last_frame_num = curr_frame_number;
 }
 
 static void dwc2_hc_handle_tt_clear(struct dwc2_hsotg *hsotg,
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Thu Jan 28 18:20:05 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Thu, 28 Jan 2016 18:20:05 -0800
Subject: [PATCH v6 14/22] usb: dwc2: host: Reorder things in hcd_queue.c
In-Reply-To: <1454034013-24657-1-git-send-email-dianders@chromium.org>
References: <1454034013-24657-1-git-send-email-dianders@chromium.org>
Message-ID: <1454034013-24657-15-git-send-email-dianders@chromium.org>

This no-op change just reorders a few functions in hcd_queue.c in order
to prepare for future changes.  Motivations here:

The functions dwc2_hcd_qh_free() and dwc2_hcd_qh_create() are exported
functions.  They are not called within the file.  That means that they
should be near the bottom so that they can easily call static helpers.

The function dwc2_qh_init() is only called by dwc2_hcd_qh_create() and
should move near the bottom with it.

The only reason that the dwc2_unreserve_timer_fn() timer function (and
its subroutine dwc2_do_unreserve()) were so high in the file was that
they needed to be above dwc2_qh_init().  Now that dwc2_qh_init() has
been moved down it can be moved down a bit.  A later patch will split
the reserve code out of dwc2_schedule_periodic() and the reserve
function should be near the unreserve function.  The reserve function
needs to be below dwc2_find_uframe() since it calls that.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Tested-by: Heiko Stuebner <heiko at sntech.de>
Tested-by: Stefan Wahren <stefan.wahren at i2se.com>
---
Changes in v6:
- Add Heiko's Tested-by.
- Add Stefan's Tested-by.

Changes in v5: None
Changes in v4:
- Reorder things in hcd_queue.c new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/hcd_queue.c | 600 +++++++++++++++++++++----------------------
 1 file changed, 300 insertions(+), 300 deletions(-)

diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 39f4de6279f8..8a2067bc1e62 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -57,295 +57,6 @@
 #define DWC2_UNRESERVE_DELAY (msecs_to_jiffies(5))
 
 /**
- * dwc2_do_unreserve() - Actually release the periodic reservation
- *
- * This function actually releases the periodic bandwidth that was reserved
- * by the given qh.
- *
- * @hsotg: The HCD state structure for the DWC OTG controller
- * @qh:    QH for the periodic transfer.
- */
-static void dwc2_do_unreserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
-{
-	assert_spin_locked(&hsotg->lock);
-
-	WARN_ON(!qh->unreserve_pending);
-
-	/* No more unreserve pending--we're doing it */
-	qh->unreserve_pending = false;
-
-	if (WARN_ON(!list_empty(&qh->qh_list_entry)))
-		list_del_init(&qh->qh_list_entry);
-
-	/* Update claimed usecs per (micro)frame */
-	hsotg->periodic_usecs -= qh->host_us;
-
-	if (hsotg->core_params->uframe_sched > 0) {
-		int i;
-
-		for (i = 0; i < 8; i++) {
-			hsotg->frame_usecs[i] += qh->frame_usecs[i];
-			qh->frame_usecs[i] = 0;
-		}
-	} else {
-		/* Release periodic channel reservation */
-		hsotg->periodic_channels--;
-	}
-}
-
-/**
- * dwc2_unreserve_timer_fn() - Timer function to release periodic reservation
- *
- * According to the kernel doc for usb_submit_urb() (specifically the part about
- * "Reserved Bandwidth Transfers"), we need to keep a reservation active as
- * long as a device driver keeps submitting.  Since we're using HCD_BH to give
- * back the URB we need to give the driver a little bit of time before we
- * release the reservation.  This worker is called after the appropriate
- * delay.
- *
- * @work: Pointer to a qh unreserve_work.
- */
-static void dwc2_unreserve_timer_fn(unsigned long data)
-{
-	struct dwc2_qh *qh = (struct dwc2_qh *)data;
-	struct dwc2_hsotg *hsotg = qh->hsotg;
-	unsigned long flags;
-
-	/*
-	 * Wait for the lock, or for us to be scheduled again.  We
-	 * could be scheduled again if:
-	 * - We started executing but didn't get the lock yet.
-	 * - A new reservation came in, but cancel didn't take effect
-	 *   because we already started executing.
-	 * - The timer has been kicked again.
-	 * In that case cancel and wait for the next call.
-	 */
-	while (!spin_trylock_irqsave(&hsotg->lock, flags)) {
-		if (timer_pending(&qh->unreserve_timer))
-			return;
-	}
-
-	/*
-	 * Might be no more unreserve pending if:
-	 * - We started executing but didn't get the lock yet.
-	 * - A new reservation came in, but cancel didn't take effect
-	 *   because we already started executing.
-	 *
-	 * We can't put this in the loop above because unreserve_pending needs
-	 * to be accessed under lock, so we can only check it once we got the
-	 * lock.
-	 */
-	if (qh->unreserve_pending)
-		dwc2_do_unreserve(hsotg, qh);
-
-	spin_unlock_irqrestore(&hsotg->lock, flags);
-}
-
-/**
- * dwc2_qh_init() - Initializes a QH structure
- *
- * @hsotg: The HCD state structure for the DWC OTG controller
- * @qh:    The QH to init
- * @urb:   Holds the information about the device/endpoint needed to initialize
- *         the QH
- */
-#define SCHEDULE_SLOP 10
-static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
-			 struct dwc2_hcd_urb *urb)
-{
-	int dev_speed, hub_addr, hub_port;
-	char *speed, *type;
-
-	dev_vdbg(hsotg->dev, "%s()\n", __func__);
-
-	/* Initialize QH */
-	qh->hsotg = hsotg;
-	setup_timer(&qh->unreserve_timer, dwc2_unreserve_timer_fn,
-		    (unsigned long)qh);
-	qh->ep_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
-	qh->ep_is_in = dwc2_hcd_is_pipe_in(&urb->pipe_info) ? 1 : 0;
-
-	qh->data_toggle = DWC2_HC_PID_DATA0;
-	qh->maxp = dwc2_hcd_get_mps(&urb->pipe_info);
-	INIT_LIST_HEAD(&qh->qtd_list);
-	INIT_LIST_HEAD(&qh->qh_list_entry);
-
-	/* FS/LS Endpoint on HS Hub, NOT virtual root hub */
-	dev_speed = dwc2_host_get_speed(hsotg, urb->priv);
-
-	dwc2_host_hub_info(hsotg, urb->priv, &hub_addr, &hub_port);
-
-	if ((dev_speed == USB_SPEED_LOW || dev_speed == USB_SPEED_FULL) &&
-	    hub_addr != 0 && hub_addr != 1) {
-		dev_vdbg(hsotg->dev,
-			 "QH init: EP %d: TT found at hub addr %d, for port %d\n",
-			 dwc2_hcd_get_ep_num(&urb->pipe_info), hub_addr,
-			 hub_port);
-		qh->do_split = 1;
-	}
-
-	if (qh->ep_type == USB_ENDPOINT_XFER_INT ||
-	    qh->ep_type == USB_ENDPOINT_XFER_ISOC) {
-		/* Compute scheduling parameters once and save them */
-		u32 hprt, prtspd;
-
-		/* Todo: Account for split transfers in the bus time */
-		int bytecount =
-			dwc2_hb_mult(qh->maxp) * dwc2_max_packet(qh->maxp);
-
-		qh->host_us = NS_TO_US(usb_calc_bus_time(qh->do_split ?
-			      USB_SPEED_HIGH : dev_speed, qh->ep_is_in,
-			      qh->ep_type == USB_ENDPOINT_XFER_ISOC,
-			      bytecount));
-
-		/* Ensure frame_number corresponds to the reality */
-		hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
-		/* Start in a slightly future (micro)frame */
-		qh->next_active_frame = dwc2_frame_num_inc(hsotg->frame_number,
-						     SCHEDULE_SLOP);
-		qh->host_interval = urb->interval;
-		dwc2_sch_dbg(hsotg, "QH=%p init nxt=%04x, fn=%04x, int=%#x\n",
-			     qh, qh->next_active_frame, hsotg->frame_number,
-			     qh->host_interval);
-#if 0
-		/* Increase interrupt polling rate for debugging */
-		if (qh->ep_type == USB_ENDPOINT_XFER_INT)
-			qh->host_interval = 8;
-#endif
-		hprt = dwc2_readl(hsotg->regs + HPRT0);
-		prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
-		if (prtspd == HPRT0_SPD_HIGH_SPEED &&
-		    (dev_speed == USB_SPEED_LOW ||
-		     dev_speed == USB_SPEED_FULL)) {
-			qh->host_interval *= 8;
-			qh->next_active_frame |= 0x7;
-			qh->start_split_frame = qh->next_active_frame;
-			dwc2_sch_dbg(hsotg,
-				     "QH=%p init*8 nxt=%04x, fn=%04x, int=%#x\n",
-				     qh, qh->next_active_frame,
-				     hsotg->frame_number, qh->host_interval);
-
-		}
-		dev_dbg(hsotg->dev, "interval=%d\n", qh->host_interval);
-	}
-
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH Initialized\n");
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - qh = %p\n", qh);
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Device Address = %d\n",
-		 dwc2_hcd_get_dev_addr(&urb->pipe_info));
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Endpoint %d, %s\n",
-		 dwc2_hcd_get_ep_num(&urb->pipe_info),
-		 dwc2_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT");
-
-	qh->dev_speed = dev_speed;
-
-	switch (dev_speed) {
-	case USB_SPEED_LOW:
-		speed = "low";
-		break;
-	case USB_SPEED_FULL:
-		speed = "full";
-		break;
-	case USB_SPEED_HIGH:
-		speed = "high";
-		break;
-	default:
-		speed = "?";
-		break;
-	}
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Speed = %s\n", speed);
-
-	switch (qh->ep_type) {
-	case USB_ENDPOINT_XFER_ISOC:
-		type = "isochronous";
-		break;
-	case USB_ENDPOINT_XFER_INT:
-		type = "interrupt";
-		break;
-	case USB_ENDPOINT_XFER_CONTROL:
-		type = "control";
-		break;
-	case USB_ENDPOINT_XFER_BULK:
-		type = "bulk";
-		break;
-	default:
-		type = "?";
-		break;
-	}
-
-	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Type = %s\n", type);
-
-	if (qh->ep_type == USB_ENDPOINT_XFER_INT) {
-		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - usecs = %d\n",
-			 qh->host_us);
-		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - interval = %d\n",
-			 qh->host_interval);
-	}
-}
-
-/**
- * dwc2_hcd_qh_create() - Allocates and initializes a QH
- *
- * @hsotg:        The HCD state structure for the DWC OTG controller
- * @urb:          Holds the information about the device/endpoint needed
- *                to initialize the QH
- * @atomic_alloc: Flag to do atomic allocation if needed
- *
- * Return: Pointer to the newly allocated QH, or NULL on error
- */
-struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
-					  struct dwc2_hcd_urb *urb,
-					  gfp_t mem_flags)
-{
-	struct dwc2_qh *qh;
-
-	if (!urb->priv)
-		return NULL;
-
-	/* Allocate memory */
-	qh = kzalloc(sizeof(*qh), mem_flags);
-	if (!qh)
-		return NULL;
-
-	dwc2_qh_init(hsotg, qh, urb);
-
-	if (hsotg->core_params->dma_desc_enable > 0 &&
-	    dwc2_hcd_qh_init_ddma(hsotg, qh, mem_flags) < 0) {
-		dwc2_hcd_qh_free(hsotg, qh);
-		return NULL;
-	}
-
-	return qh;
-}
-
-/**
- * dwc2_hcd_qh_free() - Frees the QH
- *
- * @hsotg: HCD instance
- * @qh:    The QH to free
- *
- * QH should already be removed from the list. QTD list should already be empty
- * if called from URB Dequeue.
- *
- * Must NOT be called with interrupt disabled or spinlock held
- */
-void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
-{
-	/* Make sure any unreserve work is finished. */
-	if (del_timer_sync(&qh->unreserve_timer)) {
-		unsigned long flags;
-
-		spin_lock_irqsave(&hsotg->lock, flags);
-		dwc2_do_unreserve(hsotg, qh);
-		spin_unlock_irqrestore(&hsotg->lock, flags);
-	}
-
-	if (qh->desc_list)
-		dwc2_hcd_qh_free_ddma(hsotg, qh);
-	kfree(qh);
-}
-
-/**
  * dwc2_periodic_channel_available() - Checks that a channel is available for a
  * periodic transfer
  *
@@ -518,19 +229,104 @@ static int dwc2_find_multi_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 
 static int dwc2_find_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
-	int ret;
+	int ret;
+
+	if (qh->dev_speed == USB_SPEED_HIGH) {
+		/* if this is a hs transaction we need a full frame */
+		ret = dwc2_find_single_uframe(hsotg, qh);
+	} else {
+		/*
+		 * if this is a fs transaction we may need a sequence
+		 * of frames
+		 */
+		ret = dwc2_find_multi_uframe(hsotg, qh);
+	}
+	return ret;
+}
+
+/**
+ * dwc2_do_unreserve() - Actually release the periodic reservation
+ *
+ * This function actually releases the periodic bandwidth that was reserved
+ * by the given qh.
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller
+ * @qh:    QH for the periodic transfer.
+ */
+static void dwc2_do_unreserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+	assert_spin_locked(&hsotg->lock);
+
+	WARN_ON(!qh->unreserve_pending);
+
+	/* No more unreserve pending--we're doing it */
+	qh->unreserve_pending = false;
+
+	if (WARN_ON(!list_empty(&qh->qh_list_entry)))
+		list_del_init(&qh->qh_list_entry);
+
+	/* Update claimed usecs per (micro)frame */
+	hsotg->periodic_usecs -= qh->host_us;
+
+	if (hsotg->core_params->uframe_sched > 0) {
+		int i;
+
+		for (i = 0; i < 8; i++) {
+			hsotg->frame_usecs[i] += qh->frame_usecs[i];
+			qh->frame_usecs[i] = 0;
+		}
+	} else {
+		/* Release periodic channel reservation */
+		hsotg->periodic_channels--;
+	}
+}
+
+/**
+ * dwc2_unreserve_timer_fn() - Timer function to release periodic reservation
+ *
+ * According to the kernel doc for usb_submit_urb() (specifically the part about
+ * "Reserved Bandwidth Transfers"), we need to keep a reservation active as
+ * long as a device driver keeps submitting.  Since we're using HCD_BH to give
+ * back the URB we need to give the driver a little bit of time before we
+ * release the reservation.  This worker is called after the appropriate
+ * delay.
+ *
+ * @work: Pointer to a qh unreserve_work.
+ */
+static void dwc2_unreserve_timer_fn(unsigned long data)
+{
+	struct dwc2_qh *qh = (struct dwc2_qh *)data;
+	struct dwc2_hsotg *hsotg = qh->hsotg;
+	unsigned long flags;
 
-	if (qh->dev_speed == USB_SPEED_HIGH) {
-		/* if this is a hs transaction we need a full frame */
-		ret = dwc2_find_single_uframe(hsotg, qh);
-	} else {
-		/*
-		 * if this is a fs transaction we may need a sequence
-		 * of frames
-		 */
-		ret = dwc2_find_multi_uframe(hsotg, qh);
+	/*
+	 * Wait for the lock, or for us to be scheduled again.  We
+	 * could be scheduled again if:
+	 * - We started executing but didn't get the lock yet.
+	 * - A new reservation came in, but cancel didn't take effect
+	 *   because we already started executing.
+	 * - The timer has been kicked again.
+	 * In that case cancel and wait for the next call.
+	 */
+	while (!spin_trylock_irqsave(&hsotg->lock, flags)) {
+		if (timer_pending(&qh->unreserve_timer))
+			return;
 	}
-	return ret;
+
+	/*
+	 * Might be no more unreserve pending if:
+	 * - We started executing but didn't get the lock yet.
+	 * - A new reservation came in, but cancel didn't take effect
+	 *   because we already started executing.
+	 *
+	 * We can't put this in the loop above because unreserve_pending needs
+	 * to be accessed under lock, so we can only check it once we got the
+	 * lock.
+	 */
+	if (qh->unreserve_pending)
+		dwc2_do_unreserve(hsotg, qh);
+
+	spin_unlock_irqrestore(&hsotg->lock, flags);
 }
 
 /**
@@ -695,6 +491,210 @@ static void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg,
 }
 
 /**
+ * dwc2_qh_init() - Initializes a QH structure
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller
+ * @qh:    The QH to init
+ * @urb:   Holds the information about the device/endpoint needed to initialize
+ *         the QH
+ */
+#define SCHEDULE_SLOP 10
+static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
+			 struct dwc2_hcd_urb *urb)
+{
+	int dev_speed, hub_addr, hub_port;
+	char *speed, *type;
+
+	dev_vdbg(hsotg->dev, "%s()\n", __func__);
+
+	/* Initialize QH */
+	qh->hsotg = hsotg;
+	setup_timer(&qh->unreserve_timer, dwc2_unreserve_timer_fn,
+		    (unsigned long)qh);
+	qh->ep_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
+	qh->ep_is_in = dwc2_hcd_is_pipe_in(&urb->pipe_info) ? 1 : 0;
+
+	qh->data_toggle = DWC2_HC_PID_DATA0;
+	qh->maxp = dwc2_hcd_get_mps(&urb->pipe_info);
+	INIT_LIST_HEAD(&qh->qtd_list);
+	INIT_LIST_HEAD(&qh->qh_list_entry);
+
+	/* FS/LS Endpoint on HS Hub, NOT virtual root hub */
+	dev_speed = dwc2_host_get_speed(hsotg, urb->priv);
+
+	dwc2_host_hub_info(hsotg, urb->priv, &hub_addr, &hub_port);
+
+	if ((dev_speed == USB_SPEED_LOW || dev_speed == USB_SPEED_FULL) &&
+	    hub_addr != 0 && hub_addr != 1) {
+		dev_vdbg(hsotg->dev,
+			 "QH init: EP %d: TT found at hub addr %d, for port %d\n",
+			 dwc2_hcd_get_ep_num(&urb->pipe_info), hub_addr,
+			 hub_port);
+		qh->do_split = 1;
+	}
+
+	if (qh->ep_type == USB_ENDPOINT_XFER_INT ||
+	    qh->ep_type == USB_ENDPOINT_XFER_ISOC) {
+		/* Compute scheduling parameters once and save them */
+		u32 hprt, prtspd;
+
+		/* Todo: Account for split transfers in the bus time */
+		int bytecount =
+			dwc2_hb_mult(qh->maxp) * dwc2_max_packet(qh->maxp);
+
+		qh->host_us = NS_TO_US(usb_calc_bus_time(qh->do_split ?
+			      USB_SPEED_HIGH : dev_speed, qh->ep_is_in,
+			      qh->ep_type == USB_ENDPOINT_XFER_ISOC,
+			      bytecount));
+
+		/* Ensure frame_number corresponds to the reality */
+		hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
+		/* Start in a slightly future (micro)frame */
+		qh->next_active_frame = dwc2_frame_num_inc(hsotg->frame_number,
+						     SCHEDULE_SLOP);
+		qh->host_interval = urb->interval;
+		dwc2_sch_dbg(hsotg, "QH=%p init nxt=%04x, fn=%04x, int=%#x\n",
+			     qh, qh->next_active_frame, hsotg->frame_number,
+			     qh->host_interval);
+#if 0
+		/* Increase interrupt polling rate for debugging */
+		if (qh->ep_type == USB_ENDPOINT_XFER_INT)
+			qh->host_interval = 8;
+#endif
+		hprt = dwc2_readl(hsotg->regs + HPRT0);
+		prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
+		if (prtspd == HPRT0_SPD_HIGH_SPEED &&
+		    (dev_speed == USB_SPEED_LOW ||
+		     dev_speed == USB_SPEED_FULL)) {
+			qh->host_interval *= 8;
+			qh->next_active_frame |= 0x7;
+			qh->start_split_frame = qh->next_active_frame;
+			dwc2_sch_dbg(hsotg,
+				     "QH=%p init*8 nxt=%04x, fn=%04x, int=%#x\n",
+				     qh, qh->next_active_frame,
+				     hsotg->frame_number, qh->host_interval);
+
+		}
+		dev_dbg(hsotg->dev, "interval=%d\n", qh->host_interval);
+	}
+
+	dev_vdbg(hsotg->dev, "DWC OTG HCD QH Initialized\n");
+	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - qh = %p\n", qh);
+	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Device Address = %d\n",
+		 dwc2_hcd_get_dev_addr(&urb->pipe_info));
+	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Endpoint %d, %s\n",
+		 dwc2_hcd_get_ep_num(&urb->pipe_info),
+		 dwc2_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT");
+
+	qh->dev_speed = dev_speed;
+
+	switch (dev_speed) {
+	case USB_SPEED_LOW:
+		speed = "low";
+		break;
+	case USB_SPEED_FULL:
+		speed = "full";
+		break;
+	case USB_SPEED_HIGH:
+		speed = "high";
+		break;
+	default:
+		speed = "?";
+		break;
+	}
+	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Speed = %s\n", speed);
+
+	switch (qh->ep_type) {
+	case USB_ENDPOINT_XFER_ISOC:
+		type = "isochronous";
+		break;
+	case USB_ENDPOINT_XFER_INT:
+		type = "interrupt";
+		break;
+	case USB_ENDPOINT_XFER_CONTROL:
+		type = "control";
+		break;
+	case USB_ENDPOINT_XFER_BULK:
+		type = "bulk";
+		break;
+	default:
+		type = "?";
+		break;
+	}
+
+	dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Type = %s\n", type);
+
+	if (qh->ep_type == USB_ENDPOINT_XFER_INT) {
+		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - usecs = %d\n",
+			 qh->host_us);
+		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - interval = %d\n",
+			 qh->host_interval);
+	}
+}
+
+/**
+ * dwc2_hcd_qh_create() - Allocates and initializes a QH
+ *
+ * @hsotg:        The HCD state structure for the DWC OTG controller
+ * @urb:          Holds the information about the device/endpoint needed
+ *                to initialize the QH
+ * @atomic_alloc: Flag to do atomic allocation if needed
+ *
+ * Return: Pointer to the newly allocated QH, or NULL on error
+ */
+struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
+					  struct dwc2_hcd_urb *urb,
+					  gfp_t mem_flags)
+{
+	struct dwc2_qh *qh;
+
+	if (!urb->priv)
+		return NULL;
+
+	/* Allocate memory */
+	qh = kzalloc(sizeof(*qh), mem_flags);
+	if (!qh)
+		return NULL;
+
+	dwc2_qh_init(hsotg, qh, urb);
+
+	if (hsotg->core_params->dma_desc_enable > 0 &&
+	    dwc2_hcd_qh_init_ddma(hsotg, qh, mem_flags) < 0) {
+		dwc2_hcd_qh_free(hsotg, qh);
+		return NULL;
+	}
+
+	return qh;
+}
+
+/**
+ * dwc2_hcd_qh_free() - Frees the QH
+ *
+ * @hsotg: HCD instance
+ * @qh:    The QH to free
+ *
+ * QH should already be removed from the list. QTD list should already be empty
+ * if called from URB Dequeue.
+ *
+ * Must NOT be called with interrupt disabled or spinlock held
+ */
+void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+	/* Make sure any unreserve work is finished. */
+	if (del_timer_sync(&qh->unreserve_timer)) {
+		unsigned long flags;
+
+		spin_lock_irqsave(&hsotg->lock, flags);
+		dwc2_do_unreserve(hsotg, qh);
+		spin_unlock_irqrestore(&hsotg->lock, flags);
+	}
+
+	if (qh->desc_list)
+		dwc2_hcd_qh_free_ddma(hsotg, qh);
+	kfree(qh);
+}
+
+/**
  * dwc2_hcd_qh_add() - Adds a QH to either the non periodic or periodic
  * schedule if it is not already in the schedule. If the QH is already in
  * the schedule, no action is taken.
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Thu Jan 28 18:20:04 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Thu, 28 Jan 2016 18:20:04 -0800
Subject: [PATCH v6 13/22] usb: dwc2: host: Rename some fields in struct dwc2_qh
In-Reply-To: <1454034013-24657-1-git-send-email-dianders@chromium.org>
References: <1454034013-24657-1-git-send-email-dianders@chromium.org>
Message-ID: <1454034013-24657-14-git-send-email-dianders@chromium.org>

This no-op change just does some renames to simplify a future patch.

1. The "interval" field is renamed to "host_interval" to make it more
   obvious that this interval may be 8 times the interval that the
   device sees (if we're doing split transactions).  A future patch will
   also add the "device_interval" field.
2. The "usecs" field is renamed to "host_us" again to make it more
   obvious that this is the time for the transaction as seen by the
   host.  For split transactions the device may see a much longer
   transaction time.  A future patch will also add "device_us".
3. The "sched_frame" field is renamed to "next_active_frame".  The name
   "sched_frame" kept confusing me because it felt like something more
   permament (the QH's reservation or something).  The name
   "next_active_frame" makes it more obvious that this field is
   constantly changing.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Tested-by: Heiko Stuebner <heiko at sntech.de>
Tested-by: Stefan Wahren <stefan.wahren at i2se.com>
---
Changes in v6:
- Add Heiko's Tested-by.
- Add Stefan's Tested-by.

Changes in v5: None
Changes in v4:
- Rename some fields in struct dwc2_qh new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/hcd.h       |  20 ++++----
 drivers/usb/dwc2/hcd_ddma.c  |  37 ++++++++-------
 drivers/usb/dwc2/hcd_intr.c  |  10 ++--
 drivers/usb/dwc2/hcd_queue.c | 107 ++++++++++++++++++++++---------------------
 4 files changed, 92 insertions(+), 82 deletions(-)

diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index 79473ea35bd6..10c35585a2bd 100644
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -236,10 +236,14 @@ enum dwc2_transaction_type {
  * @do_split:           Full/low speed endpoint on high-speed hub requires split
  * @td_first:           Index of first activated isochronous transfer descriptor
  * @td_last:            Index of last activated isochronous transfer descriptor
- * @usecs:              Bandwidth in microseconds per (micro)frame
- * @interval:           Interval between transfers in (micro)frames
- * @sched_frame:        (Micro)frame to initialize a periodic transfer.
- *                      The transfer executes in the following (micro)frame.
+ * @host_us:            Bandwidth in microseconds per transfer as seen by host
+ * @host_interval:      Interval between transfers as seen by the host.  If
+ *                      the host is high speed and the device is low speed this
+ *                      will be 8 times device interval.
+ * @next_active_frame:  (Micro)frame before we next need to put something on
+ *                      the bus.  We'll move the qh to active here.  If the
+ *                      host is in high speed mode this will be a uframe.  If
+ *                      the host is in low speed mode this will be a full frame.
  * @frame_usecs:        Internal variable used by the microframe scheduler
  * @start_split_frame:  (Micro)frame at which last start split was initialized
  * @ntd:                Actual number of transfer descriptors in a list
@@ -272,9 +276,9 @@ struct dwc2_qh {
 	u8 do_split;
 	u8 td_first;
 	u8 td_last;
-	u16 usecs;
-	u16 interval;
-	u16 sched_frame;
+	u16 host_us;
+	u16 host_interval;
+	u16 next_active_frame;
 	u16 frame_usecs[8];
 	u16 start_split_frame;
 	u16 ntd;
@@ -651,7 +655,7 @@ static inline u16 dwc2_hcd_get_ep_bandwidth(struct dwc2_hsotg *hsotg,
 		return 0;
 	}
 
-	return qh->usecs;
+	return qh->host_us;
 }
 
 extern void dwc2_hcd_save_data_toggle(struct dwc2_hsotg *hsotg,
diff --git a/drivers/usb/dwc2/hcd_ddma.c b/drivers/usb/dwc2/hcd_ddma.c
index 16b261cfa92d..26b00270ca0b 100644
--- a/drivers/usb/dwc2/hcd_ddma.c
+++ b/drivers/usb/dwc2/hcd_ddma.c
@@ -81,7 +81,7 @@ static u16 dwc2_max_desc_num(struct dwc2_qh *qh)
 static u16 dwc2_frame_incr_val(struct dwc2_qh *qh)
 {
 	return qh->dev_speed == USB_SPEED_HIGH ?
-	       (qh->interval + 8 - 1) / 8 : qh->interval;
+	       (qh->host_interval + 8 - 1) / 8 : qh->host_interval;
 }
 
 static int dwc2_desc_list_alloc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
@@ -252,7 +252,7 @@ static void dwc2_update_frame_list(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 	chan = qh->channel;
 	inc = dwc2_frame_incr_val(qh);
 	if (qh->ep_type == USB_ENDPOINT_XFER_ISOC)
-		i = dwc2_frame_list_idx(qh->sched_frame);
+		i = dwc2_frame_list_idx(qh->next_active_frame);
 	else
 		i = 0;
 
@@ -278,13 +278,13 @@ static void dwc2_update_frame_list(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 		return;
 
 	chan->schinfo = 0;
-	if (chan->speed == USB_SPEED_HIGH && qh->interval) {
+	if (chan->speed == USB_SPEED_HIGH && qh->host_interval) {
 		j = 1;
 		/* TODO - check this */
-		inc = (8 + qh->interval - 1) / qh->interval;
+		inc = (8 + qh->host_interval - 1) / qh->host_interval;
 		for (i = 0; i < inc; i++) {
 			chan->schinfo |= j;
-			j = j << qh->interval;
+			j = j << qh->host_interval;
 		}
 	} else {
 		chan->schinfo = 0xff;
@@ -431,7 +431,10 @@ static u16 dwc2_calc_starting_frame(struct dwc2_hsotg *hsotg,
 
 	hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
 
-	/* sched_frame is always frame number (not uFrame) both in FS and HS! */
+	/*
+	 * next_active_frame is always frame number (not uFrame) both in FS
+	 * and HS!
+	 */
 
 	/*
 	 * skip_frames is used to limit activated descriptors number
@@ -514,13 +517,13 @@ static u16 dwc2_recalc_initial_desc_idx(struct dwc2_hsotg *hsotg,
 		 */
 		fr_idx_tmp = dwc2_frame_list_idx(frame);
 		fr_idx = (FRLISTEN_64_SIZE +
-			  dwc2_frame_list_idx(qh->sched_frame) - fr_idx_tmp)
-			 % dwc2_frame_incr_val(qh);
+			  dwc2_frame_list_idx(qh->next_active_frame) -
+			  fr_idx_tmp) % dwc2_frame_incr_val(qh);
 		fr_idx = (fr_idx + fr_idx_tmp) % FRLISTEN_64_SIZE;
 	} else {
-		qh->sched_frame = dwc2_calc_starting_frame(hsotg, qh,
+		qh->next_active_frame = dwc2_calc_starting_frame(hsotg, qh,
 							   &skip_frames);
-		fr_idx = dwc2_frame_list_idx(qh->sched_frame);
+		fr_idx = dwc2_frame_list_idx(qh->next_active_frame);
 	}
 
 	qh->td_first = qh->td_last = dwc2_frame_to_desc_idx(qh, fr_idx);
@@ -583,7 +586,7 @@ static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
 	u16 next_idx;
 
 	idx = qh->td_last;
-	inc = qh->interval;
+	inc = qh->host_interval;
 	hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
 	cur_idx = dwc2_frame_list_idx(hsotg->frame_number);
 	next_idx = dwc2_desclist_idx_inc(qh->td_last, inc, qh->dev_speed);
@@ -605,11 +608,11 @@ static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
 		}
 	}
 
-	if (qh->interval) {
-		ntd_max = (dwc2_max_desc_num(qh) + qh->interval - 1) /
-				qh->interval;
+	if (qh->host_interval) {
+		ntd_max = (dwc2_max_desc_num(qh) + qh->host_interval - 1) /
+				qh->host_interval;
 		if (skip_frames && !qh->channel)
-			ntd_max -= skip_frames / qh->interval;
+			ntd_max -= skip_frames / qh->host_interval;
 	}
 
 	max_xfer_size = qh->dev_speed == USB_SPEED_HIGH ?
@@ -1029,7 +1032,7 @@ static void dwc2_complete_isoc_xfer_ddma(struct dwc2_hsotg *hsotg,
 							  idx);
 			if (rc < 0)
 				return;
-			idx = dwc2_desclist_idx_inc(idx, qh->interval,
+			idx = dwc2_desclist_idx_inc(idx, qh->host_interval,
 						    chan->speed);
 			if (!rc)
 				continue;
@@ -1039,7 +1042,7 @@ static void dwc2_complete_isoc_xfer_ddma(struct dwc2_hsotg *hsotg,
 
 			/* rc == DWC2_CMPL_STOP */
 
-			if (qh->interval >= 32)
+			if (qh->host_interval >= 32)
 				goto stop_scan;
 
 			qh->td_first = idx;
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index fe44870f84eb..d929db5e7f3f 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -148,9 +148,11 @@ static void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
 	while (qh_entry != &hsotg->periodic_sched_inactive) {
 		qh = list_entry(qh_entry, struct dwc2_qh, qh_list_entry);
 		qh_entry = qh_entry->next;
-		if (dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number)) {
-			dwc2_sch_vdbg(hsotg, "QH=%p ready fn=%04x, sch=%04x\n",
-				      qh, hsotg->frame_number, qh->sched_frame);
+		if (dwc2_frame_num_le(qh->next_active_frame,
+				      hsotg->frame_number)) {
+			dwc2_sch_vdbg(hsotg, "QH=%p ready fn=%04x, nxt=%04x\n",
+				      qh, hsotg->frame_number,
+				      qh->next_active_frame);
 
 			/*
 			 * Move QH to the ready list to be executed next
@@ -1360,7 +1362,7 @@ static void dwc2_hc_nyet_intr(struct dwc2_hsotg *hsotg,
 			int frnum = dwc2_hcd_get_frame_number(hsotg);
 
 			if (dwc2_full_frame_num(frnum) !=
-			    dwc2_full_frame_num(chan->qh->sched_frame)) {
+			    dwc2_full_frame_num(chan->qh->next_active_frame)) {
 				/*
 				 * No longer in the same full speed frame.
 				 * Treat this as a transaction error.
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index b9e4867e1afd..39f4de6279f8 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -78,7 +78,7 @@ static void dwc2_do_unreserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		list_del_init(&qh->qh_list_entry);
 
 	/* Update claimed usecs per (micro)frame */
-	hsotg->periodic_usecs -= qh->usecs;
+	hsotg->periodic_usecs -= qh->host_us;
 
 	if (hsotg->core_params->uframe_sched > 0) {
 		int i;
@@ -193,40 +193,40 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 		int bytecount =
 			dwc2_hb_mult(qh->maxp) * dwc2_max_packet(qh->maxp);
 
-		qh->usecs = NS_TO_US(usb_calc_bus_time(qh->do_split ?
-				USB_SPEED_HIGH : dev_speed, qh->ep_is_in,
-				qh->ep_type == USB_ENDPOINT_XFER_ISOC,
-				bytecount));
+		qh->host_us = NS_TO_US(usb_calc_bus_time(qh->do_split ?
+			      USB_SPEED_HIGH : dev_speed, qh->ep_is_in,
+			      qh->ep_type == USB_ENDPOINT_XFER_ISOC,
+			      bytecount));
 
 		/* Ensure frame_number corresponds to the reality */
 		hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
 		/* Start in a slightly future (micro)frame */
-		qh->sched_frame = dwc2_frame_num_inc(hsotg->frame_number,
+		qh->next_active_frame = dwc2_frame_num_inc(hsotg->frame_number,
 						     SCHEDULE_SLOP);
-		qh->interval = urb->interval;
-		dwc2_sch_dbg(hsotg, "QH=%p init sch=%04x, fn=%04x, int=%#x\n",
-			     qh, qh->sched_frame, hsotg->frame_number,
-			     qh->interval);
+		qh->host_interval = urb->interval;
+		dwc2_sch_dbg(hsotg, "QH=%p init nxt=%04x, fn=%04x, int=%#x\n",
+			     qh, qh->next_active_frame, hsotg->frame_number,
+			     qh->host_interval);
 #if 0
 		/* Increase interrupt polling rate for debugging */
 		if (qh->ep_type == USB_ENDPOINT_XFER_INT)
-			qh->interval = 8;
+			qh->host_interval = 8;
 #endif
 		hprt = dwc2_readl(hsotg->regs + HPRT0);
 		prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
 		if (prtspd == HPRT0_SPD_HIGH_SPEED &&
 		    (dev_speed == USB_SPEED_LOW ||
 		     dev_speed == USB_SPEED_FULL)) {
-			qh->interval *= 8;
-			qh->sched_frame |= 0x7;
-			qh->start_split_frame = qh->sched_frame;
+			qh->host_interval *= 8;
+			qh->next_active_frame |= 0x7;
+			qh->start_split_frame = qh->next_active_frame;
 			dwc2_sch_dbg(hsotg,
-				     "QH=%p init*8 sch=%04x, fn=%04x, int=%#x\n",
-				     qh, qh->sched_frame, hsotg->frame_number,
-				     qh->interval);
+				     "QH=%p init*8 nxt=%04x, fn=%04x, int=%#x\n",
+				     qh, qh->next_active_frame,
+				     hsotg->frame_number, qh->host_interval);
 
 		}
-		dev_dbg(hsotg->dev, "interval=%d\n", qh->interval);
+		dev_dbg(hsotg->dev, "interval=%d\n", qh->host_interval);
 	}
 
 	dev_vdbg(hsotg->dev, "DWC OTG HCD QH Initialized\n");
@@ -277,9 +277,9 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 
 	if (qh->ep_type == USB_ENDPOINT_XFER_INT) {
 		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - usecs = %d\n",
-			 qh->usecs);
+			 qh->host_us);
 		dev_vdbg(hsotg->dev, "DWC OTG HCD QH - interval = %d\n",
-			 qh->interval);
+			 qh->host_interval);
 	}
 }
 
@@ -404,19 +404,19 @@ static int dwc2_check_periodic_bandwidth(struct dwc2_hsotg *hsotg,
 		 * High speed mode
 		 * Max periodic usecs is 80% x 125 usec = 100 usec
 		 */
-		max_claimed_usecs = 100 - qh->usecs;
+		max_claimed_usecs = 100 - qh->host_us;
 	} else {
 		/*
 		 * Full speed mode
 		 * Max periodic usecs is 90% x 1000 usec = 900 usec
 		 */
-		max_claimed_usecs = 900 - qh->usecs;
+		max_claimed_usecs = 900 - qh->host_us;
 	}
 
 	if (hsotg->periodic_usecs > max_claimed_usecs) {
 		dev_err(hsotg->dev,
 			"%s: already claimed usecs %d, required usecs %d\n",
-			__func__, hsotg->periodic_usecs, qh->usecs);
+			__func__, hsotg->periodic_usecs, qh->host_us);
 		status = -ENOSPC;
 	}
 
@@ -443,7 +443,7 @@ void dwc2_hcd_init_usecs(struct dwc2_hsotg *hsotg)
 
 static int dwc2_find_single_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
-	unsigned short utime = qh->usecs;
+	unsigned short utime = qh->host_us;
 	int i;
 
 	for (i = 0; i < 8; i++) {
@@ -462,7 +462,7 @@ static int dwc2_find_single_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
  */
 static int dwc2_find_multi_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
-	unsigned short utime = qh->usecs;
+	unsigned short utime = qh->host_us;
 	unsigned short xtime;
 	int t_left;
 	int i;
@@ -608,11 +608,11 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 
 			/* Set the new frame up */
 			if (frame >= 0) {
-				qh->sched_frame &= ~0x7;
-				qh->sched_frame |= (frame & 7);
+				qh->next_active_frame &= ~0x7;
+				qh->next_active_frame |= (frame & 7);
 				dwc2_sch_dbg(hsotg,
-					     "QH=%p sched_p sch=%04x, uf=%d\n",
-					     qh, qh->sched_frame, frame);
+					     "QH=%p sched_p nxt=%04x, uf=%d\n",
+					     qh, qh->next_active_frame, frame);
 			}
 
 			if (status > 0)
@@ -641,7 +641,7 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 			hsotg->periodic_channels++;
 
 		/* Update claimed usecs per (micro)frame */
-		hsotg->periodic_usecs += qh->usecs;
+		hsotg->periodic_usecs += qh->host_us;
 	}
 
 	qh->unreserve_pending = 0;
@@ -716,7 +716,7 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		/* QH already in a schedule */
 		return 0;
 
-	if (!dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number) &&
+	if (!dwc2_frame_num_le(qh->next_active_frame, hsotg->frame_number) &&
 			!hsotg->frame_number) {
 		u16 new_frame;
 
@@ -725,9 +725,9 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		new_frame = dwc2_frame_num_inc(hsotg->frame_number,
 				SCHEDULE_SLOP);
 
-		dwc2_sch_vdbg(hsotg, "QH=%p reset sch=%04x=>%04x\n",
-			      qh, qh->sched_frame, new_frame);
-		qh->sched_frame = new_frame;
+		dwc2_sch_vdbg(hsotg, "QH=%p reset nxt=%04x=>%04x\n",
+			      qh, qh->next_active_frame, new_frame);
+		qh->next_active_frame = new_frame;
 	}
 
 	/* Add the new QH to the appropriate schedule */
@@ -793,10 +793,10 @@ static void dwc2_sched_periodic_split(struct dwc2_hsotg *hsotg,
 				      int sched_next_periodic_split)
 {
 	u16 incr;
-	u16 old_frame = qh->sched_frame;
+	u16 old_frame = qh->next_active_frame;
 
 	if (sched_next_periodic_split) {
-		qh->sched_frame = frame_number;
+		qh->next_active_frame = frame_number;
 		incr = dwc2_frame_num_inc(qh->start_split_frame, 1);
 		if (dwc2_frame_num_le(frame_number, incr)) {
 			/*
@@ -807,23 +807,24 @@ static void dwc2_sched_periodic_split(struct dwc2_hsotg *hsotg,
 			 */
 			if (qh->ep_type != USB_ENDPOINT_XFER_ISOC ||
 			    qh->ep_is_in != 0) {
-				qh->sched_frame =
-					dwc2_frame_num_inc(qh->sched_frame, 1);
+				qh->next_active_frame = dwc2_frame_num_inc(
+					qh->next_active_frame, 1);
 			}
 		}
 	} else {
-		qh->sched_frame = dwc2_frame_num_inc(qh->start_split_frame,
-						     qh->interval);
-		if (dwc2_frame_num_le(qh->sched_frame, frame_number))
-			qh->sched_frame = frame_number;
-		qh->sched_frame |= 0x7;
-		qh->start_split_frame = qh->sched_frame;
+		qh->next_active_frame =
+			dwc2_frame_num_inc(qh->start_split_frame,
+					   qh->host_interval);
+		if (dwc2_frame_num_le(qh->next_active_frame, frame_number))
+			qh->next_active_frame = frame_number;
+		qh->next_active_frame |= 0x7;
+		qh->start_split_frame = qh->next_active_frame;
 	}
 
-	dwc2_sch_vdbg(hsotg, "QH=%p next(%d) fn=%04x, sch=%04x=>%04x (%+d)\n",
+	dwc2_sch_vdbg(hsotg, "QH=%p next(%d) fn=%04x, nxt=%04x=>%04x (%+d)\n",
 		      qh, sched_next_periodic_split, frame_number, old_frame,
-		      qh->sched_frame,
-		      dwc2_frame_num_dec(qh->sched_frame, old_frame));
+		      qh->next_active_frame,
+		      dwc2_frame_num_dec(qh->next_active_frame, old_frame));
 }
 
 /*
@@ -861,10 +862,10 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 		dwc2_sched_periodic_split(hsotg, qh, frame_number,
 					  sched_next_periodic_split);
 	} else {
-		qh->sched_frame = dwc2_frame_num_inc(qh->sched_frame,
-						     qh->interval);
-		if (dwc2_frame_num_le(qh->sched_frame, frame_number))
-			qh->sched_frame = frame_number;
+		qh->next_active_frame = dwc2_frame_num_inc(
+			qh->next_active_frame, qh->host_interval);
+		if (dwc2_frame_num_le(qh->next_active_frame, frame_number))
+			qh->next_active_frame = frame_number;
 	}
 
 	if (list_empty(&qh->qtd_list)) {
@@ -876,9 +877,9 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
 	 * appropriate queue
 	 */
 	if ((hsotg->core_params->uframe_sched > 0 &&
-	     dwc2_frame_num_le(qh->sched_frame, frame_number)) ||
+	     dwc2_frame_num_le(qh->next_active_frame, frame_number)) ||
 	    (hsotg->core_params->uframe_sched <= 0 &&
-	     qh->sched_frame == frame_number))
+	     qh->next_active_frame == frame_number))
 		list_move_tail(&qh->qh_list_entry,
 			       &hsotg->periodic_sched_ready);
 	else
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Thu Jan 28 18:20:03 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Thu, 28 Jan 2016 18:20:03 -0800
Subject: [PATCH v6 12/22] usb: dwc2: host: Use periodic interrupt even with DMA
In-Reply-To: <1454034013-24657-1-git-send-email-dianders@chromium.org>
References: <1454034013-24657-1-git-send-email-dianders@chromium.org>
Message-ID: <1454034013-24657-13-git-send-email-dianders@chromium.org>

The old code in dwc2_process_periodic_channels() would only enable the
"periodic empty" interrupt if we weren't using DMA.  That wasn't right
since we can still get into cases where we have small FIFOs even on
systems that have DMA (the rk3288 is a prime example).

Let's always enable/disable the "periodic empty" when appropriate.  As
part of this:

* Always call dwc2_process_periodic_channels() even if there's nothing
  in periodic_sched_assigned (we move the queue empty check so we still
  avoid the extra work).  That will make extra certain that we will
  properly disable the "periodic empty" interrupt even if there's
  nothing queued up.

* Move the enable of "periodic empty" due to non-empty
  periodic_sched_assigned to be for slave mode (non-DMA mode) only.
  Presumably this was the original intention of the check for DMA since
  it seems to match the comments above where in slave mode we leave
  things on the assigned queue.

Note that even before this change slave mode didn't work for me, so I
can't say for sure that my understanding of slave mode is correct.
However, this shouldn't change anything for slave mode so if slave mode
worked for someone in the past it ought to still work.

With this change, I no longer get constant misses reported by my other
debugging code (and with future patches) when I've got:
* Rockchip rk3288 Chromebook, using port ff540000
  -> Pluggable 7-port Hub with Charging (powered)
     -> Microsoft Wireless Keyboard 2000 in port 1.
     -> Das Keyboard in port 2.
     -> Jabra Speaker in port 3
     -> Logitech, Inc. Webcam C600 in port 4
     -> Microsoft Sidewinder X6 Keyboard in port 5

...and I'm playing music on the USB speaker and capturing video from the
webcam.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Tested-by: Heiko Stuebner <heiko at sntech.de>
Tested-by: Stefan Wahren <stefan.wahren at i2se.com>
---
Changes in v6:
- Add Heiko's Tested-by.
- Add Stefan's Tested-by.

Changes in v5: None
Changes in v4:
- Use periodic interrupt even with DMA new for v4.

Changes in v3: None
Changes in v2: None

 drivers/usb/dwc2/hcd.c | 71 +++++++++++++++++++++++---------------------------
 1 file changed, 32 insertions(+), 39 deletions(-)

diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 40558478a192..fd731347daf7 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -1109,10 +1109,14 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg)
 	u32 fspcavail;
 	u32 gintmsk;
 	int status;
-	int no_queue_space = 0;
-	int no_fifo_space = 0;
+	bool no_queue_space = false;
+	bool no_fifo_space = false;
 	u32 qspcavail;
 
+	/* If empty list then just adjust interrupt enables */
+	if (list_empty(&hsotg->periodic_sched_assigned))
+		goto exit;
+
 	if (dbg_perio())
 		dev_vdbg(hsotg->dev, "Queue periodic transactions\n");
 
@@ -1190,42 +1194,32 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg)
 		}
 	}
 
-	if (hsotg->core_params->dma_enable <= 0) {
-		tx_status = dwc2_readl(hsotg->regs + HPTXSTS);
-		qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >>
-			    TXSTS_QSPCAVAIL_SHIFT;
-		fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >>
-			    TXSTS_FSPCAVAIL_SHIFT;
-		if (dbg_perio()) {
-			dev_vdbg(hsotg->dev,
-				 "  P Tx Req Queue Space Avail (after queue): %d\n",
-				 qspcavail);
-			dev_vdbg(hsotg->dev,
-				 "  P Tx FIFO Space Avail (after queue): %d\n",
-				 fspcavail);
-		}
-
-		if (!list_empty(&hsotg->periodic_sched_assigned) ||
-		    no_queue_space || no_fifo_space) {
-			/*
-			 * May need to queue more transactions as the request
-			 * queue or Tx FIFO empties. Enable the periodic Tx
-			 * FIFO empty interrupt. (Always use the half-empty
-			 * level to ensure that new requests are loaded as
-			 * soon as possible.)
-			 */
-			gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+exit:
+	if (no_queue_space || no_fifo_space ||
+	    (hsotg->core_params->dma_enable <= 0 &&
+	     !list_empty(&hsotg->periodic_sched_assigned))) {
+		/*
+		 * May need to queue more transactions as the request
+		 * queue or Tx FIFO empties. Enable the periodic Tx
+		 * FIFO empty interrupt. (Always use the half-empty
+		 * level to ensure that new requests are loaded as
+		 * soon as possible.)
+		 */
+		gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+		if (!(gintmsk & GINTSTS_PTXFEMP)) {
 			gintmsk |= GINTSTS_PTXFEMP;
 			dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
-		} else {
-			/*
-			 * Disable the Tx FIFO empty interrupt since there are
-			 * no more transactions that need to be queued right
-			 * now. This function is called from interrupt
-			 * handlers to queue more transactions as transfer
-			 * states change.
-			 */
-			gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+		}
+	} else {
+		/*
+		 * Disable the Tx FIFO empty interrupt since there are
+		 * no more transactions that need to be queued right
+		 * now. This function is called from interrupt
+		 * handlers to queue more transactions as transfer
+		 * states change.
+		*/
+		gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+		if (gintmsk & GINTSTS_PTXFEMP) {
 			gintmsk &= ~GINTSTS_PTXFEMP;
 			dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
 		}
@@ -1372,9 +1366,8 @@ void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg,
 	dev_vdbg(hsotg->dev, "Queue Transactions\n");
 #endif
 	/* Process host channels associated with periodic transfers */
-	if ((tr_type == DWC2_TRANSACTION_PERIODIC ||
-	     tr_type == DWC2_TRANSACTION_ALL) &&
-	    !list_empty(&hsotg->periodic_sched_assigned))
+	if (tr_type == DWC2_TRANSACTION_PERIODIC ||
+	    tr_type == DWC2_TRANSACTION_ALL)
 		dwc2_process_periodic_channels(hsotg);
 
 	/* Process host channels associated with non-periodic transfers */
-- 
2.7.0.rc3.207.g0ac5344



From dianders at chromium.org  Thu Jan 28 18:20:00 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Thu, 28 Jan 2016 18:20:00 -0800
Subject: [PATCH v6 09/22] usb: dwc2: host: Giveback URB in tasklet context
In-Reply-To: <1454034013-24657-1-git-send-email-dianders@chromium.org>
References: <1454034013-24657-1-git-send-email-dianders@chromium.org>
Message-ID: <1454034013-24657-10-git-send-email-dianders@chromium.org>

In commit 94dfd7edfd5c ("USB: HCD: support giveback of URB in tasklet
context") support was added to give back the URB in tasklet context.
Let's take advantage of this in dwc2.

This speeds up the dwc2 interrupt handler considerably.

Note that this requires the change ("usb: dwc2: host: Add a delay before
releasing periodic bandwidth") to come first.

Note that, as per Alan Stern in
<https://patchwork.kernel.org/patch/7555771/>, we also need to make sure
that the extra delay before the device drivers submit more data doesn't
break the scheduler.  At the moment the scheduler is pretty broken (see
future patches) so it's hard to be 100% certain, but I have yet to see
any new breakage introduced by this delay.  ...and speeding up interrupt
processing for dwc2 is a huge deal because it means we've got a better
chance of not missing SOF interrupts.  That means we've got an overall
win here.

Note that when playing USB audio and using a USB webcam and having
several USB keyboards plugged in, the crackling on the USB audio device
is noticably reduced with this patch.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Tested-by: Heiko Stuebner <heiko at sntech.de>
Tested-by: Stefan Wahren <stefan.wahren at i2se.com>
---
Changes in v6:
- Add Stefan's Tested-by.

Changes in v5: None
Changes in v4:
- A bit earlier in the list of patches than in v3.

Changes in v3: None
Changes in v2:
- Commit message now says that URB giveback change needs delay change.

 drivers/usb/dwc2/hcd.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 0b6ebc7fff3f..40558478a192 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -2341,9 +2341,7 @@ void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
 	kfree(qtd->urb);
 	qtd->urb = NULL;
 
-	spin_unlock(&hsotg->lock);
 	usb_hcd_giveback_urb(dwc2_hsotg_to_hcd(hsotg), urb, status);
-	spin_lock(&hsotg->lock);
 }
 
 /*
@@ -2964,7 +2962,7 @@ static struct hc_driver dwc2_hc_driver = {
 	.hcd_priv_size = sizeof(struct wrapper_priv_data),
 
 	.irq = _dwc2_hcd_irq,
-	.flags = HCD_MEMORY | HCD_USB2,
+	.flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
 
 	.start = _dwc2_hcd_start,
 	.stop = _dwc2_hcd_stop,
-- 
2.7.0.rc3.207.g0ac5344



From robh at kernel.org  Thu Jan 28 19:01:15 2016
From: robh at kernel.org (Rob Herring)
Date: Thu, 28 Jan 2016 21:01:15 -0600
Subject: [PATCH] Documentation: rockchip-dw-mshc: add RK3036 dw-mshc
 description
In-Reply-To: <1453775654-16105-1-git-send-email-shawn.lin@rock-chips.com>
References: <1453775654-16105-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <20160129030115.GA11954@rob-hp-laptop>

On Tue, Jan 26, 2016 at 10:34:14AM +0800, Shawn Lin wrote:
> rk3036 dtsi file add dw-mshc compatible "rockchip,rk3036-dw-mshc"
> but didn't add it into rockchip-dw-mshc.txt.
> 
> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
> ---
> 
>  Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt | 1 +
>  1 file changed, 1 insertion(+)

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


From ykk at rock-chips.com  Thu Jan 28 22:42:48 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 29 Jan 2016 14:42:48 +0800
Subject: [PATCH v8 0/2] Add Rockchip Inno-HDMI driver
Message-ID: <1454049768-30872-1-git-send-email-ykk@rock-chips.com>


Here are a brief introduction to Innosilicon HDMI IP:
  - Support HDMI 1.4a, HDCP 1.2 and DVI 1.0 standard compliant transmitter
  - Support HDMI1.4 a/b 3D function defined in HDMI 1.4 a/b spec
  - Digital video interface supports a pixel size of 24, 30, 36, 48bits color depth in RGB
  - S/PDIF output supports PCM, Dolby Digital, DTS digital audio transmission
    (32-192kHz Fs) using IEC60958 and IEC 61937
  - The EDID and CEC function are also supported by Innosilicon HDMI Transmitter Controlle

Changes in v8:
- Don't check whether encoder output format is RGB colorspace, cause driver
  default configure the output colorspace to RGB. (ZhengYang)
- Correct the check condition in inno_hdmi_config_video_csc() (ZhengYang)
    - if (data->enc_out_format == data->enc_out_format) {
    + if (data->enc_in_format == data->enc_out_format) {

Changes in v7:
- Correct the module licnese statement (Paul)
     - MODULE_LICENSE("GPL");
     + MODULE_LICENSE("GPLv2");
- Start indentation with tabs and fix the misspell in Kconfig (Paul)
- Carry the lost device-binding document (Heiko)

Changes in v6:
- Rebase the Makefile/Kconfig files which add by Chris's rockchip-mipi driver (Caeser)

Changes in v5:
- Use hdmi_infoframe helper functions to packed the infoframe (Russell)
- Remove the unused double wait_for_completion_timeout for ddc transfer (Russell)
- Remove the unused local variable in "inno_hdmi_i2c_write()" function (Russell)

Changes in v4:
- Modify the commit title "drm/rockchip: hdmi: ..." (Mark)
- Correct the "DKMS" to "DPMS" (Mark)
- Fix over 80 characters problems (Mark)
- Remove encoder .prepare/.commit helper functions, and move the vop mode
configure function into encoder .enable helper functions. (Mark)

Changes in v3:
- Use encoder enable/disable function, and remove the encoder DPMS function
- Keep HDMI PLL power on in standby mode

Changes in v2:
- Using DRM atomic helper functions for connector init (Mark)
- Remove "hdmi->connector.encoder = encoder;" (Mark)
- Add the Acked-by tags from Rob
- Correct the misspell "rk3036-dw-hdmi" (Heiko)

Yakir Yang (2):
  drm/rockchip: hdmi: add Innosilicon HDMI support
  dt-bindings: add document for Innosilicon HDMI on Rockchip platform

 .../display/rockchip/inno_hdmi-rockchip.txt        |  50 ++
 drivers/gpu/drm/rockchip/Kconfig                   |   8 +
 drivers/gpu/drm/rockchip/Makefile                  |   1 +
 drivers/gpu/drm/rockchip/inno_hdmi.c               | 939 +++++++++++++++++++++
 drivers/gpu/drm/rockchip/inno_hdmi.h               | 362 ++++++++
 5 files changed, 1360 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt
 create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.c
 create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.h

-- 
1.9.1




From ykk at rock-chips.com  Thu Jan 28 22:47:39 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 29 Jan 2016 14:47:39 +0800
Subject: [PATCH v8 1/2] drm/rockchip: hdmi: add Innosilicon HDMI support
In-Reply-To: <1454049768-30872-1-git-send-email-ykk@rock-chips.com>
References: <1454049768-30872-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1454050059-31196-1-git-send-email-ykk@rock-chips.com>

The Innosilicon HDMI is a low power HDMI 1.4 transmitter
IP, and it have been integrated on some rockchip CPUs
(like RK3036, RK312x).

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v8:
- Don't check whether encoder output format is RGB colorspace, cause driver
  default configure the output colorspace to RGB. (ZhengYang)
- Correct the check condition in inno_hdmi_config_video_csc() (ZhengYang)
    - if (data->enc_out_format == data->enc_out_format) {
    + if (data->enc_in_format == data->enc_out_format) {

Changes in v7:
- Correct the module licnese statement (Paul)
     - MODULE_LICENSE("GPL");
     + MODULE_LICENSE("GPLv2");
- Start indentation with tabs and fix the misspell in Kconfig (Paul)
- Carry the lost device-binding document (Heiko)

Changes in v6:
- Rebase the Makefile/Kconfig files which add by Chris's rockchip-mipi driver (Caeser)

Changes in v5:
- Use hdmi_infoframe helper functions to packed the infoframe (Russell)
- Remove the unused double wait_for_completion_timeout for ddc transfer (Russell)
- Remove the unused local variable in "inno_hdmi_i2c_write()" function (Russell)

Changes in v4:
- Modify the commit title "drm/rockchip: hdmi: ..." (Mark)
- Correct the "DKMS" to "DPMS" (Mark)
- Fix over 80 characters problems (Mark)
- Remove encoder .prepare/.commit helper functions, and move the vop mode
configure function into encoder .enable helper functions. (Mark)

Changes in v3:
- Use encoder enable/disable function, and remove the encoder DPMS function
- Keep HDMI PLL power on in standby mode

Changes in v2:
- Using DRM atomic helper functions for connector init (Mark)
- Remove "hdmi->connector.encoder = encoder;" (Mark)

 drivers/gpu/drm/rockchip/Kconfig     |   8 +
 drivers/gpu/drm/rockchip/Makefile    |   1 +
 drivers/gpu/drm/rockchip/inno_hdmi.c | 939 +++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/inno_hdmi.h | 362 ++++++++++++++
 4 files changed, 1310 insertions(+)
 create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.c
 create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.h

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 8573985..76b3362 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -35,3 +35,11 @@ config ROCKCHIP_DW_MIPI_DSI
 	 for the Synopsys DesignWare HDMI driver. If you want to
 	 enable MIPI DSI on RK3288 based SoC, you should selet this
 	 option.
+
+config ROCKCHIP_INNO_HDMI
+	tristate "Rockchip specific extensions for Innosilicon HDMI"
+	depends on DRM_ROCKCHIP
+	help
+	  This selects support for Rockchip SoC specific extensions
+	  for the Innosilicon HDMI driver. If you want to enable
+	  HDMI on RK3036 based SoC, you should select this option.
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index f6a809a..df8fbef 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -8,5 +8,6 @@ rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
 
 obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
 obj-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
+obj-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
 
 obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_vop_reg.o
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
new file mode 100644
index 0000000..c99d88d
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -0,0 +1,939 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ *    Zheng Yang <zhengyang at rock-chips.com>
+ *    Yakir Yang <ykk at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/hdmi.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+
+#include <drm/drm_of.h>
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_encoder_slave.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_vop.h"
+
+#include "inno_hdmi.h"
+
+#define to_inno_hdmi(x)	container_of(x, struct inno_hdmi, x)
+
+struct hdmi_data_info {
+	int vic;
+	bool sink_is_hdmi;
+	bool sink_has_audio;
+	unsigned int enc_in_format;
+	unsigned int enc_out_format;
+	unsigned int colorimetry;
+};
+
+struct inno_hdmi_i2c {
+	struct i2c_adapter adap;
+
+	u8 ddc_addr;
+	u8 segment_addr;
+
+	struct mutex lock;
+	struct completion cmp;
+};
+
+struct inno_hdmi {
+	struct device *dev;
+	struct drm_device *drm_dev;
+
+	int irq;
+	struct clk *pclk;
+	void __iomem *regs;
+
+	struct drm_connector	connector;
+	struct drm_encoder	encoder;
+
+	struct inno_hdmi_i2c *i2c;
+	struct i2c_adapter *ddc;
+
+	unsigned int tmds_rate;
+
+	struct hdmi_data_info	hdmi_data;
+	struct drm_display_mode previous_mode;
+};
+
+enum {
+	CSC_ITU601_16_235_TO_RGB_0_255_8BIT,
+	CSC_ITU601_0_255_TO_RGB_0_255_8BIT,
+	CSC_ITU709_16_235_TO_RGB_0_255_8BIT,
+	CSC_RGB_0_255_TO_ITU601_16_235_8BIT,
+	CSC_RGB_0_255_TO_ITU709_16_235_8BIT,
+	CSC_RGB_0_255_TO_RGB_16_235_8BIT,
+};
+
+static const char coeff_csc[][24] = {
+	/*
+	 * YUV2RGB:601 SD mode(Y[16:235], UV[16:240], RGB[0:255]):
+	 *   R = 1.164*Y + 1.596*V - 204
+	 *   G = 1.164*Y - 0.391*U - 0.813*V + 154
+	 *   B = 1.164*Y + 2.018*U - 258
+	 */
+	{
+		0x04, 0xa7, 0x00, 0x00, 0x06, 0x62, 0x02, 0xcc,
+		0x04, 0xa7, 0x11, 0x90, 0x13, 0x40, 0x00, 0x9a,
+		0x04, 0xa7, 0x08, 0x12, 0x00, 0x00, 0x03, 0x02
+	},
+	/*
+	 * YUV2RGB:601 SD mode(YUV[0:255],RGB[0:255]):
+	 *   R = Y + 1.402*V - 248
+	 *   G = Y - 0.344*U - 0.714*V + 135
+	 *   B = Y + 1.772*U - 227
+	 */
+	{
+		0x04, 0x00, 0x00, 0x00, 0x05, 0x9b, 0x02, 0xf8,
+		0x04, 0x00, 0x11, 0x60, 0x12, 0xdb, 0x00, 0x87,
+		0x04, 0x00, 0x07, 0x16, 0x00, 0x00, 0x02, 0xe3
+	},
+	/*
+	 * YUV2RGB:709 HD mode(Y[16:235],UV[16:240],RGB[0:255]):
+	 *   R = 1.164*Y + 1.793*V - 248
+	 *   G = 1.164*Y - 0.213*U - 0.534*V + 77
+	 *   B = 1.164*Y + 2.115*U - 289
+	 */
+	{
+		0x04, 0xa7, 0x00, 0x00, 0x07, 0x2c, 0x02, 0xf8,
+		0x04, 0xa7, 0x10, 0xda, 0x12, 0x22, 0x00, 0x4d,
+		0x04, 0xa7, 0x08, 0x74, 0x00, 0x00, 0x03, 0x21
+	},
+
+	/*
+	 * RGB2YUV:601 SD mode:
+	 *   Cb = -0.291G - 0.148R + 0.439B + 128
+	 *   Y  = 0.504G  + 0.257R + 0.098B + 16
+	 *   Cr = -0.368G + 0.439R - 0.071B + 128
+	 */
+	{
+		0x11, 0x5f, 0x01, 0x82, 0x10, 0x23, 0x00, 0x80,
+		0x02, 0x1c, 0x00, 0xa1, 0x00, 0x36, 0x00, 0x1e,
+		0x11, 0x29, 0x10, 0x59, 0x01, 0x82, 0x00, 0x80
+	},
+	/*
+	 * RGB2YUV:709 HD mode:
+	 *   Cb = - 0.338G - 0.101R + 0.439B + 128
+	 *   Y  = 0.614G   + 0.183R + 0.062B + 16
+	 *   Cr = - 0.399G + 0.439R - 0.040B + 128
+	 */
+	{
+		0x11, 0x98, 0x01, 0xc1, 0x10, 0x28, 0x00, 0x80,
+		0x02, 0x74, 0x00, 0xbb, 0x00, 0x3f, 0x00, 0x10,
+		0x11, 0x5a, 0x10, 0x67, 0x01, 0xc1, 0x00, 0x80
+	},
+	/*
+	 * RGB[0:255]2RGB[16:235]:
+	 *   R' = R x (235-16)/255 + 16;
+	 *   G' = G x (235-16)/255 + 16;
+	 *   B' = B x (235-16)/255 + 16;
+	 */
+	{
+		0x00, 0x00, 0x03, 0x6F, 0x00, 0x00, 0x00, 0x10,
+		0x03, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+		0x00, 0x00, 0x00, 0x00, 0x03, 0x6F, 0x00, 0x10
+	},
+};
+
+static inline u8 hdmi_readb(struct inno_hdmi *hdmi, u16 offset)
+{
+	return readl_relaxed(hdmi->regs + (offset) * 0x04);
+}
+
+static inline void hdmi_writeb(struct inno_hdmi *hdmi, u16 offset, u32 val)
+{
+	writel_relaxed(val, hdmi->regs + (offset) * 0x04);
+}
+
+static inline void hdmi_modb(struct inno_hdmi *hdmi, u16 offset,
+			     u32 msk, u32 val)
+{
+	u8 temp = hdmi_readb(hdmi, offset) & ~msk;
+
+	temp |= val & msk;
+	hdmi_writeb(hdmi, offset, temp);
+}
+
+static void inno_hdmi_i2c_init(struct inno_hdmi *hdmi)
+{
+	int ddc_bus_freq;
+
+	ddc_bus_freq = (hdmi->tmds_rate >> 2) / HDMI_SCL_RATE;
+
+	hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
+	hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
+
+	/* Clear the EDID interrupt flag and mute the interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+}
+
+static void inno_hdmi_sys_power(struct inno_hdmi *hdmi, bool enable)
+{
+	if (enable)
+		hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON);
+	else
+		hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
+}
+
+static void inno_hdmi_set_pwr_mode(struct inno_hdmi *hdmi, int mode)
+{
+	switch (mode) {
+	case NORMAL:
+		inno_hdmi_sys_power(hdmi, false);
+
+		hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x6f);
+		hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0xbb);
+
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x14);
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x10);
+		hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x0f);
+		hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x01);
+
+		inno_hdmi_sys_power(hdmi, true);
+		break;
+
+	case LOWER_PWR:
+		inno_hdmi_sys_power(hdmi, false);
+		hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
+
+		break;
+
+	default:
+		dev_err(hdmi->dev, "Unknown power mode %d\n", mode);
+	}
+}
+
+static void inno_hdmi_reset(struct inno_hdmi *hdmi)
+{
+	u32 val;
+	u32 msk;
+
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_DIGITAL, v_NOT_RST_DIGITAL);
+	udelay(100);
+
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_ANALOG, v_NOT_RST_ANALOG);
+	udelay(100);
+
+	msk = m_REG_CLK_INV | m_REG_CLK_SOURCE | m_POWER | m_INT_POL;
+	val = v_REG_CLK_INV | v_REG_CLK_SOURCE_SYS | v_PWR_ON | v_INT_POL_HIGH;
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, msk, val);
+
+	inno_hdmi_set_pwr_mode(hdmi, NORMAL);
+}
+
+static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi, int setup_rc,
+				  union hdmi_infoframe *frame, u32 frame_index,
+				  u32 mask, u32 disable, u32 enable)
+{
+	if (mask)
+		hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, disable);
+
+	hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, frame_index);
+
+	if (setup_rc >= 0) {
+		u8 packed_frame[HDMI_MAXIMUM_INFO_FRAME_SIZE];
+		ssize_t rc, i;
+
+		rc = hdmi_infoframe_pack(frame, packed_frame,
+					 sizeof(packed_frame));
+		if (rc < 0)
+			return rc;
+
+		for (i = 0; i < rc; i++)
+			hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i,
+				    packed_frame[i]);
+
+		if (mask)
+			hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, enable);
+	}
+
+	return setup_rc;
+}
+
+static int inno_hdmi_config_video_vsi(struct inno_hdmi *hdmi,
+				      struct drm_display_mode *mode)
+{
+	union hdmi_infoframe frame;
+	int rc;
+
+	rc = drm_hdmi_vendor_infoframe_from_display_mode(&frame.vendor.hdmi,
+							 mode);
+
+	return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_VSI,
+		m_PACKET_VSI_EN, v_PACKET_VSI_EN(0), v_PACKET_VSI_EN(1));
+}
+
+static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
+				      struct drm_display_mode *mode)
+{
+	union hdmi_infoframe frame;
+	int rc;
+
+	rc = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, mode);
+
+	if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV444)
+		frame.avi.colorspace = HDMI_COLORSPACE_YUV444;
+	else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV422)
+		frame.avi.colorspace = HDMI_COLORSPACE_YUV422;
+	else
+		frame.avi.colorspace = HDMI_COLORSPACE_RGB;
+
+	return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_AVI, 0, 0, 0);
+}
+
+static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
+{
+	struct hdmi_data_info *data = &hdmi->hdmi_data;
+	int c0_c2_change = 0;
+	int csc_enable = 0;
+	int csc_mode = 0;
+	int auto_csc = 0;
+	int value;
+	int i;
+
+	/* Input video mode is SDR RGB24bit, data enable signal from external */
+	hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL1, v_DE_EXTERNAL |
+		    v_VIDEO_INPUT_FORMAT(VIDEO_INPUT_SDR_RGB444));
+
+	/* Input color hardcode to RGB, and output color hardcode to RGB888 */
+	value = v_VIDEO_INPUT_BITS(VIDEO_INPUT_8BITS) |
+		v_VIDEO_OUTPUT_COLOR(0) |
+		v_VIDEO_INPUT_CSP(0);
+	hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL2, value);
+
+	if (data->enc_in_format == data->enc_out_format) {
+		if ((data->enc_in_format == HDMI_COLORSPACE_RGB) ||
+		    (data->enc_in_format >= HDMI_COLORSPACE_YUV444)) {
+			value = v_SOF_DISABLE | v_COLOR_DEPTH_NOT_INDICATED(1);
+			hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
+
+			hdmi_modb(hdmi, HDMI_VIDEO_CONTRL,
+				  m_VIDEO_AUTO_CSC | m_VIDEO_C0_C2_SWAP,
+				  v_VIDEO_AUTO_CSC(AUTO_CSC_DISABLE) |
+				  v_VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE));
+			return 0;
+		}
+	}
+
+	if (data->colorimetry == HDMI_COLORIMETRY_ITU_601) {
+		if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
+		    (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
+			csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT;
+			auto_csc = AUTO_CSC_DISABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = v_CSC_ENABLE;
+		} else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
+			   (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
+			csc_mode = CSC_ITU601_16_235_TO_RGB_0_255_8BIT;
+			auto_csc = AUTO_CSC_ENABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = v_CSC_DISABLE;
+		}
+	} else {
+		if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
+		    (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
+			csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT;
+			auto_csc = AUTO_CSC_DISABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = v_CSC_ENABLE;
+		} else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
+			   (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
+			csc_mode = CSC_ITU709_16_235_TO_RGB_0_255_8BIT;
+			auto_csc = AUTO_CSC_ENABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = v_CSC_DISABLE;
+		}
+	}
+
+	for (i = 0; i < 24; i++)
+		hdmi_writeb(hdmi, HDMI_VIDEO_CSC_COEF + i,
+			    coeff_csc[csc_mode][i]);
+
+	value = v_SOF_DISABLE | csc_enable | v_COLOR_DEPTH_NOT_INDICATED(1);
+	hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
+	hdmi_modb(hdmi, HDMI_VIDEO_CONTRL, m_VIDEO_AUTO_CSC |
+		  m_VIDEO_C0_C2_SWAP, v_VIDEO_AUTO_CSC(auto_csc) |
+		  v_VIDEO_C0_C2_SWAP(c0_c2_change));
+
+	return 0;
+}
+
+static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi,
+					 struct drm_display_mode *mode)
+{
+	int value;
+
+	/* Set detail external video timing polarity and interlace mode */
+	value = v_EXTERANL_VIDEO(1);
+	value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
+		 v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
+	value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
+		 v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
+	value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
+		 v_INETLACE(1) : v_INETLACE(0);
+	hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
+
+	/* Set detail external video timing */
+	value = mode->htotal;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
+
+	value = mode->htotal - mode->hdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
+
+	value = mode->hsync_start - mode->hdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
+
+	value = mode->hsync_end - mode->hsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
+
+	value = mode->vtotal;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
+
+	value = mode->vtotal - mode->vdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
+
+	value = mode->vsync_start - mode->vdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
+
+	value = mode->vsync_end - mode->vsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
+
+	hdmi_writeb(hdmi, HDMI_PHY_PRE_DIV_RATIO, 0x1e);
+	hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_LOW, 0x2c);
+	hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH, 0x01);
+
+	return 0;
+}
+
+static int inno_hdmi_setup(struct inno_hdmi *hdmi,
+			   struct drm_display_mode *mode)
+{
+	hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
+
+	hdmi->hdmi_data.enc_in_format = HDMI_COLORSPACE_RGB;
+	hdmi->hdmi_data.enc_out_format = HDMI_COLORSPACE_RGB;
+
+	if ((hdmi->hdmi_data.vic == 6) || (hdmi->hdmi_data.vic == 7) ||
+	    (hdmi->hdmi_data.vic == 21) || (hdmi->hdmi_data.vic == 22) ||
+	    (hdmi->hdmi_data.vic == 2) || (hdmi->hdmi_data.vic == 3) ||
+	    (hdmi->hdmi_data.vic == 17) || (hdmi->hdmi_data.vic == 18))
+		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601;
+	else
+		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
+
+	/* Mute video and audio output */
+	hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
+		  v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1));
+
+	/* Set HDMI Mode */
+	hdmi_writeb(hdmi, HDMI_HDCP_CTRL,
+		    v_HDMI_DVI(hdmi->hdmi_data.sink_is_hdmi));
+
+	inno_hdmi_config_video_timing(hdmi, mode);
+
+	inno_hdmi_config_video_csc(hdmi);
+
+	if (hdmi->hdmi_data.sink_is_hdmi) {
+		inno_hdmi_config_video_avi(hdmi, mode);
+		inno_hdmi_config_video_vsi(hdmi, mode);
+	}
+
+	/*
+	 * When IP controller have configured to an accurate video
+	 * timing, then the TMDS clock source would be switched to
+	 * DCLK_LCDC, so we need to init the TMDS rate to mode pixel
+	 * clock rate, and reconfigure the DDC clock.
+	 */
+	hdmi->tmds_rate = mode->clock * 1000;
+	inno_hdmi_i2c_init(hdmi);
+
+	/* Unmute video and audio output */
+	hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
+		  v_AUDIO_MUTE(0) | v_VIDEO_MUTE(0));
+
+	return 0;
+}
+
+static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder,
+				       struct drm_display_mode *mode,
+				       struct drm_display_mode *adj_mode)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+
+	inno_hdmi_setup(hdmi, adj_mode);
+
+	/* Store the display mode for plugin/DPMS poweron events */
+	memcpy(&hdmi->previous_mode, adj_mode, sizeof(hdmi->previous_mode));
+}
+
+static void inno_hdmi_encoder_enable(struct drm_encoder *encoder)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+
+	rockchip_drm_crtc_mode_config(encoder->crtc, DRM_MODE_CONNECTOR_HDMIA,
+				      ROCKCHIP_OUT_MODE_P888);
+
+	inno_hdmi_set_pwr_mode(hdmi, NORMAL);
+}
+
+static void inno_hdmi_encoder_disable(struct drm_encoder *encoder)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
+
+	inno_hdmi_set_pwr_mode(hdmi, LOWER_PWR);
+}
+
+static bool inno_hdmi_encoder_mode_fixup(struct drm_encoder *encoder,
+					 const struct drm_display_mode *mode,
+					 struct drm_display_mode *adj_mode)
+{
+	return true;
+}
+
+static struct drm_encoder_helper_funcs inno_hdmi_encoder_helper_funcs = {
+	.enable     = inno_hdmi_encoder_enable,
+	.disable    = inno_hdmi_encoder_disable,
+	.mode_fixup = inno_hdmi_encoder_mode_fixup,
+	.mode_set   = inno_hdmi_encoder_mode_set,
+};
+
+static struct drm_encoder_funcs inno_hdmi_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static enum drm_connector_status
+inno_hdmi_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+
+	return (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
+		connector_status_connected : connector_status_disconnected;
+}
+
+static int inno_hdmi_connector_get_modes(struct drm_connector *connector)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+	struct edid *edid;
+	int ret = 0;
+
+	if (!hdmi->ddc)
+		return 0;
+
+	edid = drm_get_edid(connector, hdmi->ddc);
+	if (edid) {
+		hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
+		hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
+		drm_mode_connector_update_edid_property(connector, edid);
+		ret = drm_add_edid_modes(connector, edid);
+		kfree(edid);
+	}
+
+	return ret;
+}
+
+static enum drm_mode_status
+inno_hdmi_connector_mode_valid(struct drm_connector *connector,
+			       struct drm_display_mode *mode)
+{
+	return MODE_OK;
+}
+
+static struct drm_encoder *
+inno_hdmi_connector_best_encoder(struct drm_connector *connector)
+{
+	struct inno_hdmi *hdmi = to_inno_hdmi(connector);
+
+	return &hdmi->encoder;
+}
+
+static int
+inno_hdmi_probe_single_connector_modes(struct drm_connector *connector,
+				       uint32_t maxX, uint32_t maxY)
+{
+	return drm_helper_probe_single_connector_modes(connector, 1920, 1080);
+}
+
+static void inno_hdmi_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs inno_hdmi_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.fill_modes = inno_hdmi_probe_single_connector_modes,
+	.detect = inno_hdmi_connector_detect,
+	.destroy = inno_hdmi_connector_destroy,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static struct drm_connector_helper_funcs inno_hdmi_connector_helper_funcs = {
+	.get_modes = inno_hdmi_connector_get_modes,
+	.mode_valid = inno_hdmi_connector_mode_valid,
+	.best_encoder = inno_hdmi_connector_best_encoder,
+};
+
+static int inno_hdmi_register(struct drm_device *drm, struct inno_hdmi *hdmi)
+{
+	struct drm_encoder *encoder = &hdmi->encoder;
+	struct device *dev = hdmi->dev;
+
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
+
+	/*
+	 * If we failed to find the CRTC(s) which this encoder is
+	 * supposed to be connected to, it's because the CRTC has
+	 * not been registered yet.  Defer probing, and hope that
+	 * the required CRTC is added later.
+	 */
+	if (encoder->possible_crtcs == 0)
+		return -EPROBE_DEFER;
+
+	drm_encoder_helper_add(encoder, &inno_hdmi_encoder_helper_funcs);
+	drm_encoder_init(drm, encoder, &inno_hdmi_encoder_funcs,
+			 DRM_MODE_ENCODER_TMDS, NULL);
+
+	hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
+
+	drm_connector_helper_add(&hdmi->connector,
+				 &inno_hdmi_connector_helper_funcs);
+	drm_connector_init(drm, &hdmi->connector, &inno_hdmi_connector_funcs,
+			   DRM_MODE_CONNECTOR_HDMIA);
+
+	drm_mode_connector_attach_encoder(&hdmi->connector, encoder);
+
+	return 0;
+}
+
+static irqreturn_t inno_hdmi_i2c_irq(struct inno_hdmi *hdmi)
+{
+	struct inno_hdmi_i2c *i2c = hdmi->i2c;
+	u8 stat;
+
+	stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
+	if (!(stat & m_INT_EDID_READY))
+		return IRQ_NONE;
+
+	/* Clear HDMI EDID interrupt flag */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+
+	complete(&i2c->cmp);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t inno_hdmi_hardirq(int irq, void *dev_id)
+{
+	struct inno_hdmi *hdmi = dev_id;
+	irqreturn_t ret = IRQ_NONE;
+	u8 interrupt;
+
+	if (hdmi->i2c)
+		ret = inno_hdmi_i2c_irq(hdmi);
+
+	interrupt = hdmi_readb(hdmi, HDMI_STATUS);
+	if (interrupt & m_INT_HOTPLUG) {
+		hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG);
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	return ret;
+}
+
+static irqreturn_t inno_hdmi_irq(int irq, void *dev_id)
+{
+	struct inno_hdmi *hdmi = dev_id;
+
+	drm_helper_hpd_irq_event(hdmi->connector.dev);
+
+	return IRQ_HANDLED;
+}
+
+static int inno_hdmi_i2c_read(struct inno_hdmi *hdmi, struct i2c_msg *msgs)
+{
+	int length = msgs->len;
+	u8 *buf = msgs->buf;
+	int ret;
+
+	ret = wait_for_completion_timeout(&hdmi->i2c->cmp, HZ / 10);
+	if (!ret)
+		return -EAGAIN;
+
+	while (length--)
+		*buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
+
+	return 0;
+}
+
+static int inno_hdmi_i2c_write(struct inno_hdmi *hdmi, struct i2c_msg *msgs)
+{
+	/*
+	 * The DDC module only support read EDID message, so
+	 * we assume that each word write to this i2c adapter
+	 * should be the offset of EDID word address.
+	 */
+	if ((msgs->len != 1) ||
+	    ((msgs->addr != DDC_ADDR) && (msgs->addr != DDC_SEGMENT_ADDR)))
+		return -EINVAL;
+
+	reinit_completion(&hdmi->i2c->cmp);
+
+	if (msgs->addr == DDC_SEGMENT_ADDR)
+		hdmi->i2c->segment_addr = msgs->buf[0];
+	if (msgs->addr == DDC_ADDR)
+		hdmi->i2c->ddc_addr = msgs->buf[0];
+
+	/* Set edid fifo first addr */
+	hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
+
+	/* Set edid word address 0x00/0x80 */
+	hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
+
+	/* Set edid segment pointer */
+	hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr);
+
+	return 0;
+}
+
+static int inno_hdmi_i2c_xfer(struct i2c_adapter *adap,
+			      struct i2c_msg *msgs, int num)
+{
+	struct inno_hdmi *hdmi = i2c_get_adapdata(adap);
+	struct inno_hdmi_i2c *i2c = hdmi->i2c;
+	int i, ret = 0;
+
+	mutex_lock(&i2c->lock);
+
+	/* Clear the EDID interrupt flag and unmute the interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY);
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+
+	for (i = 0; i < num; i++) {
+		dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n",
+			i + 1, num, msgs[i].len, msgs[i].flags);
+
+		if (msgs[i].flags & I2C_M_RD)
+			ret = inno_hdmi_i2c_read(hdmi, &msgs[i]);
+		else
+			ret = inno_hdmi_i2c_write(hdmi, &msgs[i]);
+
+		if (ret < 0)
+			break;
+	}
+
+	if (!ret)
+		ret = num;
+
+	/* Mute HDMI EDID interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
+
+	mutex_unlock(&i2c->lock);
+
+	return ret;
+}
+
+static u32 inno_hdmi_i2c_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm inno_hdmi_algorithm = {
+	.master_xfer	= inno_hdmi_i2c_xfer,
+	.functionality	= inno_hdmi_i2c_func,
+};
+
+static struct i2c_adapter *inno_hdmi_i2c_adapter(struct inno_hdmi *hdmi)
+{
+	struct i2c_adapter *adap;
+	struct inno_hdmi_i2c *i2c;
+	int ret;
+
+	i2c = devm_kzalloc(hdmi->dev, sizeof(*i2c), GFP_KERNEL);
+	if (!i2c)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_init(&i2c->lock);
+	init_completion(&i2c->cmp);
+
+	adap = &i2c->adap;
+	adap->class = I2C_CLASS_DDC;
+	adap->owner = THIS_MODULE;
+	adap->dev.parent = hdmi->dev;
+	adap->dev.of_node = hdmi->dev->of_node;
+	adap->algo = &inno_hdmi_algorithm;
+	strlcpy(adap->name, "Inno HDMI", sizeof(adap->name));
+	i2c_set_adapdata(adap, hdmi);
+
+	ret = i2c_add_adapter(adap);
+	if (ret) {
+		dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name);
+		devm_kfree(hdmi->dev, i2c);
+		return ERR_PTR(ret);
+	}
+
+	hdmi->i2c = i2c;
+
+	dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name);
+
+	return adap;
+}
+
+static int inno_hdmi_bind(struct device *dev, struct device *master,
+				 void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = data;
+	struct inno_hdmi *hdmi;
+	struct resource *iores;
+	int irq;
+	int ret;
+
+	hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
+	if (!hdmi)
+		return -ENOMEM;
+
+	hdmi->dev = dev;
+	hdmi->drm_dev = drm;
+
+	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!iores)
+		return -ENXIO;
+
+	hdmi->regs = devm_ioremap_resource(dev, iores);
+	if (IS_ERR(hdmi->regs))
+		return PTR_ERR(hdmi->regs);
+
+	hdmi->pclk = devm_clk_get(hdmi->dev, "pclk");
+	if (IS_ERR(hdmi->pclk)) {
+		dev_err(hdmi->dev, "Unable to get HDMI pclk clk\n");
+		return PTR_ERR(hdmi->pclk);
+	}
+
+	ret = clk_prepare_enable(hdmi->pclk);
+	if (ret) {
+		dev_err(hdmi->dev, "Cannot enable HDMI pclk clock: %d\n", ret);
+		return ret;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	inno_hdmi_reset(hdmi);
+
+	hdmi->ddc = inno_hdmi_i2c_adapter(hdmi);
+	if (IS_ERR(hdmi->ddc)) {
+		hdmi->ddc = NULL;
+		return PTR_ERR(hdmi->ddc);
+	}
+
+	/*
+	 * When IP controller haven't configured to an accurate video
+	 * timing, then the TMDS clock source would be switched to
+	 * PCLK_HDMI, so we need to init the TMDS rate to PCLK rate,
+	 * and reconfigure the DDC clock.
+	 */
+	hdmi->tmds_rate = clk_get_rate(hdmi->pclk);
+	inno_hdmi_i2c_init(hdmi);
+
+	ret = inno_hdmi_register(drm, hdmi);
+	if (ret)
+		return ret;
+
+	dev_set_drvdata(dev, hdmi);
+
+	/* Unmute hotplug interrupt */
+	hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
+
+	ret = devm_request_threaded_irq(dev, irq, inno_hdmi_hardirq,
+					inno_hdmi_irq, IRQF_SHARED,
+					dev_name(dev), hdmi);
+
+	return ret;
+}
+
+static void inno_hdmi_unbind(struct device *dev, struct device *master,
+			     void *data)
+{
+	struct inno_hdmi *hdmi = dev_get_drvdata(dev);
+
+	hdmi->connector.funcs->destroy(&hdmi->connector);
+	hdmi->encoder.funcs->destroy(&hdmi->encoder);
+
+	clk_disable_unprepare(hdmi->pclk);
+	i2c_put_adapter(hdmi->ddc);
+}
+
+static const struct component_ops inno_hdmi_ops = {
+	.bind	= inno_hdmi_bind,
+	.unbind	= inno_hdmi_unbind,
+};
+
+static int inno_hdmi_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &inno_hdmi_ops);
+}
+
+static int inno_hdmi_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &inno_hdmi_ops);
+
+	return 0;
+}
+
+static const struct of_device_id inno_hdmi_dt_ids[] = {
+	{ .compatible = "rockchip,rk3036-inno-hdmi",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, inno_hdmi_dt_ids);
+
+static struct platform_driver inno_hdmi_driver = {
+	.probe  = inno_hdmi_probe,
+	.remove = inno_hdmi_remove,
+	.driver = {
+		.name = "innohdmi-rockchip",
+		.of_match_table = inno_hdmi_dt_ids,
+	},
+};
+
+module_platform_driver(inno_hdmi_driver);
+
+MODULE_AUTHOR("Zheng Yang <zhengyang at rock-chips.com>");
+MODULE_AUTHOR("Yakir Yang <ykk at rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip Specific INNO-HDMI Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:innohdmi-rockchip");
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.h b/drivers/gpu/drm/rockchip/inno_hdmi.h
new file mode 100644
index 0000000..aa7c415
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.h
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ *    Zheng Yang <zhengyang at rock-chips.com>
+ *    Yakir Yang <ykk at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __INNO_HDMI_H__
+#define __INNO_HDMI_H__
+
+#define DDC_SEGMENT_ADDR		0x30
+
+enum PWR_MODE {
+	NORMAL,
+	LOWER_PWR,
+};
+
+#define HDMI_SCL_RATE			(100*1000)
+#define DDC_BUS_FREQ_L			0x4b
+#define DDC_BUS_FREQ_H			0x4c
+
+#define HDMI_SYS_CTRL			0x00
+#define m_RST_ANALOG			(1 << 6)
+#define v_RST_ANALOG			(0 << 6)
+#define v_NOT_RST_ANALOG		(1 << 6)
+#define m_RST_DIGITAL			(1 << 5)
+#define v_RST_DIGITAL			(0 << 5)
+#define v_NOT_RST_DIGITAL		(1 << 5)
+#define m_REG_CLK_INV			(1 << 4)
+#define v_REG_CLK_NOT_INV		(0 << 4)
+#define v_REG_CLK_INV			(1 << 4)
+#define m_VCLK_INV			(1 << 3)
+#define v_VCLK_NOT_INV			(0 << 3)
+#define v_VCLK_INV			(1 << 3)
+#define m_REG_CLK_SOURCE		(1 << 2)
+#define v_REG_CLK_SOURCE_TMDS		(0 << 2)
+#define v_REG_CLK_SOURCE_SYS		(1 << 2)
+#define m_POWER				(1 << 1)
+#define v_PWR_ON			(0 << 1)
+#define v_PWR_OFF			(1 << 1)
+#define m_INT_POL			(1 << 0)
+#define v_INT_POL_HIGH			1
+#define v_INT_POL_LOW			0
+
+#define HDMI_VIDEO_CONTRL1		0x01
+#define m_VIDEO_INPUT_FORMAT		(7 << 1)
+#define m_DE_SOURCE			(1 << 0)
+#define v_VIDEO_INPUT_FORMAT(n)		(n << 1)
+#define v_DE_EXTERNAL			1
+#define v_DE_INTERNAL			0
+enum {
+	VIDEO_INPUT_SDR_RGB444 = 0,
+	VIDEO_INPUT_DDR_RGB444 = 5,
+	VIDEO_INPUT_DDR_YCBCR422 = 6
+};
+
+#define HDMI_VIDEO_CONTRL2		0x02
+#define m_VIDEO_OUTPUT_COLOR		(3 << 6)
+#define m_VIDEO_INPUT_BITS		(3 << 4)
+#define m_VIDEO_INPUT_CSP		(1 << 0)
+#define v_VIDEO_OUTPUT_COLOR(n)		(((n) & 0x3) << 6)
+#define v_VIDEO_INPUT_BITS(n)		(n << 4)
+#define v_VIDEO_INPUT_CSP(n)		(n << 0)
+enum {
+	VIDEO_INPUT_12BITS = 0,
+	VIDEO_INPUT_10BITS = 1,
+	VIDEO_INPUT_REVERT = 2,
+	VIDEO_INPUT_8BITS = 3,
+};
+
+#define HDMI_VIDEO_CONTRL		0x03
+#define m_VIDEO_AUTO_CSC		(1 << 7)
+#define v_VIDEO_AUTO_CSC(n)		(n << 7)
+#define m_VIDEO_C0_C2_SWAP		(1 << 0)
+#define v_VIDEO_C0_C2_SWAP(n)		(n << 0)
+enum {
+	C0_C2_CHANGE_ENABLE = 0,
+	C0_C2_CHANGE_DISABLE = 1,
+	AUTO_CSC_DISABLE = 0,
+	AUTO_CSC_ENABLE = 1,
+};
+
+#define HDMI_VIDEO_CONTRL3		0x04
+#define m_COLOR_DEPTH_NOT_INDICATED	(1 << 4)
+#define m_SOF				(1 << 3)
+#define m_COLOR_RANGE			(1 << 2)
+#define m_CSC				(1 << 0)
+#define v_COLOR_DEPTH_NOT_INDICATED(n)	((n) << 4)
+#define v_SOF_ENABLE			(0 << 3)
+#define v_SOF_DISABLE			(1 << 3)
+#define v_COLOR_RANGE_FULL		(1 << 2)
+#define v_COLOR_RANGE_LIMITED		(0 << 2)
+#define v_CSC_ENABLE			1
+#define v_CSC_DISABLE			0
+
+#define HDMI_AV_MUTE			0x05
+#define m_AVMUTE_CLEAR			(1 << 7)
+#define m_AVMUTE_ENABLE			(1 << 6)
+#define m_AUDIO_MUTE			(1 << 1)
+#define m_VIDEO_BLACK			(1 << 0)
+#define v_AVMUTE_CLEAR(n)		(n << 7)
+#define v_AVMUTE_ENABLE(n)		(n << 6)
+#define v_AUDIO_MUTE(n)			(n << 1)
+#define v_VIDEO_MUTE(n)			(n << 0)
+
+#define HDMI_VIDEO_TIMING_CTL		0x08
+#define v_HSYNC_POLARITY(n)		(n << 3)
+#define v_VSYNC_POLARITY(n)		(n << 2)
+#define v_INETLACE(n)			(n << 1)
+#define v_EXTERANL_VIDEO(n)		(n << 0)
+
+#define HDMI_VIDEO_EXT_HTOTAL_L		0x09
+#define HDMI_VIDEO_EXT_HTOTAL_H		0x0a
+#define HDMI_VIDEO_EXT_HBLANK_L		0x0b
+#define HDMI_VIDEO_EXT_HBLANK_H		0x0c
+#define HDMI_VIDEO_EXT_HDELAY_L		0x0d
+#define HDMI_VIDEO_EXT_HDELAY_H		0x0e
+#define HDMI_VIDEO_EXT_HDURATION_L	0x0f
+#define HDMI_VIDEO_EXT_HDURATION_H	0x10
+#define HDMI_VIDEO_EXT_VTOTAL_L		0x11
+#define HDMI_VIDEO_EXT_VTOTAL_H		0x12
+#define HDMI_VIDEO_EXT_VBLANK		0x13
+#define HDMI_VIDEO_EXT_VDELAY		0x14
+#define HDMI_VIDEO_EXT_VDURATION	0x15
+
+#define HDMI_VIDEO_CSC_COEF		0x18
+
+#define HDMI_AUDIO_CTRL1		0x35
+enum {
+	CTS_SOURCE_INTERNAL = 0,
+	CTS_SOURCE_EXTERNAL = 1,
+};
+#define v_CTS_SOURCE(n)			(n << 7)
+
+enum {
+	DOWNSAMPLE_DISABLE = 0,
+	DOWNSAMPLE_1_2 = 1,
+	DOWNSAMPLE_1_4 = 2,
+};
+#define v_DOWN_SAMPLE(n)		(n << 5)
+
+enum {
+	AUDIO_SOURCE_IIS = 0,
+	AUDIO_SOURCE_SPDIF = 1,
+};
+#define v_AUDIO_SOURCE(n)		(n << 3)
+
+#define v_MCLK_ENABLE(n)		(n << 2)
+enum {
+	MCLK_128FS = 0,
+	MCLK_256FS = 1,
+	MCLK_384FS = 2,
+	MCLK_512FS = 3,
+};
+#define v_MCLK_RATIO(n)			(n)
+
+#define AUDIO_SAMPLE_RATE		0x37
+enum {
+	AUDIO_32K = 0x3,
+	AUDIO_441K = 0x0,
+	AUDIO_48K = 0x2,
+	AUDIO_882K = 0x8,
+	AUDIO_96K = 0xa,
+	AUDIO_1764K = 0xc,
+	AUDIO_192K = 0xe,
+};
+
+#define AUDIO_I2S_MODE			0x38
+enum {
+	I2S_CHANNEL_1_2 = 1,
+	I2S_CHANNEL_3_4 = 3,
+	I2S_CHANNEL_5_6 = 7,
+	I2S_CHANNEL_7_8 = 0xf
+};
+#define v_I2S_CHANNEL(n)		((n) << 2)
+enum {
+	I2S_STANDARD = 0,
+	I2S_LEFT_JUSTIFIED = 1,
+	I2S_RIGHT_JUSTIFIED = 2,
+};
+#define v_I2S_MODE(n)			(n)
+
+#define AUDIO_I2S_MAP			0x39
+#define AUDIO_I2S_SWAPS_SPDIF		0x3a
+#define v_SPIDF_FREQ(n)			(n)
+
+#define N_32K				0x1000
+#define N_441K				0x1880
+#define N_882K				0x3100
+#define N_1764K				0x6200
+#define N_48K				0x1800
+#define N_96K				0x3000
+#define N_192K				0x6000
+
+#define HDMI_AUDIO_CHANNEL_STATUS	0x3e
+#define m_AUDIO_STATUS_NLPCM		(1 << 7)
+#define m_AUDIO_STATUS_USE		(1 << 6)
+#define m_AUDIO_STATUS_COPYRIGHT	(1 << 5)
+#define m_AUDIO_STATUS_ADDITION		(3 << 2)
+#define m_AUDIO_STATUS_CLK_ACCURACY	(2 << 0)
+#define v_AUDIO_STATUS_NLPCM(n)		((n & 1) << 7)
+#define AUDIO_N_H			0x3f
+#define AUDIO_N_M			0x40
+#define AUDIO_N_L			0x41
+
+#define HDMI_AUDIO_CTS_H		0x45
+#define HDMI_AUDIO_CTS_M		0x46
+#define HDMI_AUDIO_CTS_L		0x47
+
+#define HDMI_DDC_CLK_L			0x4b
+#define HDMI_DDC_CLK_H			0x4c
+
+#define HDMI_EDID_SEGMENT_POINTER	0x4d
+#define HDMI_EDID_WORD_ADDR		0x4e
+#define HDMI_EDID_FIFO_OFFSET		0x4f
+#define HDMI_EDID_FIFO_ADDR		0x50
+
+#define HDMI_PACKET_SEND_MANUAL		0x9c
+#define HDMI_PACKET_SEND_AUTO		0x9d
+#define m_PACKET_GCP_EN			(1 << 7)
+#define m_PACKET_MSI_EN			(1 << 6)
+#define m_PACKET_SDI_EN			(1 << 5)
+#define m_PACKET_VSI_EN			(1 << 4)
+#define v_PACKET_GCP_EN(n)		((n & 1) << 7)
+#define v_PACKET_MSI_EN(n)		((n & 1) << 6)
+#define v_PACKET_SDI_EN(n)		((n & 1) << 5)
+#define v_PACKET_VSI_EN(n)		((n & 1) << 4)
+
+#define HDMI_CONTROL_PACKET_BUF_INDEX	0x9f
+enum {
+	INFOFRAME_VSI = 0x05,
+	INFOFRAME_AVI = 0x06,
+	INFOFRAME_AAI = 0x08,
+};
+
+#define HDMI_CONTROL_PACKET_ADDR	0xa0
+#define HDMI_MAXIMUM_INFO_FRAME_SIZE	0x11
+enum {
+	AVI_COLOR_MODE_RGB = 0,
+	AVI_COLOR_MODE_YCBCR422 = 1,
+	AVI_COLOR_MODE_YCBCR444 = 2,
+	AVI_COLORIMETRY_NO_DATA = 0,
+
+	AVI_COLORIMETRY_SMPTE_170M = 1,
+	AVI_COLORIMETRY_ITU709 = 2,
+	AVI_COLORIMETRY_EXTENDED = 3,
+
+	AVI_CODED_FRAME_ASPECT_NO_DATA = 0,
+	AVI_CODED_FRAME_ASPECT_4_3 = 1,
+	AVI_CODED_FRAME_ASPECT_16_9 = 2,
+
+	ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME = 0x08,
+	ACTIVE_ASPECT_RATE_4_3 = 0x09,
+	ACTIVE_ASPECT_RATE_16_9 = 0x0A,
+	ACTIVE_ASPECT_RATE_14_9 = 0x0B,
+};
+
+#define HDMI_HDCP_CTRL			0x52
+#define m_HDMI_DVI			(1 << 1)
+#define v_HDMI_DVI(n)			(n << 1)
+
+#define HDMI_INTERRUPT_MASK1		0xc0
+#define HDMI_INTERRUPT_STATUS1		0xc1
+#define	m_INT_ACTIVE_VSYNC		(1 << 5)
+#define m_INT_EDID_READY		(1 << 2)
+
+#define HDMI_INTERRUPT_MASK2		0xc2
+#define HDMI_INTERRUPT_STATUS2		0xc3
+#define m_INT_HDCP_ERR			(1 << 7)
+#define m_INT_BKSV_FLAG			(1 << 6)
+#define m_INT_HDCP_OK			(1 << 4)
+
+#define HDMI_STATUS			0xc8
+#define m_HOTPLUG			(1 << 7)
+#define m_MASK_INT_HOTPLUG		(1 << 5)
+#define m_INT_HOTPLUG			(1 << 1)
+#define v_MASK_INT_HOTPLUG(n)		((n & 0x1) << 5)
+
+#define HDMI_COLORBAR                   0xc9
+
+#define HDMI_PHY_SYNC			0xce
+#define HDMI_PHY_SYS_CTL		0xe0
+#define m_TMDS_CLK_SOURCE		(1 << 5)
+#define v_TMDS_FROM_PLL			(0 << 5)
+#define v_TMDS_FROM_GEN			(1 << 5)
+#define m_PHASE_CLK			(1 << 4)
+#define v_DEFAULT_PHASE			(0 << 4)
+#define v_SYNC_PHASE			(1 << 4)
+#define m_TMDS_CURRENT_PWR		(1 << 3)
+#define v_TURN_ON_CURRENT		(0 << 3)
+#define v_CAT_OFF_CURRENT		(1 << 3)
+#define m_BANDGAP_PWR			(1 << 2)
+#define v_BANDGAP_PWR_UP		(0 << 2)
+#define v_BANDGAP_PWR_DOWN		(1 << 2)
+#define m_PLL_PWR			(1 << 1)
+#define v_PLL_PWR_UP			(0 << 1)
+#define v_PLL_PWR_DOWN			(1 << 1)
+#define m_TMDS_CHG_PWR			(1 << 0)
+#define v_TMDS_CHG_PWR_UP		(0 << 0)
+#define v_TMDS_CHG_PWR_DOWN		(1 << 0)
+
+#define HDMI_PHY_CHG_PWR		0xe1
+#define v_CLK_CHG_PWR(n)		((n & 1) << 3)
+#define v_DATA_CHG_PWR(n)		((n & 7) << 0)
+
+#define HDMI_PHY_DRIVER			0xe2
+#define v_CLK_MAIN_DRIVER(n)		(n << 4)
+#define v_DATA_MAIN_DRIVER(n)		(n << 0)
+
+#define HDMI_PHY_PRE_EMPHASIS		0xe3
+#define v_PRE_EMPHASIS(n)		((n & 7) << 4)
+#define v_CLK_PRE_DRIVER(n)		((n & 3) << 2)
+#define v_DATA_PRE_DRIVER(n)		((n & 3) << 0)
+
+#define HDMI_PHY_FEEDBACK_DIV_RATIO_LOW		0xe7
+#define v_FEEDBACK_DIV_LOW(n)			(n & 0xff)
+#define HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH	0xe8
+#define v_FEEDBACK_DIV_HIGH(n)			(n & 1)
+
+#define HDMI_PHY_PRE_DIV_RATIO		0xed
+#define v_PRE_DIV_RATIO(n)		(n & 0x1f)
+
+#define HDMI_CEC_CTRL			0xd0
+#define m_ADJUST_FOR_HISENSE		(1 << 6)
+#define m_REJECT_RX_BROADCAST		(1 << 5)
+#define m_BUSFREETIME_ENABLE		(1 << 2)
+#define m_REJECT_RX			(1 << 1)
+#define m_START_TX			(1 << 0)
+
+#define HDMI_CEC_DATA			0xd1
+#define HDMI_CEC_TX_OFFSET		0xd2
+#define HDMI_CEC_RX_OFFSET		0xd3
+#define HDMI_CEC_CLK_H			0xd4
+#define HDMI_CEC_CLK_L			0xd5
+#define HDMI_CEC_TX_LENGTH		0xd6
+#define HDMI_CEC_RX_LENGTH		0xd7
+#define HDMI_CEC_TX_INT_MASK		0xd8
+#define m_TX_DONE			(1 << 3)
+#define m_TX_NOACK			(1 << 2)
+#define m_TX_BROADCAST_REJ		(1 << 1)
+#define m_TX_BUSNOTFREE			(1 << 0)
+
+#define HDMI_CEC_RX_INT_MASK		0xd9
+#define m_RX_LA_ERR			(1 << 4)
+#define m_RX_GLITCH			(1 << 3)
+#define m_RX_DONE			(1 << 0)
+
+#define HDMI_CEC_TX_INT			0xda
+#define HDMI_CEC_RX_INT			0xdb
+#define HDMI_CEC_BUSFREETIME_L		0xdc
+#define HDMI_CEC_BUSFREETIME_H		0xdd
+#define HDMI_CEC_LOGICADDR		0xde
+
+#endif /* __INNO_HDMI_H__ */
-- 
1.9.1




From ykk at rock-chips.com  Thu Jan 28 22:49:44 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 29 Jan 2016 14:49:44 +0800
Subject: [PATCH v8 2/2] dt-bindings: add document for Innosilicon HDMI on
 Rockchip platform
In-Reply-To: <1454049768-30872-1-git-send-email-ykk@rock-chips.com>
References: <1454049768-30872-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1454050184-31245-1-git-send-email-ykk@rock-chips.com>

Signed-off-by: Yakir Yang <ykk at rock-chips.com>

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Acked-by: Rob Herring <robh at kernel.org>
---
Changes in v8: None
Changes in v7: None
Changes in v6: None
Changes in v5: None
Changes in v4: None
Changes in v3: None
Changes in v2:
- Add the Acked-by tags from Rob
- Correct the misspell "rk3036-dw-hdmi" (Heiko)

 .../display/rockchip/inno_hdmi-rockchip.txt        | 50 ++++++++++++++++++++++
 1 file changed, 50 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt

diff --git a/Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt
new file mode 100644
index 0000000..8096a29
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt
@@ -0,0 +1,50 @@
+Rockchip specific extensions to the Innosilicon HDMI
+================================
+
+Required properties:
+- compatible:
+	"rockchip,rk3036-inno-hdmi";
+- reg:
+	Physical base address and length of the controller's registers.
+- clocks, clock-names:
+	Phandle to hdmi controller clock, name should be "pclk"
+- interrupts:
+	HDMI interrupt number
+- ports:
+	Contain one port node with endpoint definitions as defined in
+	Documentation/devicetree/bindings/graph.txt.
+- pinctrl-0, pinctrl-name:
+	Switch the iomux of HPD/CEC pins to HDMI function.
+
+Example:
+hdmi: hdmi at 20034000 {
+	compatible = "rockchip,rk3036-inno-hdmi";
+	reg = <0x20034000 0x4000>;
+	interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
+	clocks = <&cru  PCLK_HDMI>;
+	clock-names = "pclk";
+	pinctrl-names = "default";
+	pinctrl-0 = <&hdmi_ctl>;
+	status = "disabled";
+
+	hdmi_in: port {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		hdmi_in_lcdc: endpoint at 0 {
+			reg = <0>;
+			remote-endpoint = <&lcdc_out_hdmi>;
+		};
+	};
+};
+
+&pinctrl {
+	hdmi {
+		hdmi_ctl: hdmi-ctl {
+			rockchip,pins = <1 8  RK_FUNC_1 &pcfg_pull_none>,
+					<1 9  RK_FUNC_1 &pcfg_pull_none>,
+					<1 10 RK_FUNC_1 &pcfg_pull_none>,
+					<1 11 RK_FUNC_1 &pcfg_pull_none>;
+		};
+	};
+
+};
-- 
1.9.1




From ykk at rock-chips.com  Thu Jan 28 23:10:33 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 29 Jan 2016 15:10:33 +0800
Subject: [PATCH v8.1 2/2] dt-bindings: add document for Innosilicon HDMI on
 Rockchip platform
In-Reply-To: <1454049768-30872-1-git-send-email-ykk@rock-chips.com>
References: <1454049768-30872-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1454051433-32335-1-git-send-email-ykk@rock-chips.com>

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Acked-by: Rob Herring <robh at kernel.org>
---
Changes in v8.1:
- Remove dumplicate Signed-off which add at the v8 (Mark)
Changes in v8: None
Changes in v7: None
Changes in v6: None
Changes in v5: None
Changes in v4: None
Changes in v3: None
Changes in v2:
- Add the Acked-by tags from Rob
- Correct the misspell "rk3036-dw-hdmi" (Heiko)

 .../display/rockchip/inno_hdmi-rockchip.txt        | 50 ++++++++++++++++++++++
 1 file changed, 50 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt

diff --git a/Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt
new file mode 100644
index 0000000..8096a29
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt
@@ -0,0 +1,50 @@
+Rockchip specific extensions to the Innosilicon HDMI
+================================
+
+Required properties:
+- compatible:
+	"rockchip,rk3036-inno-hdmi";
+- reg:
+	Physical base address and length of the controller's registers.
+- clocks, clock-names:
+	Phandle to hdmi controller clock, name should be "pclk"
+- interrupts:
+	HDMI interrupt number
+- ports:
+	Contain one port node with endpoint definitions as defined in
+	Documentation/devicetree/bindings/graph.txt.
+- pinctrl-0, pinctrl-name:
+	Switch the iomux of HPD/CEC pins to HDMI function.
+
+Example:
+hdmi: hdmi at 20034000 {
+	compatible = "rockchip,rk3036-inno-hdmi";
+	reg = <0x20034000 0x4000>;
+	interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
+	clocks = <&cru  PCLK_HDMI>;
+	clock-names = "pclk";
+	pinctrl-names = "default";
+	pinctrl-0 = <&hdmi_ctl>;
+	status = "disabled";
+
+	hdmi_in: port {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		hdmi_in_lcdc: endpoint at 0 {
+			reg = <0>;
+			remote-endpoint = <&lcdc_out_hdmi>;
+		};
+	};
+};
+
+&pinctrl {
+	hdmi {
+		hdmi_ctl: hdmi-ctl {
+			rockchip,pins = <1 8  RK_FUNC_1 &pcfg_pull_none>,
+					<1 9  RK_FUNC_1 &pcfg_pull_none>,
+					<1 10 RK_FUNC_1 &pcfg_pull_none>,
+					<1 11 RK_FUNC_1 &pcfg_pull_none>;
+		};
+	};
+
+};
-- 
1.9.1




From moinejf at free.fr  Thu Jan 28 23:20:00 2016
From: moinejf at free.fr (Jean-Francois Moine)
Date: Fri, 29 Jan 2016 08:20:00 +0100
Subject: [PATCH v8 1/2] drm/rockchip: hdmi: add Innosilicon HDMI support
In-Reply-To: <1454050059-31196-1-git-send-email-ykk@rock-chips.com>
References: <1454049768-30872-1-git-send-email-ykk@rock-chips.com>
 <1454050059-31196-1-git-send-email-ykk@rock-chips.com>
Message-ID: <20160129082000.b25fd19cea94c4f3ec45e17f@free.fr>

On Fri, 29 Jan 2016 14:47:39 +0800
Yakir Yang <ykk at rock-chips.com> wrote:

> The Innosilicon HDMI is a low power HDMI 1.4 transmitter
> IP, and it have been integrated on some rockchip CPUs
> (like RK3036, RK312x).
> 
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
> ---
> Changes in v8:
> - Don't check whether encoder output format is RGB colorspace, cause driver
>   default configure the output colorspace to RGB. (ZhengYang)
> - Correct the check condition in inno_hdmi_config_video_csc() (ZhengYang)
>     - if (data->enc_out_format == data->enc_out_format) {
>     + if (data->enc_in_format == data->enc_out_format) {
> 
> Changes in v7:
> - Correct the module licnese statement (Paul)
>      - MODULE_LICENSE("GPL");
>      + MODULE_LICENSE("GPLv2");
> - Start indentation with tabs and fix the misspell in Kconfig (Paul)
> - Carry the lost device-binding document (Heiko)
> 
> Changes in v6:
> - Rebase the Makefile/Kconfig files which add by Chris's rockchip-mipi driver (Caeser)
> 
> Changes in v5:
> - Use hdmi_infoframe helper functions to packed the infoframe (Russell)
> - Remove the unused double wait_for_completion_timeout for ddc transfer (Russell)
> - Remove the unused local variable in "inno_hdmi_i2c_write()" function (Russell)
> 
> Changes in v4:
> - Modify the commit title "drm/rockchip: hdmi: ..." (Mark)
> - Correct the "DKMS" to "DPMS" (Mark)
> - Fix over 80 characters problems (Mark)
> - Remove encoder .prepare/.commit helper functions, and move the vop mode
> configure function into encoder .enable helper functions. (Mark)
> 
> Changes in v3:
> - Use encoder enable/disable function, and remove the encoder DPMS function
> - Keep HDMI PLL power on in standby mode
> 
> Changes in v2:
> - Using DRM atomic helper functions for connector init (Mark)
> - Remove "hdmi->connector.encoder = encoder;" (Mark)
> 
>  drivers/gpu/drm/rockchip/Kconfig     |   8 +
>  drivers/gpu/drm/rockchip/Makefile    |   1 +
>  drivers/gpu/drm/rockchip/inno_hdmi.c | 939 +++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/rockchip/inno_hdmi.h | 362 ++++++++++++++
>  4 files changed, 1310 insertions(+)
>  create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.c
>  create mode 100644 drivers/gpu/drm/rockchip/inno_hdmi.h
> 
> diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
> index 8573985..76b3362 100644
> --- a/drivers/gpu/drm/rockchip/Kconfig
> +++ b/drivers/gpu/drm/rockchip/Kconfig
> @@ -35,3 +35,11 @@ config ROCKCHIP_DW_MIPI_DSI
>  	 for the Synopsys DesignWare HDMI driver. If you want to
>  	 enable MIPI DSI on RK3288 based SoC, you should selet this
>  	 option.
> +
> +config ROCKCHIP_INNO_HDMI
> +	tristate "Rockchip specific extensions for Innosilicon HDMI"
> +	depends on DRM_ROCKCHIP
> +	help
> +	  This selects support for Rockchip SoC specific extensions
> +	  for the Innosilicon HDMI driver. If you want to enable
> +	  HDMI on RK3036 based SoC, you should select this option.
> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> index f6a809a..df8fbef 100644
> --- a/drivers/gpu/drm/rockchip/Makefile
> +++ b/drivers/gpu/drm/rockchip/Makefile
> @@ -8,5 +8,6 @@ rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
>  
>  obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
>  obj-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
> +obj-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
>  
>  obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_vop_reg.o
> diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
> new file mode 100644
> index 0000000..c99d88d
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
> @@ -0,0 +1,939 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + *    Zheng Yang <zhengyang at rock-chips.com>
> + *    Yakir Yang <ykk at rock-chips.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/irq.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/hdmi.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of_device.h>
> +
> +#include <drm/drm_of.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_encoder_slave.h>

This is not needed.

> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_vop.h"
> +
> +#include "inno_hdmi.h"
	[snip]

-- 
Ken ar c'henta?	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/



From al.kochet at gmail.com  Thu Jan 28 23:58:02 2016
From: al.kochet at gmail.com (Alexander Kochetkov)
Date: Fri, 29 Jan 2016 10:58:02 +0300
Subject: [PATCH v2] clk: rockchip: Allow sclk_i2s0 and i2s0_frac to change
 their parents rate on rk3188
In-Reply-To: <1453897469-2134-1-git-send-email-al.kochet@gmail.com>
References: <1453897469-2134-1-git-send-email-al.kochet@gmail.com>
Message-ID: <1454054282-16303-1-git-send-email-al.kochet@gmail.com>

Allow sclk_i2s0 and i2s0_frac to change their parents rate as
that the upstream dividers are purely there to feed sclk_i2s0

Tested on radxarock-lite.

Signed-off-by: Alexander Kochetkov <al.kochet at gmail.com>

Changes in v2:
Rebased on top of 4.5-rc1 of branch[1]

[1] https://git.kernel.org/cgit/linux/kernel/git/mmind/linux-rockchip.git/log/?h=v4.6-clk/next

---
 drivers/clk/rockchip/clk-rk3188.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c
index cc1d09d..629c65d 100644
--- a/drivers/clk/rockchip/clk-rk3188.c
+++ b/drivers/clk/rockchip/clk-rk3188.c
@@ -666,7 +666,7 @@ PNAME(mux_hsicphy_p)		= { "sclk_otgphy0_480m", "sclk_otgphy1_480m",
 				    "gpll", "cpll" };
 
 static struct rockchip_clk_branch rk3188_i2s0_fracmux __initdata =
-	MUX(SCLK_I2S0, "sclk_i2s0", mux_sclk_i2s0_p, 0,
+	MUX(SCLK_I2S0, "sclk_i2s0", mux_sclk_i2s0_p, CLK_SET_RATE_PARENT,
 			RK2928_CLKSEL_CON(3), 8, 2, MFLAGS);
 
 static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = {
@@ -722,7 +722,7 @@ static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = {
 	COMPOSITE_NOMUX(0, "i2s0_pre", "i2s_src", 0,
 			RK2928_CLKSEL_CON(3), 0, 7, DFLAGS,
 			RK2928_CLKGATE_CON(0), 9, GFLAGS),
-	COMPOSITE_FRACMUX(0, "i2s0_frac", "i2s0_pre", 0,
+	COMPOSITE_FRACMUX(0, "i2s0_frac", "i2s0_pre", CLK_SET_RATE_PARENT,
 			RK2928_CLKSEL_CON(7), 0,
 			RK2928_CLKGATE_CON(0), 10, GFLAGS,
 			&rk3188_i2s0_fracmux),
-- 
1.7.9.5



From zhengxing at rock-chips.com  Fri Jan 29 00:49:21 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Fri, 29 Jan 2016 16:49:21 +0800
Subject: [RFC PATCH] ARM: dts: rockchip: increase the mclk_fs to 512 for kylin
 board
Message-ID: <1454057361-22923-1-git-send-email-zhengxing@rock-chips.com>

If we playback the 8KHz FS audio with the 256 mclk_fs, we need the
mclk = 256 * 8000 = 2.048MHz, the frac div is 594 / 2.048 = 290,
the frac div value 0x00809015 set to the CRU_CLKSEL7_CON will cause
to hang.

We increase the mclk_fs to 512, will get the mclk = 512 * 8000 =
4.096MHz, use 0x01009015 instead of 0x00809015 to work around this
issue. We will keep tracking it.

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
---

 arch/arm/boot/dts/rk3036-kylin.dts |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/rk3036-kylin.dts b/arch/arm/boot/dts/rk3036-kylin.dts
index 190f22c..be468f9 100644
--- a/arch/arm/boot/dts/rk3036-kylin.dts
+++ b/arch/arm/boot/dts/rk3036-kylin.dts
@@ -67,7 +67,7 @@
 		compatible = "simple-audio-card";
 		simple-audio-card,format = "i2s";
 		simple-audio-card,name = "rockchip,rt5616-codec";
-		simple-audio-card,mclk-fs = <256>;
+		simple-audio-card,mclk-fs = <512>;
 		simple-audio-card,widgets =
 			"Microphone", "Microphone Jack",
 			"Headphone", "Headphone Jack";
-- 
1.7.9.5




From heiko at sntech.de  Fri Jan 29 01:21:37 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Fri, 29 Jan 2016 10:21:37 +0100
Subject: [PATCH v2] clk: rockchip: Allow sclk_i2s0 and i2s0_frac to change
 their parents rate on rk3188
In-Reply-To: <1454054282-16303-1-git-send-email-al.kochet@gmail.com>
References: <1453897469-2134-1-git-send-email-al.kochet@gmail.com>
 <1454054282-16303-1-git-send-email-al.kochet@gmail.com>
Message-ID: <3497216.6Y407LhQuk@phil>

Hi Alexander,

Am Freitag, 29. Januar 2016, 10:58:02 schrieb Alexander Kochetkov:
> Allow sclk_i2s0 and i2s0_frac to change their parents rate as
> that the upstream dividers are purely there to feed sclk_i2s0
> 
> Tested on radxarock-lite.
> 
> Signed-off-by: Alexander Kochetkov <al.kochet at gmail.com>
> 
> Changes in v2:
> Rebased on top of 4.5-rc1 of branch[1]
> 
> [1]
> https://git.kernel.org/cgit/linux/kernel/git/mmind/linux-rockchip.git/log
> /?h=v4.6-clk/next

please put these comments (changes in vY, etc) in the "comment section" 
below the "---" in future patches.

I've removed these lines after the Signed-off here and applied the patch to 
my clk branch for 4.6 now.


Thanks
Heiko

> 
> ---
>  drivers/clk/rockchip/clk-rk3188.c |    4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/clk/rockchip/clk-rk3188.c
> b/drivers/clk/rockchip/clk-rk3188.c index cc1d09d..629c65d 100644
> --- a/drivers/clk/rockchip/clk-rk3188.c
> +++ b/drivers/clk/rockchip/clk-rk3188.c
> @@ -666,7 +666,7 @@ PNAME(mux_hsicphy_p)		= { "sclk_otgphy0_480m",
> "sclk_otgphy1_480m", "gpll", "cpll" };
> 
>  static struct rockchip_clk_branch rk3188_i2s0_fracmux __initdata =
> -	MUX(SCLK_I2S0, "sclk_i2s0", mux_sclk_i2s0_p, 0,
> +	MUX(SCLK_I2S0, "sclk_i2s0", mux_sclk_i2s0_p, CLK_SET_RATE_PARENT,
>  			RK2928_CLKSEL_CON(3), 8, 2, MFLAGS);
> 
>  static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = {
> @@ -722,7 +722,7 @@ static struct rockchip_clk_branch
> rk3188_clk_branches[] __initdata = { COMPOSITE_NOMUX(0, "i2s0_pre",
> "i2s_src", 0,
>  			RK2928_CLKSEL_CON(3), 0, 7, DFLAGS,
>  			RK2928_CLKGATE_CON(0), 9, GFLAGS),
> -	COMPOSITE_FRACMUX(0, "i2s0_frac", "i2s0_pre", 0,
> +	COMPOSITE_FRACMUX(0, "i2s0_frac", "i2s0_pre", CLK_SET_RATE_PARENT,
>  			RK2928_CLKSEL_CON(7), 0,
>  			RK2928_CLKGATE_CON(0), 10, GFLAGS,
>  			&rk3188_i2s0_fracmux),



From zhengxing at rock-chips.com  Fri Jan 29 02:44:38 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Fri, 29 Jan 2016 18:44:38 +0800
Subject: [PATCH] ASoC: rt5616: Add support sample rate to 192KHz
Message-ID: <1454064278-5791-1-git-send-email-zhengxing@rock-chips.com>

Reference the TRM, the ALC5616 support one 24bit/8KHz ~ 192KHz
I2S/PCM Interface for stereo DAC and stereo ADC.

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
---

 sound/soc/codecs/rt5616.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c
index 1c10d8e..3220e5c 100644
--- a/sound/soc/codecs/rt5616.c
+++ b/sound/soc/codecs/rt5616.c
@@ -1218,7 +1218,7 @@ static int rt5616_resume(struct snd_soc_codec *codec)
 #define rt5616_resume NULL
 #endif
 
-#define RT5616_STEREO_RATES SNDRV_PCM_RATE_8000_96000
+#define RT5616_STEREO_RATES SNDRV_PCM_RATE_8000_192000
 #define RT5616_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
 			SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
 
-- 
1.7.9.5




From joro at 8bytes.org  Fri Jan 29 03:26:45 2016
From: joro at 8bytes.org (Joerg Roedel)
Date: Fri, 29 Jan 2016 12:26:45 +0100
Subject: [PATCH v1] iommu/rockchip: reconstruct to support multi slaves
In-Reply-To: <1453186980-21055-1-git-send-email-zhengsq@rock-chips.com>
References: <1453186980-21055-1-git-send-email-zhengsq@rock-chips.com>
Message-ID: <20160129112645.GC4204@8bytes.org>

On Tue, Jan 19, 2016 at 03:03:00PM +0800, Shunqian Zheng wrote:
> From: ZhengShunQian <zhengsq at rock-chips.com>
> 
> There are some IPs, such as video encoder/decoder, contains 2 slave iommus,
> one for reading and the other for writing. They share the same irq and
> clock with master.
> 
> This patch reconstructs to support this case by making them share the same
> Page Directory, Page Tables and even the register operations.
> That means every instruction to the reading MMU registers would be
> duplicated to the writing MMU and vice versa.
> 
> Signed-off-by: ZhengShunQian <zhengsq at rock-chips.com>
> ---
>  drivers/iommu/rockchip-iommu.c | 214 ++++++++++++++++++++++++++---------------
>  1 file changed, 135 insertions(+), 79 deletions(-)

Applied, thanks.



From wdc at rock-chips.com  Fri Jan 29 03:35:23 2016
From: wdc at rock-chips.com (David.Wu)
Date: Fri, 29 Jan 2016 19:35:23 +0800
Subject: [PATCH v2] pinctrl: rockchip: add support for the rk3399
In-Reply-To: <7536813.E6m4KCLL9V@diego>
References: <1451997055-47655-1-git-send-email-wdc@rock-chips.com>
 <1452152498-21780-1-git-send-email-wdc@rock-chips.com>
 <7536813.E6m4KCLL9V@diego>
Message-ID: <56AB4E7B.2060807@rock-chips.com>

Hi Heiko,

? 2016/1/29 0:56, Heiko St?bner ??:
> Hi David,
>
> Am Donnerstag, 7. Januar 2016, 15:41:38 schrieb David Wu:
>> From: David Wu <david.wu at rock-chips.com>
>>
>> The pinctrl of rk3399 is much different from other's,
>> especially the 3bits of drive strength.
>>
>> Signed-off-by: David Wu <david.wu at rock-chips.com>
>> ---
>> change from v1:
>> - need spin_unlock_irqrestore for set drive default case
>>
>>   .../bindings/pinctrl/rockchip,pinctrl.txt          |   1 +
>>   drivers/pinctrl/pinctrl-rockchip.c                 | 339
>> ++++++++++++++++++++- 2 files changed, 326 insertions(+), 14 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
>> b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt index
>> 391ef4b..3bb9456 100644
>> --- a/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
>> +++ b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
>> @@ -22,6 +22,7 @@ Required properties for iomux controller:
>>     - compatible: one of "rockchip,rk2928-pinctrl",
>> "rockchip,rk3066a-pinctrl" "rockchip,rk3066b-pinctrl",
>> "rockchip,rk3188-pinctrl"
>>   		       "rockchip,rk3288-pinctrl", "rockchip,rk3368-pinctrl"
>> +		       "rockchip,rk3399-pinctrl"
>>     - rockchip,grf: phandle referencing a syscon providing the
>>   	 "general register files"
>>
>> diff --git a/drivers/pinctrl/pinctrl-rockchip.c
>> b/drivers/pinctrl/pinctrl-rockchip.c index a065112..9d61c6e 100644
>> --- a/drivers/pinctrl/pinctrl-rockchip.c
>> +++ b/drivers/pinctrl/pinctrl-rockchip.c
> [...]
>
>> @@ -685,19 +821,60 @@ static int rockchip_get_drive_perpin(struct
>> rockchip_pin_bank *bank, struct rockchip_pin_ctrl *ctrl = info->ctrl;
>>   	struct regmap *regmap;
>>   	int reg, ret;
>> -	u32 data;
>> +	u32 data, temp, rmask_bits;
>>   	u8 bit;
>> +	int drv_type = bank->drv[pin_num / 8].drv_type;
>>
>>   	ctrl->drv_calc_reg(bank, pin_num, &regmap, &reg, &bit);
>>
>> +	switch (drv_type) {
>> +	case DRV_TYPE_IO_1V8_3V0_AUTO:
>> +	case DRV_TYPE_IO_3V3_ONLY:
>> +		if (RK3399_DRV_3BITS_PER_PIN + bit >= 16) {
> According to the doc excerpts, wouldn't the big special handling only be
> necessary for for gpio3b5 alone (+ other banks) ... i.e. the one setting being
> split over both registers?

yes, only the pin5 setting of 3bits width bank is split over two registers.

> If so I think doing something like the following might be easier to
> understand? Same of course for the set-counterpart. [Beware if I missed up the
> indices somewhere :-)

yeap.
Seemed it is clearer to view by using following code.
I will have a try, thanks.

>
> rmask_bits = RK3399_DRV_3BITS_PER_PIN;
> switch(bit) {
> case 0 ... 12:
> 	/* regular case, nothing to do */
> 	break;
> case 15:
> 	/* drive-strength offset is special, as it is spread over 2 registers */
> 	ret = regmap_read(regmap, reg, &data);
> 	if (ret)
> 		return ret;
>
> 	ret = regmap_read(regmap, reg + 0x4, &temp);
> 	if (ret)
> 		return ret;
>
> 	/*
> 	 * the bit data[15] contains bit 0 of the value
> 	 * while temp[1:0] containts bits 2 and 1
> 	 */
> 	data >>= 15;
> 	temp &= 0x3;
> 	temp <<= 1;
> 	data |= temp;
>
> 	return rockchip_perpin_drv_list[drv_type][data];
> case 18 ... 21:
> 	/* setting fully enclosed in the second register */
> 	rmask_bits = RK3399_DRV_3BITS_PER_PIN;
> 	reg += 4;
> 	bit -= 18;
> }
>
>> +			/* need to read regs twice */
>> +			ret = regmap_read(regmap, reg, &temp);
>> +			if (ret)
>> +				return ret;
>> +
>> +			temp >>= bit;
>> +			rmask_bits = 16 - bit;
>> +			temp &= (1 << rmask_bits) - 1;
>> +
>> +			reg = reg + 0x4;
>> +			ret = regmap_read(regmap, reg, &data);
>> +			if (ret)
>> +				return ret;
>> +
>> +			rmask_bits = RK3399_DRV_3BITS_PER_PIN
>> +				+ bit - 16;
>> +			data &= (1 << rmask_bits) - 1;
>> +			data <<= 16 - bit;
>> +			data |= temp;
>> +
>> +			return rockchip_perpin_drv_list[drv_type][data];
>> +		}
>> +
>> +		rmask_bits = RK3399_DRV_3BITS_PER_PIN;
>> +		break;
>> +	case DRV_TYPE_IO_DEFAULT:
>> +	case DRV_TYPE_IO_1V8_OR_3V0:
>> +	case DRV_TYPE_IO_1V8_ONLY:
>> +		rmask_bits = RK3288_DRV_BITS_PER_PIN;
>> +		break;
>> +	default:
>> +		dev_err(info->dev, "unsupported pinctrl drive type: %d\n",
>> +			drv_type);
>> +		return -EINVAL;
>> +	}
>> +
>>   	ret = regmap_read(regmap, reg, &data);
>>   	if (ret)
>>   		return ret;
>>
>>   	data >>= bit;
>> -	data &= (1 << RK3288_DRV_BITS_PER_PIN) - 1;
>> +	data &= (1 << rmask_bits) - 1;
>>
>> -	return rockchip_perpin_drv_list[data];
>> +	return rockchip_perpin_drv_list[drv_type][data];
>>   }
>>
>>   static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank,
> [...]
>
>> @@ -729,8 +913,45 @@ static int rockchip_set_drive_perpin(struct
>> rockchip_pin_bank *bank,
>>
>>   	spin_lock_irqsave(&bank->slock, flags);
>>
>> +	switch (drv_type) {
>> +	case DRV_TYPE_IO_1V8_3V0_AUTO:
>> +	case DRV_TYPE_IO_3V3_ONLY:
>> +		if (RK3399_DRV_3BITS_PER_PIN + bit >= 16) {
>> +			/* need to write regs twice */
>> +			rmask_bits = 16 - bit;
>> +			/* enable the write to the equivalent lower bits */
>> +			data = ((1 << rmask_bits) - 1) << (bit + 16);
>> +			rmask = data | (data >> 16);
>> +			ret &= (1 << rmask_bits) - 1;
>> +			data |= (ret << bit);
>> +
>> +			ret = regmap_update_bits(regmap, reg, rmask, data);
>> +			if (ret)
>> +				return ret;
>> +
>> +			ret = i >> rmask_bits;
>> +			rmask_bits = RK3399_DRV_3BITS_PER_PIN + bit - 16;
>> +			reg = reg + 0x4;
>> +			bit = 0;
> Apart from a change similar to the above, should the setting maybe also
> directly return and not depend on the final write?

okay, i will do the simialr change to the above.

>> +		} else {
>> +			rmask_bits = RK3399_DRV_3BITS_PER_PIN;
>> +		}
>> +		break;
>> +	case DRV_TYPE_IO_DEFAULT:
>> +	case DRV_TYPE_IO_1V8_OR_3V0:
>> +	case DRV_TYPE_IO_1V8_ONLY:
>> +		rmask_bits = RK3288_DRV_BITS_PER_PIN;
>> +		break;
>> +	default:
>> +		spin_unlock_irqrestore(&bank->slock, flags);
>> +		dev_err(info->dev, "unsupported pinctrl drive type: %d\n",
>> +			drv_type);
>> +		return -EINVAL;
>> +
>> +	}
>> +
>>   	/* enable the write to the equivalent lower bits */
>> -	data = ((1 << RK3288_DRV_BITS_PER_PIN) - 1) << (bit + 16);
>> +	data = ((1 << rmask_bits) - 1) << (bit + 16);
>>   	rmask = data | (data >> 16);
>>   	data |= (ret << bit);
>>
> [...]
>
>> @@ -2208,6 +2461,62 @@ static struct rockchip_pin_ctrl rk3368_pin_ctrl = {
>>   		.drv_calc_reg		= rk3368_calc_drv_reg_and_bit,
>>   };
>>
>> +static struct rockchip_pin_bank rk3399_pin_banks[] = {
>> +	PIN_BANK_IOMUX_DRV_FLAGS_OFFSET(0, 32, "gpio0", IOMUX_SOURCE_PMU,
>> +					IOMUX_SOURCE_PMU,
>> +					IOMUX_SOURCE_PMU,
>> +					IOMUX_SOURCE_PMU,
>> +					DRV_TYPE_IO_1V8_ONLY,
>> +					DRV_TYPE_IO_1V8_ONLY,
>> +					0,
>> +					0,
> DRV_TYPE_IO_DEFAULT instead of 0 please.
>
>
>
>




From robh at kernel.org  Fri Jan 29 08:28:00 2016
From: robh at kernel.org (Rob Herring)
Date: Fri, 29 Jan 2016 10:28:00 -0600
Subject: [PATCH v4 6/9] ASoC: rt5616: add mclk property for rt5616 document
In-Reply-To: <1453970618-4383-7-git-send-email-wxt@rock-chips.com>
References: <1453970618-4383-1-git-send-email-wxt@rock-chips.com>
 <1453970618-4383-7-git-send-email-wxt@rock-chips.com>
Message-ID: <20160129162800.GA10835@rob-hp-laptop>

On Thu, Jan 28, 2016 at 04:43:35PM +0800, Caesar Wang wrote:
> This patch add the mclk property for the CODEC driver,
> since sometime the CODEC driver needs the clock enabled.

s/add/adds/

s/sometime/sometimes/

> The system clock of ALC5616 can be selected from MCLK,
> That's also make as the codec master clock provider,

That also makes the codec the master clock provider.

> 
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
> 
> ---
> 
> Changes in v4:
> - Add this patch included in kylin series patches.
> 
>  Documentation/devicetree/bindings/sound/rt5616.txt | 6 ++++++
>  1 file changed, 6 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/sound/rt5616.txt b/Documentation/devicetree/bindings/sound/rt5616.txt
> index efc48c6..e410858 100644
> --- a/Documentation/devicetree/bindings/sound/rt5616.txt
> +++ b/Documentation/devicetree/bindings/sound/rt5616.txt
> @@ -8,6 +8,12 @@ Required properties:
>  
>  - reg : The I2C address of the device.
>  
> +Optional properties:
> +
> +- clocks: The phandle of the master clock to the CODEC.
> +
> +- clock-names: Should be "mclk".
> +
>  Pins on the device (for linking into audio routes) for RT5616:
>  
>    * IN1P
> -- 
> 1.9.1
> 


From dianders at chromium.org  Fri Jan 29 11:11:59 2016
From: dianders at chromium.org (Doug Anderson)
Date: Fri, 29 Jan 2016 11:11:59 -0800
Subject: [Bug] usb: dwc2: Add functions to set and clear force mode
In-Reply-To: <56A8277F.9@gmail.com>
References: <56A7603C.1000409@rock-chips.com>
 <CAD=FV=Xk4v=1QMYG9XmXh6hJLKU6i_wVakOArF1du-Z_4_+Neg@mail.gmail.com>
 <56A8277F.9@gmail.com>
Message-ID: <CAD=FV=Uab6H6sFVj_bPRs7H4HcBi2pqZK3HDCHQxC+EDk=inDA@mail.gmail.com>

Caesar,

On Tue, Jan 26, 2016 at 6:12 PM, Caesar Wang <caesar.upstream at gmail.com> wrote:
> Thanks Doug's reply.
>
> Cc: Wulf
> ? 2016?01?27? 00:05, Doug Anderson ??:
>>
>> Hi,
>>
>> On Tue, Jan 26, 2016 at 4:02 AM, Caesar Wang <wxt at rock-chips.com> wrote:
>>>
>>> Hi  John, Felipe
>>>
>>>
>>> I'm no familiar with usb stuff.
>>> then I found this patch will break usb working for rk3036 SoCs, maybe
>>> more
>>> SoCs.
>>> Says, U disk can't work on usb host.
>>>
>>> The failure log:
>>>
>>>     32.645481] usb usb2-port1: connect-debounce failed
>>>
>>>
>>> Tested by following branch:
>>> https://github.com/Caesar-github/rockchip/tree/kylin/next   (kernel:
>>> 4.5-rc1)
>>>
>>> Revert "usb: dwc2: Add functions to set and clear force mode" will work
>>> for
>>> it.
>>>
>>>
>>> Maybe, someone have some suggestions or ideas?
>>
>> Can you check if this series helps you?
>>
>> http://marc.info/?l=linux-usb&m=145255851516121&w=2
>
>
> Unluckily, this series patches can't fix it on rk3036 SoCs.
> I revert this CL ("usb: dwc2: Add functions to set and clear force mode") to
> work firstly.

You're 100% positive?  In particular you made sure you had this patch
(one of the two in John's series I pointed at), which explicitly
mentions fixing a problem with the patch you mention?

Author:     John Youn <John.Youn at synopsys.com>
AuthorDate: Mon Jan 11 16:32:28 2016 -0800

    usb: dwc2: Fix probe problem on bcm2835

    Fixes an issue found on Raspberry PI platform that prevents probe. Don't
    skip setting the force mode if it's already set.

    Fixes: 09c96980dc72 ("usb: dwc2: Add functions to set and clear force mode")
    Signed-off-by: John Youn <johnyoun at synopsys.com>
    Reported-by: Stefan Wahren <stefan.wahren at i2se.com>
    Reported-by: Remi Pommarel <repk at triplefau.lt>
    Tested-by: Stefan Wahren <stefan.wahren at i2se.com>
    Tested-by: Remi Pommarel <repk at triplefau.lt>


Maybe try again just in case there was some problem updating your
kernel when you tested before?  If you're sure you can reproduce the
problem even with John's two patches, perhaps you can give more
details about what part of his patch broke things for you.

-Doug


From sboyd at codeaurora.org  Fri Jan 29 16:31:00 2016
From: sboyd at codeaurora.org (Stephen Boyd)
Date: Fri, 29 Jan 2016 16:31:00 -0800
Subject: [PATCH] clk: rockchip: fix wrong mmc phase shift for rk3228
In-Reply-To: <1453779018-30666-1-git-send-email-shawn.lin@rock-chips.com>
References: <1453779018-30666-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <20160130003100.GB12841@codeaurora.org>

On 01/26, Shawn Lin wrote:
> mmc sample shift is 0 for rk3228 refer to user manaul.
> So it's broken if we enable mmc tuning for rk3228.
> 
> Fixes: 307a2e9ac ("clk: rockchip: add clock controller for rk3228")
> Cc: Xing Zheng <zhengxing at rock-chips.com>
> Cc: Jeffy Chen <jeffy.chen at rock-chips.com>
> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
> ---

Acked-by: Stephen Boyd <sboyd at codeaurora.org>

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


From sboyd at codeaurora.org  Fri Jan 29 17:29:23 2016
From: sboyd at codeaurora.org (Stephen Boyd)
Date: Fri, 29 Jan 2016 17:29:23 -0800
Subject: [PATCH v3] clk-divider: make sure read-only dividers do not
 write to their register
In-Reply-To: <4666170.lmsMJxAogk@phil>
References: <4666170.lmsMJxAogk@phil>
Message-ID: <20160130012923.GF4848@codeaurora.org>

On 01/21, Heiko Stuebner wrote:
> Commit e6d5e7d90be9 ("clk-divider: Fix READ_ONLY when divider > 1") removed
> the special ops struct for read-only clocks and instead opted to handle
> them inside the regular ops.
> 
> On the rk3368 this results in breakage as aclkm now gets set a value.
> While it is the same divider value, the A53 core still doesn't like it,
> which can result in the cpu ending up in a hang.
> The reason being that "ACLKENMasserts one clock cycle before the rising
> edge of ACLKM" and the clock should only be touched when STANDBYWFIL2
> is asserted.
> 
> To fix this, reintroduce the read-only ops but do include the round_rate
> callback. That way no writes that may be unsafe are done to the divider
> register in any case.
> 
> The Rockchip use of the clk_divider_ops is adapted to this split again,
> as is the nxp, lpc18xx-ccu driver that was included since the original
> commit. On lpc18xx-ccu the divider seems to always be read-only
> so only uses the new ops now.
> 
> Fixes: e6d5e7d90be9 ("clk-divider: Fix READ_ONLY when divider > 1")
> Reported-by: Zhang Qing <zhangqing at rock-chips.com>
> Signed-off-by: Heiko Stuebner <heiko at sntech.de>
> ---

Applied to clk-next

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


From david.wu at rock-chips.com  Sat Jan 30 03:31:55 2016
From: david.wu at rock-chips.com (David Wu)
Date: Sat, 30 Jan 2016 19:31:55 +0800
Subject: [PATCH v3 0/2] add pinctrl support for rk3399
In-Reply-To: <1451997055-47655-1-git-send-email-wdc@rock-chips.com>
References: <1451997055-47655-1-git-send-email-wdc@rock-chips.com>
Message-ID: <1454153517-52969-1-git-send-email-david.wu@rock-chips.com>

Test on rk3399 fpga-board

David Wu (2):
  pinctrl: rockchip: add support for the rk3399
  dt-bindings: rockchip-pinctrl: Support the RK3399 SoCs compatible

 .../bindings/pinctrl/rockchip,pinctrl.txt          |   1 +
 drivers/pinctrl/pinctrl-rockchip.c                 | 372 ++++++++++++++++++++-
 2 files changed, 359 insertions(+), 14 deletions(-)

-- 
1.9.1




From david.wu at rock-chips.com  Sat Jan 30 03:31:57 2016
From: david.wu at rock-chips.com (David Wu)
Date: Sat, 30 Jan 2016 19:31:57 +0800
Subject: [PATCH v3 2/2] dt-bindings: rockchip-pinctrl: Support the RK3399 SoCs
 compatible
In-Reply-To: <1454153517-52969-1-git-send-email-david.wu@rock-chips.com>
References: <1451997055-47655-1-git-send-email-wdc@rock-chips.com>
 <1454153517-52969-1-git-send-email-david.wu@rock-chips.com>
Message-ID: <1454153517-52969-3-git-send-email-david.wu@rock-chips.com>

Change-Id: Ic1bdb3dc358837e97d7b48dd58be2f8d9d08d766
---
 Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
index 391ef4b..3bb9456 100644
--- a/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
+++ b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
@@ -22,6 +22,7 @@ Required properties for iomux controller:
   - compatible: one of "rockchip,rk2928-pinctrl", "rockchip,rk3066a-pinctrl"
 		       "rockchip,rk3066b-pinctrl", "rockchip,rk3188-pinctrl"
 		       "rockchip,rk3288-pinctrl", "rockchip,rk3368-pinctrl"
+		       "rockchip,rk3399-pinctrl"
   - rockchip,grf: phandle referencing a syscon providing the
 	 "general register files"
 
-- 
1.9.1




From david.wu at rock-chips.com  Sat Jan 30 03:31:56 2016
From: david.wu at rock-chips.com (David Wu)
Date: Sat, 30 Jan 2016 19:31:56 +0800
Subject: [PATCH v3 1/2] pinctrl: rockchip: add support for the rk3399
In-Reply-To: <1454153517-52969-1-git-send-email-david.wu@rock-chips.com>
References: <1451997055-47655-1-git-send-email-wdc@rock-chips.com>
 <1454153517-52969-1-git-send-email-david.wu@rock-chips.com>
Message-ID: <1454153517-52969-2-git-send-email-david.wu@rock-chips.com>

The pinctrl of rk3399 is much different from other's,
especially the 3bits of drive strength.

Signed-off-by: David Wu <david.wu at rock-chips.com>
---
Change in v3:
- use switch-case to distinguish special 3bits width per pin (Heiko)

Change in v2:
- need spin_unlock_irqrestore for set drive default case

 drivers/pinctrl/pinctrl-rockchip.c | 372 +++++++++++++++++++++++++++++++++++--
 1 file changed, 358 insertions(+), 14 deletions(-)

diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c
index a065112..d5c71b0 100644
--- a/drivers/pinctrl/pinctrl-rockchip.c
+++ b/drivers/pinctrl/pinctrl-rockchip.c
@@ -64,6 +64,7 @@ enum rockchip_pinctrl_type {
 	RK3188,
 	RK3288,
 	RK3368,
+	RK3399,
 };
 
 /**
@@ -86,6 +87,31 @@ struct rockchip_iomux {
 };
 
 /**
+ * enum type index corresponding to rockchip_perpin_drv_list arrays index.
+ */
+enum rockchip_pin_drv_type {
+	DRV_TYPE_IO_DEFAULT = 0,
+	DRV_TYPE_IO_1V8_OR_3V0,
+	DRV_TYPE_IO_1V8_ONLY,
+	DRV_TYPE_IO_1V8_3V0_AUTO,
+	DRV_TYPE_IO_3V3_ONLY,
+	DRV_TYPE_MAX
+};
+
+/**
+ * @drv_type: drive strength variant using rockchip_perpin_drv_type
+ * @offset: if initialized to -1 it will be autocalculated, by specifying
+ *	    an initial offset value the relevant source offset can be reset
+ *	    to a new value for autocalculating the following drive strength
+ *	    registers. if used chips own cal_drv func instead to calculate
+ *	    registers offset, the variant could be ignored.
+ */
+struct rockchip_drv {
+	enum rockchip_pin_drv_type	drv_type;
+	int				offset;
+};
+
+/**
  * @reg_base: register base of the gpio bank
  * @reg_pull: optional separate register for additional pull settings
  * @clk: clock of the gpio bank
@@ -96,6 +122,7 @@ struct rockchip_iomux {
  * @name: name of the bank
  * @bank_num: number of the bank, to account for holes
  * @iomux: array describing the 4 iomux sources of the bank
+ * @drv: array describing the 4 drive strength sources of the bank
  * @valid: are all necessary informations present
  * @of_node: dt node of this bank
  * @drvdata: common pinctrl basedata
@@ -115,6 +142,7 @@ struct rockchip_pin_bank {
 	char				*name;
 	u8				bank_num;
 	struct rockchip_iomux		iomux[4];
+	struct rockchip_drv		drv[4];
 	bool				valid;
 	struct device_node		*of_node;
 	struct rockchip_pinctrl		*drvdata;
@@ -151,6 +179,47 @@ struct rockchip_pin_bank {
 		},							\
 	}
 
+#define PIN_BANK_DRV_FLAGS(id, pins, label, type0, type1, type2, type3) \
+	{								\
+		.bank_num	= id,					\
+		.nr_pins	= pins,					\
+		.name		= label,				\
+		.iomux		= {					\
+			{ .offset = -1 },				\
+			{ .offset = -1 },				\
+			{ .offset = -1 },				\
+			{ .offset = -1 },				\
+		},							\
+		.drv		= {					\
+			{ .drv_type = type0, .offset = -1 },		\
+			{ .drv_type = type1, .offset = -1 },		\
+			{ .drv_type = type2, .offset = -1 },		\
+			{ .drv_type = type3, .offset = -1 },		\
+		},							\
+	}
+
+#define PIN_BANK_IOMUX_DRV_FLAGS_OFFSET(id, pins, label, iom0, iom1,	\
+					iom2, iom3, drv0, drv1, drv2,	\
+					drv3, offset0, offset1,		\
+					offset2, offset3)		\
+	{								\
+		.bank_num	= id,					\
+		.nr_pins	= pins,					\
+		.name		= label,				\
+		.iomux		= {					\
+			{ .type = iom0, .offset = -1 },			\
+			{ .type = iom1, .offset = -1 },			\
+			{ .type = iom2, .offset = -1 },			\
+			{ .type = iom3, .offset = -1 },			\
+		},							\
+		.drv		= {					\
+			{ .drv_type = drv0, .offset = offset0 },	\
+			{ .drv_type = drv1, .offset = offset1 },	\
+			{ .drv_type = drv2, .offset = offset2 },	\
+			{ .drv_type = drv3, .offset = offset3 },	\
+		},							\
+	}
+
 /**
  */
 struct rockchip_pin_ctrl {
@@ -161,6 +230,9 @@ struct rockchip_pin_ctrl {
 	enum rockchip_pinctrl_type	type;
 	int				grf_mux_offset;
 	int				pmu_mux_offset;
+	int				grf_drv_offset;
+	int				pmu_drv_offset;
+
 	void	(*pull_calc_reg)(struct rockchip_pin_bank *bank,
 				    int pin_num, struct regmap **regmap,
 				    int *reg, u8 *bit);
@@ -676,7 +748,68 @@ static void rk3368_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
 	}
 }
 
-static int rockchip_perpin_drv_list[] = { 2, 4, 8, 12 };
+#define RK3399_PULL_GRF_OFFSET		0xe040
+#define RK3399_PULL_PMU_OFFSET		0x40
+#define RK3399_DRV_3BITS_PER_PIN	3
+
+static void rk3399_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+					 int pin_num, struct regmap **regmap,
+					 int *reg, u8 *bit)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+
+	/* The bank0:16 and bank1:32 pins are located in PMU */
+	if ((bank->bank_num == 0) || (bank->bank_num == 1)) {
+		*regmap = info->regmap_pmu;
+		*reg = RK3399_PULL_PMU_OFFSET;
+
+		*reg += bank->bank_num * RK3188_PULL_BANK_STRIDE;
+
+		*reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4);
+		*bit = pin_num % RK3188_PULL_PINS_PER_REG;
+		*bit *= RK3188_PULL_BITS_PER_PIN;
+	} else {
+		*regmap = info->regmap_base;
+		*reg = RK3399_PULL_GRF_OFFSET;
+
+		/* correct the offset, as we're starting with the 3rd bank */
+		*reg -= 0x20;
+		*reg += bank->bank_num * RK3188_PULL_BANK_STRIDE;
+		*reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4);
+
+		*bit = (pin_num % RK3188_PULL_PINS_PER_REG);
+		*bit *= RK3188_PULL_BITS_PER_PIN;
+	}
+}
+
+static void rk3399_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
+					int pin_num, struct regmap **regmap,
+					int *reg, u8 *bit)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+	int drv_num = (pin_num / 8);
+
+	/*  The bank0:16 and bank1:32 pins are located in PMU */
+	if ((bank->bank_num == 0) || (bank->bank_num == 1))
+		*regmap = info->regmap_pmu;
+	else
+		*regmap = info->regmap_base;
+
+	*reg = bank->drv[drv_num].offset;
+	if ((bank->drv[drv_num].drv_type == DRV_TYPE_IO_1V8_3V0_AUTO) ||
+	    (bank->drv[drv_num].drv_type == DRV_TYPE_IO_3V3_ONLY))
+		*bit = (pin_num % 8) * 3;
+	else
+		*bit = (pin_num % 8) * 2;
+}
+
+static int rockchip_perpin_drv_list[DRV_TYPE_MAX][8] = {
+	{ 2, 4, 8, 12, -1, -1, -1, -1 },
+	{ 3, 6, 9, 12, -1, -1, -1, -1 },
+	{ 5, 10, 15, 20, -1, -1, -1, -1 },
+	{ 4, 6, 8, 10, 12, 14, 16, 18 },
+	{ 4, 7, 10, 13, 16, 19, 22, 26 }
+};
 
 static int rockchip_get_drive_perpin(struct rockchip_pin_bank *bank,
 				     int pin_num)
@@ -685,19 +818,74 @@ static int rockchip_get_drive_perpin(struct rockchip_pin_bank *bank,
 	struct rockchip_pin_ctrl *ctrl = info->ctrl;
 	struct regmap *regmap;
 	int reg, ret;
-	u32 data;
+	u32 data, temp, rmask_bits;
 	u8 bit;
+	int drv_type = bank->drv[pin_num / 8].drv_type;
 
 	ctrl->drv_calc_reg(bank, pin_num, &regmap, &reg, &bit);
 
+	switch (drv_type) {
+	case DRV_TYPE_IO_1V8_3V0_AUTO:
+	case DRV_TYPE_IO_3V3_ONLY:
+		rmask_bits = RK3399_DRV_3BITS_PER_PIN;
+		switch (bit) {
+		case 0 ... 12:
+			/* regular case, nothing to do */
+			break;
+		case 15:
+			/*
+			 * drive-strength offset is special, as it is
+			 * spread over 2 registers
+			 */
+			ret = regmap_read(regmap, reg, &data);
+			if (ret)
+				return ret;
+
+			ret = regmap_read(regmap, reg + 0x4, &temp);
+			if (ret)
+				return ret;
+
+			/*
+			 * the bit data[15] contains bit 0 of the value
+			 * while temp[1:0] contains bits 2 and 1
+			 */
+			data >>= 15;
+			temp &= 0x3;
+			temp <<= 1;
+			data |= temp;
+
+			return rockchip_perpin_drv_list[drv_type][data];
+		case 18 ... 21:
+			/* setting fully enclosed in the second register */
+			reg += 4;
+			bit -= 16;
+			break;
+		default:
+			dev_err(info->dev, "unsupported bit: %d for pinctrl drive type: %d\n",
+				bit, drv_type);
+			return -EINVAL;
+		}
+
+		break;
+	case DRV_TYPE_IO_DEFAULT:
+	case DRV_TYPE_IO_1V8_OR_3V0:
+	case DRV_TYPE_IO_1V8_ONLY:
+		rmask_bits = RK3288_DRV_BITS_PER_PIN;
+		break;
+	default:
+		dev_err(info->dev, "unsupported pinctrl drive type: %d\n",
+			drv_type);
+		return -EINVAL;
+	}
+
 	ret = regmap_read(regmap, reg, &data);
 	if (ret)
 		return ret;
 
 	data >>= bit;
-	data &= (1 << RK3288_DRV_BITS_PER_PIN) - 1;
+	data &= (1 << rmask_bits) - 1;
 
-	return rockchip_perpin_drv_list[data];
+	return rockchip_perpin_drv_list[drv_type][data];
 }
 
 static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank,
@@ -708,16 +896,23 @@ static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank,
 	struct regmap *regmap;
 	unsigned long flags;
 	int reg, ret, i;
-	u32 data, rmask;
+	u32 data, rmask, rmask_bits, temp;
 	u8 bit;
+	int drv_type = bank->drv[pin_num / 8].drv_type;
+
+	dev_dbg(info->dev, "setting drive of GPIO%d-%d to %d\n",
+		bank->bank_num, pin_num, strength);
 
 	ctrl->drv_calc_reg(bank, pin_num, &regmap, &reg, &bit);
 
 	ret = -EINVAL;
-	for (i = 0; i < ARRAY_SIZE(rockchip_perpin_drv_list); i++) {
-		if (rockchip_perpin_drv_list[i] == strength) {
+	for (i = 0; i < ARRAY_SIZE(rockchip_perpin_drv_list[drv_type]); i++) {
+		if (rockchip_perpin_drv_list[drv_type][i] == strength) {
 			ret = i;
 			break;
+		} else if (rockchip_perpin_drv_list[drv_type][i] < 0) {
+			ret = rockchip_perpin_drv_list[drv_type][i];
+			break;
 		}
 	}
 
@@ -729,8 +924,67 @@ static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank,
 
 	spin_lock_irqsave(&bank->slock, flags);
 
+	switch (drv_type) {
+	case DRV_TYPE_IO_1V8_3V0_AUTO:
+	case DRV_TYPE_IO_3V3_ONLY:
+		rmask_bits = RK3399_DRV_3BITS_PER_PIN;
+		switch (bit) {
+		case 0 ... 12:
+			/* regular case, nothing to do */
+			break;
+		case 15:
+			/*
+			 * the bit data[15] contains bit 0 of the value
+			 * while temp[1:0] contains bits 2 and 1
+			 */
+			data = (ret & 0x1) << 15;
+			temp = (ret >> 0x1) & 0x3;
+
+			rmask = BIT(15) | BIT(31);
+			data |= BIT(31);
+			ret = regmap_update_bits(regmap, reg, rmask, data);
+			if (ret) {
+				spin_unlock_irqrestore(&bank->slock, flags);
+				return ret;
+			}
+
+			/*
+			 * drive-strength offset is special, as it is
+			 * spread over 2 registers
+			 */
+			rmask = 0x3 | (0x3 << 16);
+			temp |= (0x3 << 16);
+			reg += 0x4;
+			ret = regmap_update_bits(regmap, reg, rmask, temp);
+
+			spin_unlock_irqrestore(&bank->slock, flags);
+			return ret;
+		case 18 ... 21:
+			/* setting fully enclosed in the second register */
+			reg += 4;
+			bit -= 16;
+			break;
+		default:
+			spin_unlock_irqrestore(&bank->slock, flags);
+			dev_err(info->dev, "unsupported bit: %d for pinctrl drive type: %d\n",
+				bit, drv_type);
+			return -EINVAL;
+		}
+		break;
+	case DRV_TYPE_IO_DEFAULT:
+	case DRV_TYPE_IO_1V8_OR_3V0:
+	case DRV_TYPE_IO_1V8_ONLY:
+		rmask_bits = RK3288_DRV_BITS_PER_PIN;
+		break;
+	default:
+		spin_unlock_irqrestore(&bank->slock, flags);
+		dev_err(info->dev, "unsupported pinctrl drive type: %d\n",
+			drv_type);
+		return -EINVAL;
+	}
+
 	/* enable the write to the equivalent lower bits */
-	data = ((1 << RK3288_DRV_BITS_PER_PIN) - 1) << (bit + 16);
+	data = ((1 << rmask_bits) - 1) << (bit + 16);
 	rmask = data | (data >> 16);
 	data |= (ret << bit);
 
@@ -767,6 +1021,7 @@ static int rockchip_get_pull(struct rockchip_pin_bank *bank, int pin_num)
 	case RK3188:
 	case RK3288:
 	case RK3368:
+	case RK3399:
 		data >>= bit;
 		data &= (1 << RK3188_PULL_BITS_PER_PIN) - 1;
 
@@ -823,6 +1078,7 @@ static int rockchip_set_pull(struct rockchip_pin_bank *bank,
 	case RK3188:
 	case RK3288:
 	case RK3368:
+	case RK3399:
 		spin_lock_irqsave(&bank->slock, flags);
 
 		/* enable the write to the equivalent lower bits */
@@ -1003,6 +1259,7 @@ static bool rockchip_pinconf_pull_valid(struct rockchip_pin_ctrl *ctrl,
 	case RK3188:
 	case RK3288:
 	case RK3368:
+	case RK3399:
 		return (pull != PIN_CONFIG_BIAS_PULL_PIN_DEFAULT);
 	}
 
@@ -1860,7 +2117,7 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
 	struct device_node *np;
 	struct rockchip_pin_ctrl *ctrl;
 	struct rockchip_pin_bank *bank;
-	int grf_offs, pmu_offs, i, j;
+	int grf_offs, pmu_offs, drv_grf_offs, drv_pmu_offs, i, j;
 
 	match = of_match_node(rockchip_pinctrl_dt_match, node);
 	ctrl = (struct rockchip_pin_ctrl *)match->data;
@@ -1884,6 +2141,8 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
 
 	grf_offs = ctrl->grf_mux_offset;
 	pmu_offs = ctrl->pmu_mux_offset;
+	drv_pmu_offs = ctrl->pmu_drv_offset;
+	drv_grf_offs = ctrl->grf_drv_offset;
 	bank = ctrl->pin_banks;
 	for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
 		int bank_pins = 0;
@@ -1893,27 +2152,39 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
 		bank->pin_base = ctrl->nr_pins;
 		ctrl->nr_pins += bank->nr_pins;
 
-		/* calculate iomux offsets */
+		/* calculate iomux and drv offsets */
 		for (j = 0; j < 4; j++) {
 			struct rockchip_iomux *iom = &bank->iomux[j];
+			struct rockchip_drv *drv = &bank->drv[j];
 			int inc;
 
 			if (bank_pins >= bank->nr_pins)
 				break;
 
-			/* preset offset value, set new start value */
+			/* preset iomux offset value, set new start value */
 			if (iom->offset >= 0) {
 				if (iom->type & IOMUX_SOURCE_PMU)
 					pmu_offs = iom->offset;
 				else
 					grf_offs = iom->offset;
-			} else { /* set current offset */
+			} else { /* set current iomux offset */
 				iom->offset = (iom->type & IOMUX_SOURCE_PMU) ?
 							pmu_offs : grf_offs;
 			}
 
-			dev_dbg(d->dev, "bank %d, iomux %d has offset 0x%x\n",
-				 i, j, iom->offset);
+			/* preset drv offset value, set new start value */
+			if (drv->offset >= 0) {
+				if (iom->type & IOMUX_SOURCE_PMU)
+					drv_pmu_offs = drv->offset;
+				else
+					drv_grf_offs = drv->offset;
+			} else { /* set current drv offset */
+				drv->offset = (iom->type & IOMUX_SOURCE_PMU) ?
+						drv_pmu_offs : drv_grf_offs;
+			}
+
+			dev_dbg(d->dev, "bank %d, iomux %d has iom_offset 0x%x drv_offset 0x%x\n",
+				i, j, iom->offset, drv->offset);
 
 			/*
 			 * Increase offset according to iomux width.
@@ -1925,6 +2196,21 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
 			else
 				grf_offs += inc;
 
+			/*
+			 * Increase offset according to drv width.
+			 * 3bit drive-strenth'es are spread over two registers.
+			 */
+			if ((drv->drv_type == DRV_TYPE_IO_1V8_3V0_AUTO) ||
+			    (drv->drv_type == DRV_TYPE_IO_3V3_ONLY))
+				inc = 8;
+			else
+				inc = 4;
+
+			if (iom->type & IOMUX_SOURCE_PMU)
+				drv_pmu_offs += inc;
+			else
+				drv_grf_offs += inc;
+
 			bank_pins += 8;
 		}
 	}
@@ -2208,6 +2494,62 @@ static struct rockchip_pin_ctrl rk3368_pin_ctrl = {
 		.drv_calc_reg		= rk3368_calc_drv_reg_and_bit,
 };
 
+static struct rockchip_pin_bank rk3399_pin_banks[] = {
+	PIN_BANK_IOMUX_DRV_FLAGS_OFFSET(0, 32, "gpio0", IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					DRV_TYPE_IO_1V8_ONLY,
+					DRV_TYPE_IO_1V8_ONLY,
+					DRV_TYPE_IO_DEFAULT,
+					DRV_TYPE_IO_DEFAULT,
+					0x0,
+					0x8,
+					-1,
+					-1
+					),
+	PIN_BANK_IOMUX_DRV_FLAGS_OFFSET(1, 32, "gpio1", IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					DRV_TYPE_IO_1V8_OR_3V0,
+					DRV_TYPE_IO_1V8_OR_3V0,
+					DRV_TYPE_IO_1V8_OR_3V0,
+					DRV_TYPE_IO_1V8_OR_3V0,
+					0x20,
+					0x28,
+					0x30,
+					0x38
+					),
+	PIN_BANK_DRV_FLAGS(2, 32, "gpio2", DRV_TYPE_IO_1V8_OR_3V0,
+			   DRV_TYPE_IO_1V8_OR_3V0,
+			   DRV_TYPE_IO_1V8_ONLY,
+			   DRV_TYPE_IO_1V8_ONLY
+			   ),
+	PIN_BANK_DRV_FLAGS(3, 32, "gpio3", DRV_TYPE_IO_3V3_ONLY,
+			   DRV_TYPE_IO_3V3_ONLY,
+			   DRV_TYPE_IO_3V3_ONLY,
+			   DRV_TYPE_IO_1V8_OR_3V0
+			   ),
+	PIN_BANK_DRV_FLAGS(4, 32, "gpio4", DRV_TYPE_IO_1V8_OR_3V0,
+			   DRV_TYPE_IO_1V8_3V0_AUTO,
+			   DRV_TYPE_IO_1V8_OR_3V0,
+			   DRV_TYPE_IO_1V8_OR_3V0
+			   ),
+};
+
+static struct rockchip_pin_ctrl rk3399_pin_ctrl = {
+		.pin_banks		= rk3399_pin_banks,
+		.nr_banks		= ARRAY_SIZE(rk3399_pin_banks),
+		.label			= "RK3399-GPIO",
+		.type			= RK3399,
+		.grf_mux_offset		= 0xe000,
+		.pmu_mux_offset		= 0x0,
+		.grf_drv_offset		= 0xe100,
+		.pmu_drv_offset		= 0x80,
+		.pull_calc_reg		= rk3399_calc_pull_reg_and_bit,
+		.drv_calc_reg		= rk3399_calc_drv_reg_and_bit,
+};
 
 static const struct of_device_id rockchip_pinctrl_dt_match[] = {
 	{ .compatible = "rockchip,rk2928-pinctrl",
@@ -2224,6 +2566,8 @@ static const struct of_device_id rockchip_pinctrl_dt_match[] = {
 		.data = (void *)&rk3288_pin_ctrl },
 	{ .compatible = "rockchip,rk3368-pinctrl",
 		.data = (void *)&rk3368_pin_ctrl },
+	{ .compatible = "rockchip,rk3399-pinctrl",
+		.data = (void *)&rk3399_pin_ctrl },
 	{},
 };
 MODULE_DEVICE_TABLE(of, rockchip_pinctrl_dt_match);
-- 
1.9.1




From david.wu at rock-chips.com  Sat Jan 30 04:01:45 2016
From: david.wu at rock-chips.com (David Wu)
Date: Sat, 30 Jan 2016 20:01:45 +0800
Subject: [PATCH] PM / AVS: rockchip-io: add GRF and PMUGRF types to distinguish
Message-ID: <1454155305-55516-1-git-send-email-david.wu@rock-chips.com>

As rk3368 contained two separated iodomain areas, this was
determined to use which regmap base address.

Signed-off-by: David Wu <david.wu at rock-chips.com>
---
 drivers/power/avs/rockchip-io-domain.c | 32 ++++++++++++++++++++++++--------
 1 file changed, 24 insertions(+), 8 deletions(-)

diff --git a/drivers/power/avs/rockchip-io-domain.c b/drivers/power/avs/rockchip-io-domain.c
index 8099456..b17aeb7 100644
--- a/drivers/power/avs/rockchip-io-domain.c
+++ b/drivers/power/avs/rockchip-io-domain.c
@@ -47,6 +47,11 @@
 #define RK3368_SOC_CON15_FLASH0		BIT(14)
 #define RK3368_SOC_FLASH_SUPPLY_NUM	2
 
+enum rockchip_iodomain_grf_type {
+	GRF,
+	PMUGRF
+};
+
 struct rockchip_iodomain;
 
 /**
@@ -54,6 +59,7 @@ struct rockchip_iodomain;
  */
 struct rockchip_iodomain_soc_data {
 	int grf_offset;
+	enum rockchip_iodomain_grf_type type;
 	const char *supply_names[MAX_SUPPLIES];
 	void (*init)(struct rockchip_iodomain *iod);
 };
@@ -67,7 +73,7 @@ struct rockchip_iodomain_supply {
 
 struct rockchip_iodomain {
 	struct device *dev;
-	struct regmap *grf;
+	struct regmap *base;
 	struct rockchip_iodomain_soc_data *soc_data;
 	struct rockchip_iodomain_supply supplies[MAX_SUPPLIES];
 };
@@ -86,7 +92,7 @@ static int rockchip_iodomain_write(struct rockchip_iodomain_supply *supply,
 	/* apply hiword-mask */
 	val |= (BIT(supply->idx) << 16);
 
-	ret = regmap_write(iod->grf, iod->soc_data->grf_offset, val);
+	ret = regmap_write(iod->base, iod->soc_data->grf_offset, val);
 	if (ret)
 		dev_err(iod->dev, "Couldn't write to GRF\n");
 
@@ -157,7 +163,7 @@ static void rk3288_iodomain_init(struct rockchip_iodomain *iod)
 	 * instead of a special gpio.
 	 */
 	val = RK3288_SOC_CON2_FLASH0 | (RK3288_SOC_CON2_FLASH0 << 16);
-	ret = regmap_write(iod->grf, RK3288_SOC_CON2, val);
+	ret = regmap_write(iod->base, RK3288_SOC_CON2, val);
 	if (ret < 0)
 		dev_warn(iod->dev, "couldn't update flash0 ctrl\n");
 }
@@ -176,7 +182,7 @@ static void rk3368_iodomain_init(struct rockchip_iodomain *iod)
 	 * instead of a special gpio.
 	 */
 	val = RK3368_SOC_CON15_FLASH0 | (RK3368_SOC_CON15_FLASH0 << 16);
-	ret = regmap_write(iod->grf, RK3368_SOC_CON15, val);
+	ret = regmap_write(iod->base, RK3368_SOC_CON15, val);
 	if (ret < 0)
 		dev_warn(iod->dev, "couldn't update flash0 ctrl\n");
 }
@@ -187,6 +193,7 @@ static void rk3368_iodomain_init(struct rockchip_iodomain *iod)
  */
 static const struct rockchip_iodomain_soc_data soc_data_rk3188 = {
 	.grf_offset = 0x104,
+	.type = GRF,
 	.supply_names = {
 		NULL,
 		NULL,
@@ -209,6 +216,7 @@ static const struct rockchip_iodomain_soc_data soc_data_rk3188 = {
 
 static const struct rockchip_iodomain_soc_data soc_data_rk3288 = {
 	.grf_offset = 0x380,
+	.type = GRF,
 	.supply_names = {
 		"lcdc",		/* LCDC_VDD */
 		"dvp",		/* DVPIO_VDD */
@@ -226,6 +234,7 @@ static const struct rockchip_iodomain_soc_data soc_data_rk3288 = {
 
 static const struct rockchip_iodomain_soc_data soc_data_rk3368 = {
 	.grf_offset = 0x900,
+	.type = GRF,
 	.supply_names = {
 		NULL,		/* reserved */
 		"dvp",		/* DVPIO_VDD */
@@ -242,6 +251,7 @@ static const struct rockchip_iodomain_soc_data soc_data_rk3368 = {
 
 static const struct rockchip_iodomain_soc_data soc_data_rk3368_pmu = {
 	.grf_offset = 0x100,
+	.type = PMUGRF,
 	.supply_names = {
 		NULL,
 		NULL,
@@ -293,10 +303,16 @@ static int rockchip_iodomain_probe(struct platform_device *pdev)
 	match = of_match_node(rockchip_iodomain_match, np);
 	iod->soc_data = (struct rockchip_iodomain_soc_data *)match->data;
 
-	iod->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
-	if (IS_ERR(iod->grf)) {
-		dev_err(&pdev->dev, "couldn't find grf regmap\n");
-		return PTR_ERR(iod->grf);
+	if (iod->soc_data->type == PMUGRF)
+		iod->base = syscon_regmap_lookup_by_phandle(
+				np, "rockchip,pmugrf");
+	else
+		iod->base = syscon_regmap_lookup_by_phandle(
+				np, "rockchip,grf");
+	if (IS_ERR(iod->base)) {
+		dev_err(&pdev->dev, "couldn't find %s regmap\n",
+			(iod->soc_data->type == PMUGRF) ? "pmugrf" : "grf");
+		return PTR_ERR(iod->base);
 	}
 
 	for (i = 0; i < MAX_SUPPLIES; i++) {
-- 
1.9.1




From david.wu at rock-chips.com  Sat Jan 30 04:20:11 2016
From: david.wu at rock-chips.com (David Wu)
Date: Sat, 30 Jan 2016 20:20:11 +0800
Subject: [PATCH v4 0/2] add pinctrl support for rk3399
Message-ID: <1454156413-56148-1-git-send-email-david.wu@rock-chips.com>

Test on rk3399 fpga-board

David Wu (2):
  pinctrl: rockchip: add support for the rk3399
  dt-bindings: rockchip-pinctrl: Support the RK3399 SoCs compatible

 .../bindings/pinctrl/rockchip,pinctrl.txt          |   1 +
 drivers/pinctrl/pinctrl-rockchip.c                 | 372 ++++++++++++++++++++-
 2 files changed, 359 insertions(+), 14 deletions(-)

-- 
1.9.1




From david.wu at rock-chips.com  Sat Jan 30 04:20:13 2016
From: david.wu at rock-chips.com (David Wu)
Date: Sat, 30 Jan 2016 20:20:13 +0800
Subject: [PATCH v4 2/2] dt-bindings: rockchip-pinctrl: Support the RK3399 SoCs
 compatible
In-Reply-To: <1454156413-56148-1-git-send-email-david.wu@rock-chips.com>
References: <1454156413-56148-1-git-send-email-david.wu@rock-chips.com>
Message-ID: <1454156413-56148-3-git-send-email-david.wu@rock-chips.com>

Signed-off-by: David Wu <david.wu at rock-chips.com>
---
Change in v4:
- need Signed-off

 Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
index 391ef4b..3bb9456 100644
--- a/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
+++ b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
@@ -22,6 +22,7 @@ Required properties for iomux controller:
   - compatible: one of "rockchip,rk2928-pinctrl", "rockchip,rk3066a-pinctrl"
 		       "rockchip,rk3066b-pinctrl", "rockchip,rk3188-pinctrl"
 		       "rockchip,rk3288-pinctrl", "rockchip,rk3368-pinctrl"
+		       "rockchip,rk3399-pinctrl"
   - rockchip,grf: phandle referencing a syscon providing the
 	 "general register files"
 
-- 
1.9.1




From david.wu at rock-chips.com  Sat Jan 30 04:20:12 2016
From: david.wu at rock-chips.com (David Wu)
Date: Sat, 30 Jan 2016 20:20:12 +0800
Subject: [PATCH v4 1/2] pinctrl: rockchip: add support for the rk3399
In-Reply-To: <1454156413-56148-1-git-send-email-david.wu@rock-chips.com>
References: <1454156413-56148-1-git-send-email-david.wu@rock-chips.com>
Message-ID: <1454156413-56148-2-git-send-email-david.wu@rock-chips.com>

The pinctrl of rk3399 is much different from other's,
especially the 3bits of drive strength.

Signed-off-by: David Wu <david.wu at rock-chips.com>
---
Change in v4: None

Change in v3:
- use switch-case to distinguish special 3bits width per pin (Heiko)

Change in v2:
- need spin_unlock_irqrestore for set drive default case

 drivers/pinctrl/pinctrl-rockchip.c | 372 +++++++++++++++++++++++++++++++++++--
 1 file changed, 358 insertions(+), 14 deletions(-)

diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c
index a065112..d5c71b0 100644
--- a/drivers/pinctrl/pinctrl-rockchip.c
+++ b/drivers/pinctrl/pinctrl-rockchip.c
@@ -64,6 +64,7 @@ enum rockchip_pinctrl_type {
 	RK3188,
 	RK3288,
 	RK3368,
+	RK3399,
 };
 
 /**
@@ -86,6 +87,31 @@ struct rockchip_iomux {
 };
 
 /**
+ * enum type index corresponding to rockchip_perpin_drv_list arrays index.
+ */
+enum rockchip_pin_drv_type {
+	DRV_TYPE_IO_DEFAULT = 0,
+	DRV_TYPE_IO_1V8_OR_3V0,
+	DRV_TYPE_IO_1V8_ONLY,
+	DRV_TYPE_IO_1V8_3V0_AUTO,
+	DRV_TYPE_IO_3V3_ONLY,
+	DRV_TYPE_MAX
+};
+
+/**
+ * @drv_type: drive strength variant using rockchip_perpin_drv_type
+ * @offset: if initialized to -1 it will be autocalculated, by specifying
+ *	    an initial offset value the relevant source offset can be reset
+ *	    to a new value for autocalculating the following drive strength
+ *	    registers. if used chips own cal_drv func instead to calculate
+ *	    registers offset, the variant could be ignored.
+ */
+struct rockchip_drv {
+	enum rockchip_pin_drv_type	drv_type;
+	int				offset;
+};
+
+/**
  * @reg_base: register base of the gpio bank
  * @reg_pull: optional separate register for additional pull settings
  * @clk: clock of the gpio bank
@@ -96,6 +122,7 @@ struct rockchip_iomux {
  * @name: name of the bank
  * @bank_num: number of the bank, to account for holes
  * @iomux: array describing the 4 iomux sources of the bank
+ * @drv: array describing the 4 drive strength sources of the bank
  * @valid: are all necessary informations present
  * @of_node: dt node of this bank
  * @drvdata: common pinctrl basedata
@@ -115,6 +142,7 @@ struct rockchip_pin_bank {
 	char				*name;
 	u8				bank_num;
 	struct rockchip_iomux		iomux[4];
+	struct rockchip_drv		drv[4];
 	bool				valid;
 	struct device_node		*of_node;
 	struct rockchip_pinctrl		*drvdata;
@@ -151,6 +179,47 @@ struct rockchip_pin_bank {
 		},							\
 	}
 
+#define PIN_BANK_DRV_FLAGS(id, pins, label, type0, type1, type2, type3) \
+	{								\
+		.bank_num	= id,					\
+		.nr_pins	= pins,					\
+		.name		= label,				\
+		.iomux		= {					\
+			{ .offset = -1 },				\
+			{ .offset = -1 },				\
+			{ .offset = -1 },				\
+			{ .offset = -1 },				\
+		},							\
+		.drv		= {					\
+			{ .drv_type = type0, .offset = -1 },		\
+			{ .drv_type = type1, .offset = -1 },		\
+			{ .drv_type = type2, .offset = -1 },		\
+			{ .drv_type = type3, .offset = -1 },		\
+		},							\
+	}
+
+#define PIN_BANK_IOMUX_DRV_FLAGS_OFFSET(id, pins, label, iom0, iom1,	\
+					iom2, iom3, drv0, drv1, drv2,	\
+					drv3, offset0, offset1,		\
+					offset2, offset3)		\
+	{								\
+		.bank_num	= id,					\
+		.nr_pins	= pins,					\
+		.name		= label,				\
+		.iomux		= {					\
+			{ .type = iom0, .offset = -1 },			\
+			{ .type = iom1, .offset = -1 },			\
+			{ .type = iom2, .offset = -1 },			\
+			{ .type = iom3, .offset = -1 },			\
+		},							\
+		.drv		= {					\
+			{ .drv_type = drv0, .offset = offset0 },	\
+			{ .drv_type = drv1, .offset = offset1 },	\
+			{ .drv_type = drv2, .offset = offset2 },	\
+			{ .drv_type = drv3, .offset = offset3 },	\
+		},							\
+	}
+
 /**
  */
 struct rockchip_pin_ctrl {
@@ -161,6 +230,9 @@ struct rockchip_pin_ctrl {
 	enum rockchip_pinctrl_type	type;
 	int				grf_mux_offset;
 	int				pmu_mux_offset;
+	int				grf_drv_offset;
+	int				pmu_drv_offset;
+
 	void	(*pull_calc_reg)(struct rockchip_pin_bank *bank,
 				    int pin_num, struct regmap **regmap,
 				    int *reg, u8 *bit);
@@ -676,7 +748,68 @@ static void rk3368_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
 	}
 }
 
-static int rockchip_perpin_drv_list[] = { 2, 4, 8, 12 };
+#define RK3399_PULL_GRF_OFFSET		0xe040
+#define RK3399_PULL_PMU_OFFSET		0x40
+#define RK3399_DRV_3BITS_PER_PIN	3
+
+static void rk3399_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+					 int pin_num, struct regmap **regmap,
+					 int *reg, u8 *bit)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+
+	/* The bank0:16 and bank1:32 pins are located in PMU */
+	if ((bank->bank_num == 0) || (bank->bank_num == 1)) {
+		*regmap = info->regmap_pmu;
+		*reg = RK3399_PULL_PMU_OFFSET;
+
+		*reg += bank->bank_num * RK3188_PULL_BANK_STRIDE;
+
+		*reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4);
+		*bit = pin_num % RK3188_PULL_PINS_PER_REG;
+		*bit *= RK3188_PULL_BITS_PER_PIN;
+	} else {
+		*regmap = info->regmap_base;
+		*reg = RK3399_PULL_GRF_OFFSET;
+
+		/* correct the offset, as we're starting with the 3rd bank */
+		*reg -= 0x20;
+		*reg += bank->bank_num * RK3188_PULL_BANK_STRIDE;
+		*reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4);
+
+		*bit = (pin_num % RK3188_PULL_PINS_PER_REG);
+		*bit *= RK3188_PULL_BITS_PER_PIN;
+	}
+}
+
+static void rk3399_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
+					int pin_num, struct regmap **regmap,
+					int *reg, u8 *bit)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+	int drv_num = (pin_num / 8);
+
+	/*  The bank0:16 and bank1:32 pins are located in PMU */
+	if ((bank->bank_num == 0) || (bank->bank_num == 1))
+		*regmap = info->regmap_pmu;
+	else
+		*regmap = info->regmap_base;
+
+	*reg = bank->drv[drv_num].offset;
+	if ((bank->drv[drv_num].drv_type == DRV_TYPE_IO_1V8_3V0_AUTO) ||
+	    (bank->drv[drv_num].drv_type == DRV_TYPE_IO_3V3_ONLY))
+		*bit = (pin_num % 8) * 3;
+	else
+		*bit = (pin_num % 8) * 2;
+}
+
+static int rockchip_perpin_drv_list[DRV_TYPE_MAX][8] = {
+	{ 2, 4, 8, 12, -1, -1, -1, -1 },
+	{ 3, 6, 9, 12, -1, -1, -1, -1 },
+	{ 5, 10, 15, 20, -1, -1, -1, -1 },
+	{ 4, 6, 8, 10, 12, 14, 16, 18 },
+	{ 4, 7, 10, 13, 16, 19, 22, 26 }
+};
 
 static int rockchip_get_drive_perpin(struct rockchip_pin_bank *bank,
 				     int pin_num)
@@ -685,19 +818,74 @@ static int rockchip_get_drive_perpin(struct rockchip_pin_bank *bank,
 	struct rockchip_pin_ctrl *ctrl = info->ctrl;
 	struct regmap *regmap;
 	int reg, ret;
-	u32 data;
+	u32 data, temp, rmask_bits;
 	u8 bit;
+	int drv_type = bank->drv[pin_num / 8].drv_type;
 
 	ctrl->drv_calc_reg(bank, pin_num, &regmap, &reg, &bit);
 
+	switch (drv_type) {
+	case DRV_TYPE_IO_1V8_3V0_AUTO:
+	case DRV_TYPE_IO_3V3_ONLY:
+		rmask_bits = RK3399_DRV_3BITS_PER_PIN;
+		switch (bit) {
+		case 0 ... 12:
+			/* regular case, nothing to do */
+			break;
+		case 15:
+			/*
+			 * drive-strength offset is special, as it is
+			 * spread over 2 registers
+			 */
+			ret = regmap_read(regmap, reg, &data);
+			if (ret)
+				return ret;
+
+			ret = regmap_read(regmap, reg + 0x4, &temp);
+			if (ret)
+				return ret;
+
+			/*
+			 * the bit data[15] contains bit 0 of the value
+			 * while temp[1:0] contains bits 2 and 1
+			 */
+			data >>= 15;
+			temp &= 0x3;
+			temp <<= 1;
+			data |= temp;
+
+			return rockchip_perpin_drv_list[drv_type][data];
+		case 18 ... 21:
+			/* setting fully enclosed in the second register */
+			reg += 4;
+			bit -= 16;
+			break;
+		default:
+			dev_err(info->dev, "unsupported bit: %d for pinctrl drive type: %d\n",
+				bit, drv_type);
+			return -EINVAL;
+		}
+
+		break;
+	case DRV_TYPE_IO_DEFAULT:
+	case DRV_TYPE_IO_1V8_OR_3V0:
+	case DRV_TYPE_IO_1V8_ONLY:
+		rmask_bits = RK3288_DRV_BITS_PER_PIN;
+		break;
+	default:
+		dev_err(info->dev, "unsupported pinctrl drive type: %d\n",
+			drv_type);
+		return -EINVAL;
+	}
+
 	ret = regmap_read(regmap, reg, &data);
 	if (ret)
 		return ret;
 
 	data >>= bit;
-	data &= (1 << RK3288_DRV_BITS_PER_PIN) - 1;
+	data &= (1 << rmask_bits) - 1;
 
-	return rockchip_perpin_drv_list[data];
+	return rockchip_perpin_drv_list[drv_type][data];
 }
 
 static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank,
@@ -708,16 +896,23 @@ static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank,
 	struct regmap *regmap;
 	unsigned long flags;
 	int reg, ret, i;
-	u32 data, rmask;
+	u32 data, rmask, rmask_bits, temp;
 	u8 bit;
+	int drv_type = bank->drv[pin_num / 8].drv_type;
+
+	dev_dbg(info->dev, "setting drive of GPIO%d-%d to %d\n",
+		bank->bank_num, pin_num, strength);
 
 	ctrl->drv_calc_reg(bank, pin_num, &regmap, &reg, &bit);
 
 	ret = -EINVAL;
-	for (i = 0; i < ARRAY_SIZE(rockchip_perpin_drv_list); i++) {
-		if (rockchip_perpin_drv_list[i] == strength) {
+	for (i = 0; i < ARRAY_SIZE(rockchip_perpin_drv_list[drv_type]); i++) {
+		if (rockchip_perpin_drv_list[drv_type][i] == strength) {
 			ret = i;
 			break;
+		} else if (rockchip_perpin_drv_list[drv_type][i] < 0) {
+			ret = rockchip_perpin_drv_list[drv_type][i];
+			break;
 		}
 	}
 
@@ -729,8 +924,67 @@ static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank,
 
 	spin_lock_irqsave(&bank->slock, flags);
 
+	switch (drv_type) {
+	case DRV_TYPE_IO_1V8_3V0_AUTO:
+	case DRV_TYPE_IO_3V3_ONLY:
+		rmask_bits = RK3399_DRV_3BITS_PER_PIN;
+		switch (bit) {
+		case 0 ... 12:
+			/* regular case, nothing to do */
+			break;
+		case 15:
+			/*
+			 * the bit data[15] contains bit 0 of the value
+			 * while temp[1:0] contains bits 2 and 1
+			 */
+			data = (ret & 0x1) << 15;
+			temp = (ret >> 0x1) & 0x3;
+
+			rmask = BIT(15) | BIT(31);
+			data |= BIT(31);
+			ret = regmap_update_bits(regmap, reg, rmask, data);
+			if (ret) {
+				spin_unlock_irqrestore(&bank->slock, flags);
+				return ret;
+			}
+
+			/*
+			 * drive-strength offset is special, as it is
+			 * spread over 2 registers
+			 */
+			rmask = 0x3 | (0x3 << 16);
+			temp |= (0x3 << 16);
+			reg += 0x4;
+			ret = regmap_update_bits(regmap, reg, rmask, temp);
+
+			spin_unlock_irqrestore(&bank->slock, flags);
+			return ret;
+		case 18 ... 21:
+			/* setting fully enclosed in the second register */
+			reg += 4;
+			bit -= 16;
+			break;
+		default:
+			spin_unlock_irqrestore(&bank->slock, flags);
+			dev_err(info->dev, "unsupported bit: %d for pinctrl drive type: %d\n",
+				bit, drv_type);
+			return -EINVAL;
+		}
+		break;
+	case DRV_TYPE_IO_DEFAULT:
+	case DRV_TYPE_IO_1V8_OR_3V0:
+	case DRV_TYPE_IO_1V8_ONLY:
+		rmask_bits = RK3288_DRV_BITS_PER_PIN;
+		break;
+	default:
+		spin_unlock_irqrestore(&bank->slock, flags);
+		dev_err(info->dev, "unsupported pinctrl drive type: %d\n",
+			drv_type);
+		return -EINVAL;
+	}
+
 	/* enable the write to the equivalent lower bits */
-	data = ((1 << RK3288_DRV_BITS_PER_PIN) - 1) << (bit + 16);
+	data = ((1 << rmask_bits) - 1) << (bit + 16);
 	rmask = data | (data >> 16);
 	data |= (ret << bit);
 
@@ -767,6 +1021,7 @@ static int rockchip_get_pull(struct rockchip_pin_bank *bank, int pin_num)
 	case RK3188:
 	case RK3288:
 	case RK3368:
+	case RK3399:
 		data >>= bit;
 		data &= (1 << RK3188_PULL_BITS_PER_PIN) - 1;
 
@@ -823,6 +1078,7 @@ static int rockchip_set_pull(struct rockchip_pin_bank *bank,
 	case RK3188:
 	case RK3288:
 	case RK3368:
+	case RK3399:
 		spin_lock_irqsave(&bank->slock, flags);
 
 		/* enable the write to the equivalent lower bits */
@@ -1003,6 +1259,7 @@ static bool rockchip_pinconf_pull_valid(struct rockchip_pin_ctrl *ctrl,
 	case RK3188:
 	case RK3288:
 	case RK3368:
+	case RK3399:
 		return (pull != PIN_CONFIG_BIAS_PULL_PIN_DEFAULT);
 	}
 
@@ -1860,7 +2117,7 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
 	struct device_node *np;
 	struct rockchip_pin_ctrl *ctrl;
 	struct rockchip_pin_bank *bank;
-	int grf_offs, pmu_offs, i, j;
+	int grf_offs, pmu_offs, drv_grf_offs, drv_pmu_offs, i, j;
 
 	match = of_match_node(rockchip_pinctrl_dt_match, node);
 	ctrl = (struct rockchip_pin_ctrl *)match->data;
@@ -1884,6 +2141,8 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
 
 	grf_offs = ctrl->grf_mux_offset;
 	pmu_offs = ctrl->pmu_mux_offset;
+	drv_pmu_offs = ctrl->pmu_drv_offset;
+	drv_grf_offs = ctrl->grf_drv_offset;
 	bank = ctrl->pin_banks;
 	for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
 		int bank_pins = 0;
@@ -1893,27 +2152,39 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
 		bank->pin_base = ctrl->nr_pins;
 		ctrl->nr_pins += bank->nr_pins;
 
-		/* calculate iomux offsets */
+		/* calculate iomux and drv offsets */
 		for (j = 0; j < 4; j++) {
 			struct rockchip_iomux *iom = &bank->iomux[j];
+			struct rockchip_drv *drv = &bank->drv[j];
 			int inc;
 
 			if (bank_pins >= bank->nr_pins)
 				break;
 
-			/* preset offset value, set new start value */
+			/* preset iomux offset value, set new start value */
 			if (iom->offset >= 0) {
 				if (iom->type & IOMUX_SOURCE_PMU)
 					pmu_offs = iom->offset;
 				else
 					grf_offs = iom->offset;
-			} else { /* set current offset */
+			} else { /* set current iomux offset */
 				iom->offset = (iom->type & IOMUX_SOURCE_PMU) ?
 							pmu_offs : grf_offs;
 			}
 
-			dev_dbg(d->dev, "bank %d, iomux %d has offset 0x%x\n",
-				 i, j, iom->offset);
+			/* preset drv offset value, set new start value */
+			if (drv->offset >= 0) {
+				if (iom->type & IOMUX_SOURCE_PMU)
+					drv_pmu_offs = drv->offset;
+				else
+					drv_grf_offs = drv->offset;
+			} else { /* set current drv offset */
+				drv->offset = (iom->type & IOMUX_SOURCE_PMU) ?
+						drv_pmu_offs : drv_grf_offs;
+			}
+
+			dev_dbg(d->dev, "bank %d, iomux %d has iom_offset 0x%x drv_offset 0x%x\n",
+				i, j, iom->offset, drv->offset);
 
 			/*
 			 * Increase offset according to iomux width.
@@ -1925,6 +2196,21 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
 			else
 				grf_offs += inc;
 
+			/*
+			 * Increase offset according to drv width.
+			 * 3bit drive-strenth'es are spread over two registers.
+			 */
+			if ((drv->drv_type == DRV_TYPE_IO_1V8_3V0_AUTO) ||
+			    (drv->drv_type == DRV_TYPE_IO_3V3_ONLY))
+				inc = 8;
+			else
+				inc = 4;
+
+			if (iom->type & IOMUX_SOURCE_PMU)
+				drv_pmu_offs += inc;
+			else
+				drv_grf_offs += inc;
+
 			bank_pins += 8;
 		}
 	}
@@ -2208,6 +2494,62 @@ static struct rockchip_pin_ctrl rk3368_pin_ctrl = {
 		.drv_calc_reg		= rk3368_calc_drv_reg_and_bit,
 };
 
+static struct rockchip_pin_bank rk3399_pin_banks[] = {
+	PIN_BANK_IOMUX_DRV_FLAGS_OFFSET(0, 32, "gpio0", IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					DRV_TYPE_IO_1V8_ONLY,
+					DRV_TYPE_IO_1V8_ONLY,
+					DRV_TYPE_IO_DEFAULT,
+					DRV_TYPE_IO_DEFAULT,
+					0x0,
+					0x8,
+					-1,
+					-1
+					),
+	PIN_BANK_IOMUX_DRV_FLAGS_OFFSET(1, 32, "gpio1", IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					DRV_TYPE_IO_1V8_OR_3V0,
+					DRV_TYPE_IO_1V8_OR_3V0,
+					DRV_TYPE_IO_1V8_OR_3V0,
+					DRV_TYPE_IO_1V8_OR_3V0,
+					0x20,
+					0x28,
+					0x30,
+					0x38
+					),
+	PIN_BANK_DRV_FLAGS(2, 32, "gpio2", DRV_TYPE_IO_1V8_OR_3V0,
+			   DRV_TYPE_IO_1V8_OR_3V0,
+			   DRV_TYPE_IO_1V8_ONLY,
+			   DRV_TYPE_IO_1V8_ONLY
+			   ),
+	PIN_BANK_DRV_FLAGS(3, 32, "gpio3", DRV_TYPE_IO_3V3_ONLY,
+			   DRV_TYPE_IO_3V3_ONLY,
+			   DRV_TYPE_IO_3V3_ONLY,
+			   DRV_TYPE_IO_1V8_OR_3V0
+			   ),
+	PIN_BANK_DRV_FLAGS(4, 32, "gpio4", DRV_TYPE_IO_1V8_OR_3V0,
+			   DRV_TYPE_IO_1V8_3V0_AUTO,
+			   DRV_TYPE_IO_1V8_OR_3V0,
+			   DRV_TYPE_IO_1V8_OR_3V0
+			   ),
+};
+
+static struct rockchip_pin_ctrl rk3399_pin_ctrl = {
+		.pin_banks		= rk3399_pin_banks,
+		.nr_banks		= ARRAY_SIZE(rk3399_pin_banks),
+		.label			= "RK3399-GPIO",
+		.type			= RK3399,
+		.grf_mux_offset		= 0xe000,
+		.pmu_mux_offset		= 0x0,
+		.grf_drv_offset		= 0xe100,
+		.pmu_drv_offset		= 0x80,
+		.pull_calc_reg		= rk3399_calc_pull_reg_and_bit,
+		.drv_calc_reg		= rk3399_calc_drv_reg_and_bit,
+};
 
 static const struct of_device_id rockchip_pinctrl_dt_match[] = {
 	{ .compatible = "rockchip,rk2928-pinctrl",
@@ -2224,6 +2566,8 @@ static const struct of_device_id rockchip_pinctrl_dt_match[] = {
 		.data = (void *)&rk3288_pin_ctrl },
 	{ .compatible = "rockchip,rk3368-pinctrl",
 		.data = (void *)&rk3368_pin_ctrl },
+	{ .compatible = "rockchip,rk3399-pinctrl",
+		.data = (void *)&rk3399_pin_ctrl },
 	{},
 };
 MODULE_DEVICE_TABLE(of, rockchip_pinctrl_dt_match);
-- 
1.9.1




From heiko at sntech.de  Sat Jan 30 05:48:15 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sat, 30 Jan 2016 14:48:15 +0100
Subject: [PATCH v4 1/2] pinctrl: rockchip: add support for the rk3399
In-Reply-To: <1454156413-56148-2-git-send-email-david.wu@rock-chips.com>
References: <1454156413-56148-1-git-send-email-david.wu@rock-chips.com>
 <1454156413-56148-2-git-send-email-david.wu@rock-chips.com>
Message-ID: <2864405.Bm4XacGINh@phil>

Hi David,

Am Samstag, 30. Januar 2016, 20:20:12 schrieb David Wu:
> The pinctrl of rk3399 is much different from other's,
> especially the 3bits of drive strength.
> 
> Signed-off-by: David Wu <david.wu at rock-chips.com>
> ---
> Change in v4: None

you're to fast for me ;-)

[...]

> @@ -729,8 +924,67 @@ static int rockchip_set_drive_perpin(struct
> rockchip_pin_bank *bank,
> 
>  	spin_lock_irqsave(&bank->slock, flags);
> 
> +	switch (drv_type) {
> +	case DRV_TYPE_IO_1V8_3V0_AUTO:
> +	case DRV_TYPE_IO_3V3_ONLY:
> +		rmask_bits = RK3399_DRV_3BITS_PER_PIN;
> +		switch (bit) {
> +		case 0 ... 12:
> +			/* regular case, nothing to do */
> +			break;
> +		case 15:
> +			/*
> +			 * the bit data[15] contains bit 0 of the value
> +			 * while temp[1:0] contains bits 2 and 1
> +			 */

I'd think the introductory comment should mode here, so like:
			/*
			 * drive-strength offset is special, as it is
			 * spread over 2 registers, the bit data[15] contains bit 0
			 * of the value while temp[1:0] contains bits 2 and 1
			 */


> +			data = (ret & 0x1) << 15;
> +			temp = (ret >> 0x1) & 0x3;
> +
> +			rmask = BIT(15) | BIT(31);
> +			data |= BIT(31);
> +			ret = regmap_update_bits(regmap, reg, rmask, data);
> +			if (ret) {
> +				spin_unlock_irqrestore(&bank->slock, flags);
> +				return ret;
> +			}
> +
> +			/*
> +			 * drive-strength offset is special, as it is
> +			 * spread over 2 registers
> +			 */

as I wrote, this should probably move a bit higher


> +			rmask = 0x3 | (0x3 << 16);
> +			temp |= (0x3 << 16);
> +			reg += 0x4;
> +			ret = regmap_update_bits(regmap, reg, rmask, temp);
> +
> +			spin_unlock_irqrestore(&bank->slock, flags);
> +			return ret;
> +		case 18 ... 21:
> +			/* setting fully enclosed in the second register */
> +			reg += 4;
> +			bit -= 16;
> +			break;
> +		default:
> +			spin_unlock_irqrestore(&bank->slock, flags);
> +			dev_err(info->dev, "unsupported bit: %d for pinctrl drive type: 
%d\n",
> +				bit, drv_type);
> +			return -EINVAL;
> +		}
> +		break;
> +	case DRV_TYPE_IO_DEFAULT:
> +	case DRV_TYPE_IO_1V8_OR_3V0:
> +	case DRV_TYPE_IO_1V8_ONLY:
> +		rmask_bits = RK3288_DRV_BITS_PER_PIN;
> +		break;
> +	default:
> +		spin_unlock_irqrestore(&bank->slock, flags);
> +		dev_err(info->dev, "unsupported pinctrl drive type: %d\n",
> +			drv_type);
> +		return -EINVAL;
> +	}
> +
>  	/* enable the write to the equivalent lower bits */
> -	data = ((1 << RK3288_DRV_BITS_PER_PIN) - 1) << (bit + 16);
> +	data = ((1 << rmask_bits) - 1) << (bit + 16);
>  	rmask = data | (data >> 16);
>  	data |= (ret << bit);
> 

Otherwise this looks nice to me, so with the comment fixed
Reviewed-by: Heiko Stuebner <heiko at sntech.de>


Thanks
Heiko


From heiko at sntech.de  Sat Jan 30 04:16:13 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sat, 30 Jan 2016 13:16:13 +0100
Subject: [PATCH v3 2/2] dt-bindings: rockchip-pinctrl: Support the RK3399
 SoCs compatible
In-Reply-To: <1454153517-52969-3-git-send-email-david.wu@rock-chips.com>
References: <1451997055-47655-1-git-send-email-wdc@rock-chips.com>
 <1454153517-52969-1-git-send-email-david.wu@rock-chips.com>
 <1454153517-52969-3-git-send-email-david.wu@rock-chips.com>
Message-ID: <14543443.RsRWh5K5Uv@phil>

Hi David,

Am Samstag, 30. Januar 2016, 19:31:57 schrieb David Wu:
> Change-Id: Ic1bdb3dc358837e97d7b48dd58be2f8d9d08d766

please no Change-Id, it's missing the Signed-off, and as Linus Walleij said, 
the compatible addition to the documentation can maybe stay in the pinctrl-
patch itself.

I think both Linus and me overlooked that the binding-doc-addition was 
already correctly included in your v2 patch at the top :-)


Heiko

> ---
>  Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git
> a/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
> b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt index
> 391ef4b..3bb9456 100644
> --- a/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
> +++ b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
> @@ -22,6 +22,7 @@ Required properties for iomux controller:
>    - compatible: one of "rockchip,rk2928-pinctrl",
> "rockchip,rk3066a-pinctrl" "rockchip,rk3066b-pinctrl",
> "rockchip,rk3188-pinctrl"
>  		       "rockchip,rk3288-pinctrl", "rockchip,rk3368-pinctrl"
> +		       "rockchip,rk3399-pinctrl"
>    - rockchip,grf: phandle referencing a syscon providing the
>  	 "general register files"



From heiko at sntech.de  Sat Jan 30 04:39:11 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sat, 30 Jan 2016 13:39:11 +0100
Subject: [PATCH] PM / AVS: rockchip-io: add GRF and PMUGRF types to
 distinguish
In-Reply-To: <1454155305-55516-1-git-send-email-david.wu@rock-chips.com>
References: <1454155305-55516-1-git-send-email-david.wu@rock-chips.com>
Message-ID: <5387650.lAgTxexlF2@phil>

Hi David,

Am Samstag, 30. Januar 2016, 20:01:45 schrieb David Wu:
> As rk3368 contained two separated iodomain areas, this was
> determined to use which regmap base address.
> 
> Signed-off-by: David Wu <david.wu at rock-chips.com>

I don't think we need to specify this on a driver level. Both GRF areas are 
"General register files" only located in two separate power-domains.
So the rockchip,grf property should work for both. Especially as nothing 
keeps designers from introducing yet another GRF-area somewhere else ;-)

>From when I started working on the rk3368, I still have a preliminary 
patches for that sitting here, so I've attached on how I envisoned that to 
work.


Heiko

> ---
>  drivers/power/avs/rockchip-io-domain.c | 32
> ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8
> deletions(-)
> 
> diff --git a/drivers/power/avs/rockchip-io-domain.c
> b/drivers/power/avs/rockchip-io-domain.c index 8099456..b17aeb7 100644
> --- a/drivers/power/avs/rockchip-io-domain.c
> +++ b/drivers/power/avs/rockchip-io-domain.c
> @@ -47,6 +47,11 @@
>  #define RK3368_SOC_CON15_FLASH0		BIT(14)
>  #define RK3368_SOC_FLASH_SUPPLY_NUM	2
> 
> +enum rockchip_iodomain_grf_type {
> +	GRF,
> +	PMUGRF
> +};
> +
>  struct rockchip_iodomain;
> 
>  /**
> @@ -54,6 +59,7 @@ struct rockchip_iodomain;
>   */
>  struct rockchip_iodomain_soc_data {
>  	int grf_offset;
> +	enum rockchip_iodomain_grf_type type;
>  	const char *supply_names[MAX_SUPPLIES];
>  	void (*init)(struct rockchip_iodomain *iod);
>  };
> @@ -67,7 +73,7 @@ struct rockchip_iodomain_supply {
> 
>  struct rockchip_iodomain {
>  	struct device *dev;
> -	struct regmap *grf;
> +	struct regmap *base;
>  	struct rockchip_iodomain_soc_data *soc_data;
>  	struct rockchip_iodomain_supply supplies[MAX_SUPPLIES];
>  };
> @@ -86,7 +92,7 @@ static int rockchip_iodomain_write(struct
> rockchip_iodomain_supply *supply, /* apply hiword-mask */
>  	val |= (BIT(supply->idx) << 16);
> 
> -	ret = regmap_write(iod->grf, iod->soc_data->grf_offset, val);
> +	ret = regmap_write(iod->base, iod->soc_data->grf_offset, val);
>  	if (ret)
>  		dev_err(iod->dev, "Couldn't write to GRF\n");
> 
> @@ -157,7 +163,7 @@ static void rk3288_iodomain_init(struct
> rockchip_iodomain *iod) * instead of a special gpio.
>  	 */
>  	val = RK3288_SOC_CON2_FLASH0 | (RK3288_SOC_CON2_FLASH0 << 16);
> -	ret = regmap_write(iod->grf, RK3288_SOC_CON2, val);
> +	ret = regmap_write(iod->base, RK3288_SOC_CON2, val);
>  	if (ret < 0)
>  		dev_warn(iod->dev, "couldn't update flash0 ctrl\n");
>  }
> @@ -176,7 +182,7 @@ static void rk3368_iodomain_init(struct
> rockchip_iodomain *iod) * instead of a special gpio.
>  	 */
>  	val = RK3368_SOC_CON15_FLASH0 | (RK3368_SOC_CON15_FLASH0 << 16);
> -	ret = regmap_write(iod->grf, RK3368_SOC_CON15, val);
> +	ret = regmap_write(iod->base, RK3368_SOC_CON15, val);
>  	if (ret < 0)
>  		dev_warn(iod->dev, "couldn't update flash0 ctrl\n");
>  }
> @@ -187,6 +193,7 @@ static void rk3368_iodomain_init(struct
> rockchip_iodomain *iod) */
>  static const struct rockchip_iodomain_soc_data soc_data_rk3188 = {
>  	.grf_offset = 0x104,
> +	.type = GRF,
>  	.supply_names = {
>  		NULL,
>  		NULL,
> @@ -209,6 +216,7 @@ static const struct rockchip_iodomain_soc_data
> soc_data_rk3188 = {
> 
>  static const struct rockchip_iodomain_soc_data soc_data_rk3288 = {
>  	.grf_offset = 0x380,
> +	.type = GRF,
>  	.supply_names = {
>  		"lcdc",		/* LCDC_VDD */
>  		"dvp",		/* DVPIO_VDD */
> @@ -226,6 +234,7 @@ static const struct rockchip_iodomain_soc_data
> soc_data_rk3288 = {
> 
>  static const struct rockchip_iodomain_soc_data soc_data_rk3368 = {
>  	.grf_offset = 0x900,
> +	.type = GRF,
>  	.supply_names = {
>  		NULL,		/* reserved */
>  		"dvp",		/* DVPIO_VDD */
> @@ -242,6 +251,7 @@ static const struct rockchip_iodomain_soc_data
> soc_data_rk3368 = {
> 
>  static const struct rockchip_iodomain_soc_data soc_data_rk3368_pmu = {
>  	.grf_offset = 0x100,
> +	.type = PMUGRF,
>  	.supply_names = {
>  		NULL,
>  		NULL,
> @@ -293,10 +303,16 @@ static int rockchip_iodomain_probe(struct
> platform_device *pdev) match = of_match_node(rockchip_iodomain_match,
> np);
>  	iod->soc_data = (struct rockchip_iodomain_soc_data *)match->data;
> 
> -	iod->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
> -	if (IS_ERR(iod->grf)) {
> -		dev_err(&pdev->dev, "couldn't find grf regmap\n");
> -		return PTR_ERR(iod->grf);
> +	if (iod->soc_data->type == PMUGRF)
> +		iod->base = syscon_regmap_lookup_by_phandle(
> +				np, "rockchip,pmugrf");
> +	else
> +		iod->base = syscon_regmap_lookup_by_phandle(
> +				np, "rockchip,grf");
> +	if (IS_ERR(iod->base)) {
> +		dev_err(&pdev->dev, "couldn't find %s regmap\n",
> +			(iod->soc_data->type == PMUGRF) ? "pmugrf" : "grf");
> +		return PTR_ERR(iod->base);
>  	}
> 
>  	for (i = 0; i < MAX_SUPPLIES; i++) {
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-PM-AVS-rockchip-io-add-io-selectors-and-supplies-for.patch
Type: text/x-patch
Size: 4768 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160130/a4868cb1/attachment.bin>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0002-ARM64-dts-rockchip-add-iodomains-and-stuff.patch
Type: text/x-patch
Size: 1036 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160130/a4868cb1/attachment-0001.bin>

From caesar.upstream at gmail.com  Sun Jan 31 01:09:51 2016
From: caesar.upstream at gmail.com (Caesar Wang)
Date: Sun, 31 Jan 2016 17:09:51 +0800
Subject: [PATCH v4 6/9] ASoC: rt5616: add mclk property for rt5616 document
In-Reply-To: <20160129162800.GA10835@rob-hp-laptop>
References: <1453970618-4383-1-git-send-email-wxt@rock-chips.com>
 <1453970618-4383-7-git-send-email-wxt@rock-chips.com>
 <20160129162800.GA10835@rob-hp-laptop>
Message-ID: <56ADCF5F.1010203@gmail.com>


? 2016?01?30? 00:28, Rob Herring ??:
> On Thu, Jan 28, 2016 at 04:43:35PM +0800, Caesar Wang wrote:
>> This patch add the mclk property for the CODEC driver,
>> since sometime the CODEC driver needs the clock enabled.
> s/add/adds/
>
> s/sometime/sometimes/

Done, thanks for pointing out that.


>> The system clock of ALC5616 can be selected from MCLK,
>> That's also make as the codec master clock provider,
> That also makes the codec the master clock provider.
>

Ditto.

>> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
>>
>> ---
>>
>> Changes in v4:
>> - Add this patch included in kylin series patches.
>>
>>   Documentation/devicetree/bindings/sound/rt5616.txt | 6 ++++++
>>   1 file changed, 6 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/sound/rt5616.txt b/Documentation/devicetree/bindings/sound/rt5616.txt
>> index efc48c6..e410858 100644
>> --- a/Documentation/devicetree/bindings/sound/rt5616.txt
>> +++ b/Documentation/devicetree/bindings/sound/rt5616.txt
>> @@ -8,6 +8,12 @@ Required properties:
>>   
>>   - reg : The I2C address of the device.
>>   
>> +Optional properties:
>> +
>> +- clocks: The phandle of the master clock to the CODEC.
>> +
>> +- clock-names: Should be "mclk".
>> +
>>   Pins on the device (for linking into audio routes) for RT5616:
>>   
>>     * IN1P
>> -- 
>> 1.9.1
>>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip


-- 
Thanks,
Caesar



From wxt at rock-chips.com  Sun Jan 31 01:10:43 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Sun, 31 Jan 2016 17:10:43 +0800
Subject: [PATCH v4.1 6/9] ASoC: rt5616: add mclk property for rt5616 document
In-Reply-To: <1453970618-4383-1-git-send-email-wxt@rock-chips.com>
References: <1453970618-4383-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1454231443-22354-1-git-send-email-wxt@rock-chips.com>

This patch adds the mclk property for the CODEC driver,
since sometimes the CODEC driver needs the clock enabled.

The system clock of ALC5616 can be selected from MCLK,
That also makes the codec the master clock provider

Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v4 -> v4.1:
- Fix the wrong word, as the Rob comments on
  https://patchwork.kernel.org/patch/8147731/.

Changes in v4:
- Add this patch included in kylin series patches.

 Documentation/devicetree/bindings/sound/rt5616.txt | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Documentation/devicetree/bindings/sound/rt5616.txt b/Documentation/devicetree/bindings/sound/rt5616.txt
index efc48c6..e410858 100644
--- a/Documentation/devicetree/bindings/sound/rt5616.txt
+++ b/Documentation/devicetree/bindings/sound/rt5616.txt
@@ -8,6 +8,12 @@ Required properties:
 
 - reg : The I2C address of the device.
 
+Optional properties:
+
+- clocks: The phandle of the master clock to the CODEC.
+
+- clock-names: Should be "mclk".
+
 Pins on the device (for linking into audio routes) for RT5616:
 
   * IN1P
-- 
1.9.1



From kever.yang at rock-chips.com  Sun Jan 31 01:23:59 2016
From: kever.yang at rock-chips.com (Kever Yang)
Date: Sun, 31 Jan 2016 17:23:59 +0800
Subject: [PATCH v6 10/22] usb: dwc2: host: Properly set the HFIR
In-Reply-To: <1454034013-24657-11-git-send-email-dianders@chromium.org>
References: <1454034013-24657-1-git-send-email-dianders@chromium.org>
 <1454034013-24657-11-git-send-email-dianders@chromium.org>
Message-ID: <56ADD2AF.3040901@rock-chips.com>

Doug,

On 01/29/2016 10:20 AM, Douglas Anderson wrote:
> According to the most up to date version of the dwc2 databook, the FRINT
> field of the HFIR register should be programmed to:
> * 125 us * (PHY clock freq for HS) - 1
> * 1000 us * (PHY clock freq for FS/LS) - 1
I got 3 version of dwc_otg databook, 2.74a, 2.94a and 3.10a,
all the doc describe the FrInt as:

* 125 us * (PHY clock freq for HS)
* 1000 us * (PHY clock freq for FS/LS)

Maybe John can help to check the design.

There are some feature different in new and old version, but not sure
if this is one of then.

The doc says If no value is programmed, the corecalculates the value
based on the PHY clock specified in the FS/LS PHY Clock select field of
Host configuration register(HCFG.FLSLPclkSel), does this work?

Thanks,
- Kever
>
> This is opposed to older versions of the doc that claimed it should be:
> * 125 us * (PHY clock freq for HS)
> * 1000 us * (PHY clock freq for FS/LS)
>
> In case you didn't spot it, the difference is the "- 1".
>
> Let's add the "- 1" to match the newest user manual.  It's presumed that
> the "- 1" should have always been there and that this was always a
> documentation error.  If some hardware needs the "- 1" and other
> hardware doesn't, we'll have to add a configuration parameter for it in
> the future.
>
> I checked things before and after this patch on rk3288 using a Total
> Phase Beagle 5000 analyzer.
>
> Before this patch, a low speed mouse shows constant Frame Timing Jitter
> errors.  After this patch errors have gone away.
>
> Before this patch SOF packets move forward about 1 us per 4 ms.  After
> this patch the SOF packets move backward about 1 us per 255 ms.  Some
> specific SOF timestamps from the analyzer are below.
>
> Before:
>    6.603.790
>    6.603.916
>    6.604.041
>    6.604.166
>    ...
>    6.607.541
>    6.607.667
>    6.607.792
>    6.607.917
>    ...
>    6.611.417
>    6.611.543
>    6.611.668
>    6.611.793
>
> After:
>    6.215.159
>    6.215.284
>    6.215.408
>    6.215.533
>    6.215.658
>    ...
>    6.470.658
>    6.470.783
>    6.470.907
>    ...
>    6.726.032
>    6.726.157
>    6.725.281
>    6.725.406
>
> Signed-off-by: Douglas Anderson <dianders at chromium.org>
> Tested-by: Heiko Stuebner <heiko at sntech.de>
> ---
> Changes in v6:
> - Incorporated Properly set the HFIR patch to big series in v6
> - Add Heiko's Tested-by.
>
> Changes in v5: None
> Changes in v4: None
> Changes in v3: None
> Changes in v2: None
>
>   drivers/usb/dwc2/core.c | 4 ++--
>   1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
> index ed73b26818c0..a5db20f12ee4 100644
> --- a/drivers/usb/dwc2/core.c
> +++ b/drivers/usb/dwc2/core.c
> @@ -2245,10 +2245,10 @@ u32 dwc2_calc_frame_interval(struct dwc2_hsotg *hsotg)
>   
>   	if ((hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT == HPRT0_SPD_HIGH_SPEED)
>   		/* High speed case */
> -		return 125 * clock;
> +		return 125 * clock - 1;
>   	else
>   		/* FS/LS case */
> -		return 1000 * clock;
> +		return 1000 * clock - 1;
>   }
>   
>   /**




From kever.yang at rock-chips.com  Sun Jan 31 01:25:03 2016
From: kever.yang at rock-chips.com (Kever Yang)
Date: Sun, 31 Jan 2016 17:25:03 +0800
Subject: [PATCH v6 11/22] usb: dwc2: host: There's not really a TT for
 the root hub
In-Reply-To: <1454034013-24657-12-git-send-email-dianders@chromium.org>
References: <1454034013-24657-1-git-send-email-dianders@chromium.org>
 <1454034013-24657-12-git-send-email-dianders@chromium.org>
Message-ID: <56ADD2EF.9040504@rock-chips.com>

Doug,

Reviewed-by: Kever Yang <kever.yang at rock-chips.com>

Thanks,
- Kever
On 01/29/2016 10:20 AM, Douglas Anderson wrote:
> I find that when I plug a full speed (NOT high speed) hub into a dwc2
> port and then I plug a bunch of devices into that full speed hub that
> dwc2 goes bat guano crazy.  Specifically, it just spews errors like this
> in the console:
>    usb usb1: clear tt 1 (9043) error -22
>
> The specific test case I used looks like this:
> /:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=dwc2/1p, 480M
>      |__ Port 1: Dev 17, If 0, Class=Hub, Driver=hub/4p, 12M
>          |__ Port 2: Dev 19, If 0, ..., Driver=usbhid, 1.5M
>          |__ Port 4: Dev 20, If 0, ..., Driver=usbhid, 12M
>          |__ Port 4: Dev 20, If 1, ..., Driver=usbhid, 12M
>          |__ Port 4: Dev 20, If 2, ..., Driver=usbhid, 12M
>
> Showing VID/PID:
>   Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
>   Bus 001 Device 017: ID 03eb:3301 Atmel Corp. at43301 4-Port Hub
>   Bus 001 Device 020: ID 045e:0745 Microsoft Corp. Nano Transceiver ...
>   Bus 001 Device 019: ID 046d:c404 Logitech, Inc. TrackMan Wheel
>
> I spent a bunch of time trying to figure out why there are errors to
> begin with.  I believe that the issue may be a hardware issue where the
> transceiver sometimes accidentally sends a PREAMBLE packet if you send a
> packet to a full speed device right after one to a low speed device.
> Luckily the USB driver retries and the second time things work OK.
>
> In any case, things kinda seem work despite the errors, except for the
> "clear tt" spew mucking up my console.  Chalk it up for a win for
> retries and robust protocols.
>
> So getting back to the "clear tt" problem, it appears that we get those
> because there's not actually a TT here to clear.  It's my understanding
> that when dwc2 operates in low speed or full speed mode that there's no
> real TT out there.  That makes all these attempts to "clear the TT"
> somewhat meaningless and also causes the spew in the log.
>
> Let's just skip all the useless TT clears.  Eventually we should root
> cause the errors, but even if we do this is still a proper fix and is
> likely to avoid the "clear tt" error in the future.
>
> Note that hooking up a Full Speed USB Audio Device (Jabra 510) to this
> same hub with the keyboard / trackball shows that even audio works over
> this janky connection.  As a point to note, this particular change (skip
> bogus TT clears) compared to just commenting out the dev_err() in
> hub_tt_work() actually produces better audio.
>
> Note: don't ask me where I got a full speed USB hub or whether the
> massive amount of dust that accumulated on it while it was in my junk
> box affected its funtionality.  Just smile and nod.
>
> Signed-off-by: Douglas Anderson <dianders at chromium.org>
> ---
> Changes in v6:
> - There's not really a TT for the root hub new for v6
>
> Changes in v5: None
> Changes in v4: None
> Changes in v3: None
> Changes in v2: None
>
>   drivers/usb/dwc2/hcd_intr.c | 10 ++++++++++
>   1 file changed, 10 insertions(+)
>
> diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
> index 5d25a5ec9736..fe44870f84eb 100644
> --- a/drivers/usb/dwc2/hcd_intr.c
> +++ b/drivers/usb/dwc2/hcd_intr.c
> @@ -87,6 +87,7 @@ static void dwc2_hc_handle_tt_clear(struct dwc2_hsotg *hsotg,
>   				    struct dwc2_host_chan *chan,
>   				    struct dwc2_qtd *qtd)
>   {
> +	struct usb_device *root_hub = dwc2_hsotg_to_hcd(hsotg)->self.root_hub;
>   	struct urb *usb_urb;
>   
>   	if (!chan->qh)
> @@ -102,6 +103,15 @@ static void dwc2_hc_handle_tt_clear(struct dwc2_hsotg *hsotg,
>   	if (!usb_urb || !usb_urb->dev || !usb_urb->dev->tt)
>   		return;
>   
> +	/*
> +	 * The root hub doesn't really have a TT, but Linux thinks it
> +	 * does because how could you have a "high speed hub" that
> +	 * directly talks directly to low speed devices without a TT?
> +	 * It's all lies.  Lies, I tell you.
> +	 */
> +	if (usb_urb->dev->tt->hub == root_hub)
> +		return;
> +
>   	if (qtd->urb->status != -EPIPE && qtd->urb->status != -EREMOTEIO) {
>   		chan->qh->tt_buffer_dirty = 1;
>   		if (usb_hub_clear_tt_buffer(usb_urb))




From kever.yang at rock-chips.com  Sun Jan 31 01:36:00 2016
From: kever.yang at rock-chips.com (Kever Yang)
Date: Sun, 31 Jan 2016 17:36:00 +0800
Subject: [PATCH v6 18/22] usb: dwc2: host: Schedule periodic right away
 if it's time
In-Reply-To: <1454034013-24657-19-git-send-email-dianders@chromium.org>
References: <1454034013-24657-1-git-send-email-dianders@chromium.org>
 <1454034013-24657-19-git-send-email-dianders@chromium.org>
Message-ID: <56ADD580.1070505@rock-chips.com>

Doug,

On 01/29/2016 10:20 AM, Douglas Anderson wrote:
> In dwc2_hcd_qh_deactivate() we will put some things on the
> periodic_sched_ready list.  These things won't be taken off the ready
> list until the next SOF, which might be a little late.  Let's put them
> on right away.
>
> Signed-off-by: Douglas Anderson <dianders at chromium.org>
> Tested-by: Heiko Stuebner <heiko at sntech.de>
> Tested-by: Stefan Wahren <stefan.wahren at i2se.com>
> ---
> Changes in v6:
> - Add Heiko's Tested-by.
> - Add Stefan's Tested-by.
>
> Changes in v5: None
> Changes in v4:
> - Schedule periodic right away if it's time new for v4.
>
> Changes in v3: None
> Changes in v2: None
>
>   drivers/usb/dwc2/hcd_queue.c | 18 ++++++++++++++++--
>   1 file changed, 16 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
> index 9b3c435339ee..3abb34a5fc5b 100644
> --- a/drivers/usb/dwc2/hcd_queue.c
> +++ b/drivers/usb/dwc2/hcd_queue.c
> @@ -1080,12 +1080,26 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
>   	 * Note: we purposely use the frame_number from the "hsotg" structure
>   	 * since we know SOF interrupt will handle future frames.
>   	 */
> -	if (dwc2_frame_num_le(qh->next_active_frame, hsotg->frame_number))
> +	if (dwc2_frame_num_le(qh->next_active_frame, hsotg->frame_number)) {
> +		enum dwc2_transaction_type tr_type;
> +
> +		/*
> +		 * We're bypassing the SOF handler which is normally what puts
> +		 * us on the ready list because we're in a hurry and need to
> +		 * try to catch up.
> +		 */
> +		dwc2_sch_vdbg(hsotg, "QH=%p IMM ready fn=%04x, nxt=%04x\n",
> +			      qh, frame_number, qh->next_active_frame);
>   		list_move_tail(&qh->qh_list_entry,
>   			       &hsotg->periodic_sched_ready);
> -	else
> +
> +		tr_type = dwc2_hcd_select_transactions(hsotg);
Do we need to add select_transactions call here? If we get into this 
function in interrupt
and once we put the qh in ready queue, the qh can be handled in this 
frame again by the
later function call of dwc_hcd_select_transactions, so what we need to 
to here is put
it in ready list instead of inactive queue, and wait for the schedule.

Thanks,
- Kever

> +		if (tr_type != DWC2_TRANSACTION_NONE)
> +			dwc2_hcd_queue_transactions(hsotg, tr_type);
> +	} else {
>   		list_move_tail(&qh->qh_list_entry,
>   			       &hsotg->periodic_sched_inactive);
> +	}
>   }
>   
>   /**




From heiko at sntech.de  Sun Jan 31 02:46:47 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sun, 31 Jan 2016 11:46:47 +0100
Subject: [PATCH v4 1/9] ARM: dts: rockchip: add the leds control for kylin
 board
In-Reply-To: <1453970618-4383-2-git-send-email-wxt@rock-chips.com>
References: <1453970618-4383-1-git-send-email-wxt@rock-chips.com>
 <1453970618-4383-2-git-send-email-wxt@rock-chips.com>
Message-ID: <2785565.JmZWWova7F@phil>

Am Donnerstag, 28. Januar 2016, 16:43:30 schrieb Caesar Wang:
> As the kylin schematic drawing, add the needed work led for
> kylin board.
> 
> Run:
> echo 0 > /sys/class/leds/kylin:red:led/brightness
> echo 1 > /sys/class/leds/kylin:red:led/brightness
> 
> The led can normal on/off on kylin board.
> 
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>

applied to my dts32-branch for 4.6


From heiko at sntech.de  Sun Jan 31 03:06:25 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sun, 31 Jan 2016 12:06:25 +0100
Subject: [PATCH v4 9/9] ARM: dts: rockchip: support the spi for rk3036
In-Reply-To: <1453970618-4383-10-git-send-email-wxt@rock-chips.com>
References: <1453970618-4383-1-git-send-email-wxt@rock-chips.com>
 <1453970618-4383-10-git-send-email-wxt@rock-chips.com>
Message-ID: <3295129.SttO6ack9z@phil>

Hi Caesar,

Am Donnerstag, 28. Januar 2016, 16:43:38 schrieb Caesar Wang:
> You have to use the 4 bus to work if someone wants to support
> the spi devices, since the the pin is re-used by data[5-8] and spi.
> If support the spi making the happy work, that will waste the
> emmc performance.
> 
> Moment, the kylin hasn't the spi devices to work, so maybe we need wait
> the new required to enable in kylin board.
> 
> Anyway, the spi should be needed land in rk3036 dts.
> 
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
> 
> ---
> 
> Changes in v4:
> - Add this patch included in kylin series patches.
> 
>  arch/arm/boot/dts/rk3036.dtsi | 42
> ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42
> insertions(+)
> 
> diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
> index 532f232..40a5017 100644
> --- a/arch/arm/boot/dts/rk3036.dtsi
> +++ b/arch/arm/boot/dts/rk3036.dtsi
> @@ -60,6 +60,7 @@
>  		serial0 = &uart0;
>  		serial1 = &uart1;
>  		serial2 = &uart2;
> +		spi = &spi;
>  	};
> 
>  	memory {
> @@ -485,6 +486,23 @@
>  		status = "disabled";
>  	};
> 
> +	spi: spi at 20074000 {
> +		compatible = "rockchip,rockchip-spi";
> +		reg = <0x20074000 0x1000>;
> +		interrupts = <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>;
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&spi_txd &spi_rxd &spi_clk &spi_cs0 &spi_cs1>;

Do we really want to enable both chip-selects by default?

On the rk3288 Lin Huang wrote:
    * It's assumed that most users of the SPI ports are using chip select
      0.  Thus the default pinctrl for the ports enables chip select 0
      (but not chip select 1 on ports that have it).  If a board wants to
      use chip select 1 or wants a GPIO chip select the board should
      override the pinctrl (just like boards can override UART pinctrl if
      they have hardware flow control).

Do we expect again mostly a use of cs0 or will in the major cases both chip-
selects be needed?


> +		num-cs = <2>;
> +		clocks =<&cru PCLK_SPI>, <&cru SCLK_SPI>;
> +		clock-names = "apb-pclk","spi_pclk";
> +		dmas = <&pdma 8>, <&pdma 9>;
> +		#dma-cells = <2>;

What do you need #dma-cells for? This is not a dma-controller :-)


> +		dma-names = "tx", "rx";
> +		status = "disabled";
> +	};


Also I'd suggest an ordering like:

+	spi: spi at 20074000 {
+		compatible = "rockchip,rockchip-spi";
+		reg = <0x20074000 0x1000>;
+		interrupts = <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>;
+		clocks =<&cru PCLK_SPI>, <&cru SCLK_SPI>;
+		clock-names = "apb-pclk","spi_pclk";
+		dmas = <&pdma 8>, <&pdma 9>;
+		dma-names = "tx", "rx";
+		num-cs = <2>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&spi_txd &spi_rxd &spi_clk &spi_cs0 &spi_cs1>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		status = "disabled";
+	};


Heiko


From dianders at chromium.org  Sun Jan 31 14:09:47 2016
From: dianders at chromium.org (Doug Anderson)
Date: Sun, 31 Jan 2016 14:09:47 -0800
Subject: [PATCH v6 18/22] usb: dwc2: host: Schedule periodic right away if
 it's time
In-Reply-To: <56ADD580.1070505@rock-chips.com>
References: <1454034013-24657-1-git-send-email-dianders@chromium.org>
 <1454034013-24657-19-git-send-email-dianders@chromium.org>
 <56ADD580.1070505@rock-chips.com>
Message-ID: <CAD=FV=XUjAbSQhr9LtPG=5ycPbv6UBsA0rvfrPd25EndnjxRrw@mail.gmail.com>

Kever,

On Sun, Jan 31, 2016 at 1:36 AM, Kever Yang <kever.yang at rock-chips.com> wrote:
> Doug,
>
>
> On 01/29/2016 10:20 AM, Douglas Anderson wrote:
>>
>> In dwc2_hcd_qh_deactivate() we will put some things on the
>> periodic_sched_ready list.  These things won't be taken off the ready
>> list until the next SOF, which might be a little late.  Let's put them
>> on right away.
>>
>> Signed-off-by: Douglas Anderson <dianders at chromium.org>
>> Tested-by: Heiko Stuebner <heiko at sntech.de>
>> Tested-by: Stefan Wahren <stefan.wahren at i2se.com>
>> ---
>> Changes in v6:
>> - Add Heiko's Tested-by.
>> - Add Stefan's Tested-by.
>>
>> Changes in v5: None
>> Changes in v4:
>> - Schedule periodic right away if it's time new for v4.
>>
>> Changes in v3: None
>> Changes in v2: None
>>
>>   drivers/usb/dwc2/hcd_queue.c | 18 ++++++++++++++++--
>>   1 file changed, 16 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
>> index 9b3c435339ee..3abb34a5fc5b 100644
>> --- a/drivers/usb/dwc2/hcd_queue.c
>> +++ b/drivers/usb/dwc2/hcd_queue.c
>> @@ -1080,12 +1080,26 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg
>> *hsotg, struct dwc2_qh *qh,
>>          * Note: we purposely use the frame_number from the "hsotg"
>> structure
>>          * since we know SOF interrupt will handle future frames.
>>          */
>> -       if (dwc2_frame_num_le(qh->next_active_frame, hsotg->frame_number))
>> +       if (dwc2_frame_num_le(qh->next_active_frame, hsotg->frame_number))
>> {
>> +               enum dwc2_transaction_type tr_type;
>> +
>> +               /*
>> +                * We're bypassing the SOF handler which is normally what
>> puts
>> +                * us on the ready list because we're in a hurry and need
>> to
>> +                * try to catch up.
>> +                */
>> +               dwc2_sch_vdbg(hsotg, "QH=%p IMM ready fn=%04x,
>> nxt=%04x\n",
>> +                             qh, frame_number, qh->next_active_frame);
>>                 list_move_tail(&qh->qh_list_entry,
>>                                &hsotg->periodic_sched_ready);
>> -       else
>> +
>> +               tr_type = dwc2_hcd_select_transactions(hsotg);
>
> Do we need to add select_transactions call here? If we get into this
> function in interrupt
> and once we put the qh in ready queue, the qh can be handled in this frame
> again by the
> later function call of dwc_hcd_select_transactions, so what we need to to
> here is put
> it in ready list instead of inactive queue, and wait for the schedule.

I'm not sure I understand.  Can you restate?


I'll try to explain more in the meantime...

Both before and after my change, this function would place something
on the ready queue if the next_active_frame <= the frame number as of
last SOF interrupt (aka hsotg->frame_number).  Otherwise it goes on
the inactive queue.  Assuming that the previous change ("usb: dwc2:
host: Manage frame nums better in scheduler") worked properly then
next_active_frame shouldn't be less than (hsotg->frame_number - 1).
Remember that next_active_frame is always 1 before the wire frame, so
if "next_active_frame == hsotg->frame_number - 1" it means that we
need to get the transfer on the wire _right away_.  If
"next_active_frame == hsotg->frame_number" the transfer doesn't need
to go on the wire right away, but since dwc2 can be prepped one frame
in advance it doesn't hurt to give it to the hardware right away if
there's space.

As I understand it, if we stick something on the ready queue it won't
generally get looked at until the next SOF interrupt.  That means
we'll be too late if "next_active_frame == hsotg->frame_number - 1"
and we'll possibly be too late (depending on interrupt latency) if
"next_active_frame == hsotg->frame_number"


Note that before my series, there were more places than just the SOF
interrupt that would update hsotg->frame_number (see "usb: dwc2: host:
Manage frame nums better in scheduler" for fix).  Also before my
series (specially "usb: dwc2: host: Manage frame nums better in
scheduler") we used the actual current frame number when doing
comparisons.  Also before my series (specifically "usb: dwc2: host:
Properly set even/odd frame") we didn't really place things in the
frame that they were scheduled in anyway.


Also note that I believe that when dwc2_hcd_qh_deactivate() is called
our spinlock is held which means that the SOF interrupt either ran
before our function or won't run till after it.

>
>> +               if (tr_type != DWC2_TRANSACTION_NONE)
>> +                       dwc2_hcd_queue_transactions(hsotg, tr_type);
>> +       } else {
>>                 list_move_tail(&qh->qh_list_entry,
>>                                &hsotg->periodic_sched_inactive);
>> +       }
>>   }
>>     /**
>
>
>


From dianders at chromium.org  Sun Jan 31 14:19:37 2016
From: dianders at chromium.org (Doug Anderson)
Date: Sun, 31 Jan 2016 14:19:37 -0800
Subject: [PATCH v6 10/22] usb: dwc2: host: Properly set the HFIR
In-Reply-To: <56ADD2AF.3040901@rock-chips.com>
References: <1454034013-24657-1-git-send-email-dianders@chromium.org>
 <1454034013-24657-11-git-send-email-dianders@chromium.org>
 <56ADD2AF.3040901@rock-chips.com>
Message-ID: <CAD=FV=UNi4xu0=tt0cwAFaCHiwyPm_UoGrckvzMG196Q9Egg7g@mail.gmail.com>

Kever,

On Sun, Jan 31, 2016 at 1:23 AM, Kever Yang <kever.yang at rock-chips.com> wrote:
> Doug,
>
> On 01/29/2016 10:20 AM, Douglas Anderson wrote:
>>
>> According to the most up to date version of the dwc2 databook, the FRINT
>> field of the HFIR register should be programmed to:
>> * 125 us * (PHY clock freq for HS) - 1
>> * 1000 us * (PHY clock freq for FS/LS) - 1
>
> I got 3 version of dwc_otg databook, 2.74a, 2.94a and 3.10a,
> all the doc describe the FrInt as:

Can you check to see if you can get 3.30a (October 2015)?


> * 125 us * (PHY clock freq for HS)
> * 1000 us * (PHY clock freq for FS/LS)
>
> Maybe John can help to check the design.

Yes, this really needs John or someone at Synopsys.


> There are some feature different in new and old version, but not sure
> if this is one of then.
>
> The doc says If no value is programmed, the corecalculates the value
> based on the PHY clock specified in the FS/LS PHY Clock select field of
> Host configuration register(HCFG.FLSLPclkSel), does this work?

It seems to.  It looks like that's what makes our firmware work.  I'm
not 100% sure if there are any downsides to that approach...

-Doug


From wdc at rock-chips.com  Sun Jan 31 18:35:30 2016
From: wdc at rock-chips.com (David.Wu)
Date: Mon, 1 Feb 2016 10:35:30 +0800
Subject: [PATCH v3 2/2] dt-bindings: rockchip-pinctrl: Support the RK3399
 SoCs compatible
In-Reply-To: <14543443.RsRWh5K5Uv@phil>
References: <1451997055-47655-1-git-send-email-wdc@rock-chips.com>
 <1454153517-52969-1-git-send-email-david.wu@rock-chips.com>
 <1454153517-52969-3-git-send-email-david.wu@rock-chips.com>
 <14543443.RsRWh5K5Uv@phil>
Message-ID: <56AEC472.9030909@rock-chips.com>

Hi Heiko,

? 2016/1/30 20:16, Heiko Stuebner ??:
> Hi David,
>
> Am Samstag, 30. Januar 2016, 19:31:57 schrieb David Wu:
>> Change-Id: Ic1bdb3dc358837e97d7b48dd58be2f8d9d08d766
> please no Change-Id, it's missing the Signed-off, and as Linus Walleij said,
> the compatible addition to the documentation can maybe stay in the pinctrl-
> patch itself.
>
> I think both Linus and me overlooked that the binding-doc-addition was
> already correctly included in your v2 patch at the top :-)
>

Okay, I will merge it in one patch. sorry for my misunderstand.


> Heiko
>
>> ---
>>   Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt | 1 +
>>   1 file changed, 1 insertion(+)
>>
>> diff --git
>> a/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
>> b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt index
>> 391ef4b..3bb9456 100644
>> --- a/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
>> +++ b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
>> @@ -22,6 +22,7 @@ Required properties for iomux controller:
>>     - compatible: one of "rockchip,rk2928-pinctrl",
>> "rockchip,rk3066a-pinctrl" "rockchip,rk3066b-pinctrl",
>> "rockchip,rk3188-pinctrl"
>>   		       "rockchip,rk3288-pinctrl", "rockchip,rk3368-pinctrl"
>> +		       "rockchip,rk3399-pinctrl"
>>     - rockchip,grf: phandle referencing a syscon providing the
>>   	 "general register files"
>
>
>




From david.wu at rock-chips.com  Sun Jan 31 18:58:21 2016
From: david.wu at rock-chips.com (David Wu)
Date: Mon,  1 Feb 2016 10:58:21 +0800
Subject: [PATCH v5] pinctrl: rockchip: add support for the rk3399
Message-ID: <1454295501-32165-1-git-send-email-david.wu@rock-chips.com>

The pinctrl of rk3399 is much different from other's,
especially the 3bits of drive strength.

Signed-off-by: David Wu <david.wu at rock-chips.com>
Reviewed-by: Heiko Stuebner <heiko at sntech.de>
---
Changes in v5:
- merge the binding-doc-addition in one patch (Linus Heiko) 
- fix the comment of bit15 drive setting (Heiko)

Change in v4: None

Change in v3:
- use switch-case to distinguish special 3bits width per pin (Heiko)

Change in v2:
- need spin_unlock_irqrestore for set drive default case

 .../bindings/pinctrl/rockchip,pinctrl.txt          |   1 +
 drivers/pinctrl/pinctrl-rockchip.c                 | 369 ++++++++++++++++++++-
 2 files changed, 356 insertions(+), 14 deletions(-)

diff --git a/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
index 391ef4b..3bb9456 100644
--- a/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
+++ b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
@@ -22,6 +22,7 @@ Required properties for iomux controller:
   - compatible: one of "rockchip,rk2928-pinctrl", "rockchip,rk3066a-pinctrl"
 		       "rockchip,rk3066b-pinctrl", "rockchip,rk3188-pinctrl"
 		       "rockchip,rk3288-pinctrl", "rockchip,rk3368-pinctrl"
+		       "rockchip,rk3399-pinctrl"
   - rockchip,grf: phandle referencing a syscon providing the
 	 "general register files"
 
diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c
index a065112..d485f22 100644
--- a/drivers/pinctrl/pinctrl-rockchip.c
+++ b/drivers/pinctrl/pinctrl-rockchip.c
@@ -64,6 +64,7 @@ enum rockchip_pinctrl_type {
 	RK3188,
 	RK3288,
 	RK3368,
+	RK3399,
 };
 
 /**
@@ -86,6 +87,31 @@ struct rockchip_iomux {
 };
 
 /**
+ * enum type index corresponding to rockchip_perpin_drv_list arrays index.
+ */
+enum rockchip_pin_drv_type {
+	DRV_TYPE_IO_DEFAULT = 0,
+	DRV_TYPE_IO_1V8_OR_3V0,
+	DRV_TYPE_IO_1V8_ONLY,
+	DRV_TYPE_IO_1V8_3V0_AUTO,
+	DRV_TYPE_IO_3V3_ONLY,
+	DRV_TYPE_MAX
+};
+
+/**
+ * @drv_type: drive strength variant using rockchip_perpin_drv_type
+ * @offset: if initialized to -1 it will be autocalculated, by specifying
+ *	    an initial offset value the relevant source offset can be reset
+ *	    to a new value for autocalculating the following drive strength
+ *	    registers. if used chips own cal_drv func instead to calculate
+ *	    registers offset, the variant could be ignored.
+ */
+struct rockchip_drv {
+	enum rockchip_pin_drv_type	drv_type;
+	int				offset;
+};
+
+/**
  * @reg_base: register base of the gpio bank
  * @reg_pull: optional separate register for additional pull settings
  * @clk: clock of the gpio bank
@@ -96,6 +122,7 @@ struct rockchip_iomux {
  * @name: name of the bank
  * @bank_num: number of the bank, to account for holes
  * @iomux: array describing the 4 iomux sources of the bank
+ * @drv: array describing the 4 drive strength sources of the bank
  * @valid: are all necessary informations present
  * @of_node: dt node of this bank
  * @drvdata: common pinctrl basedata
@@ -115,6 +142,7 @@ struct rockchip_pin_bank {
 	char				*name;
 	u8				bank_num;
 	struct rockchip_iomux		iomux[4];
+	struct rockchip_drv		drv[4];
 	bool				valid;
 	struct device_node		*of_node;
 	struct rockchip_pinctrl		*drvdata;
@@ -151,6 +179,47 @@ struct rockchip_pin_bank {
 		},							\
 	}
 
+#define PIN_BANK_DRV_FLAGS(id, pins, label, type0, type1, type2, type3) \
+	{								\
+		.bank_num	= id,					\
+		.nr_pins	= pins,					\
+		.name		= label,				\
+		.iomux		= {					\
+			{ .offset = -1 },				\
+			{ .offset = -1 },				\
+			{ .offset = -1 },				\
+			{ .offset = -1 },				\
+		},							\
+		.drv		= {					\
+			{ .drv_type = type0, .offset = -1 },		\
+			{ .drv_type = type1, .offset = -1 },		\
+			{ .drv_type = type2, .offset = -1 },		\
+			{ .drv_type = type3, .offset = -1 },		\
+		},							\
+	}
+
+#define PIN_BANK_IOMUX_DRV_FLAGS_OFFSET(id, pins, label, iom0, iom1,	\
+					iom2, iom3, drv0, drv1, drv2,	\
+					drv3, offset0, offset1,		\
+					offset2, offset3)		\
+	{								\
+		.bank_num	= id,					\
+		.nr_pins	= pins,					\
+		.name		= label,				\
+		.iomux		= {					\
+			{ .type = iom0, .offset = -1 },			\
+			{ .type = iom1, .offset = -1 },			\
+			{ .type = iom2, .offset = -1 },			\
+			{ .type = iom3, .offset = -1 },			\
+		},							\
+		.drv		= {					\
+			{ .drv_type = drv0, .offset = offset0 },	\
+			{ .drv_type = drv1, .offset = offset1 },	\
+			{ .drv_type = drv2, .offset = offset2 },	\
+			{ .drv_type = drv3, .offset = offset3 },	\
+		},							\
+	}
+
 /**
  */
 struct rockchip_pin_ctrl {
@@ -161,6 +230,9 @@ struct rockchip_pin_ctrl {
 	enum rockchip_pinctrl_type	type;
 	int				grf_mux_offset;
 	int				pmu_mux_offset;
+	int				grf_drv_offset;
+	int				pmu_drv_offset;
+
 	void	(*pull_calc_reg)(struct rockchip_pin_bank *bank,
 				    int pin_num, struct regmap **regmap,
 				    int *reg, u8 *bit);
@@ -676,7 +748,68 @@ static void rk3368_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
 	}
 }
 
-static int rockchip_perpin_drv_list[] = { 2, 4, 8, 12 };
+#define RK3399_PULL_GRF_OFFSET		0xe040
+#define RK3399_PULL_PMU_OFFSET		0x40
+#define RK3399_DRV_3BITS_PER_PIN	3
+
+static void rk3399_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+					 int pin_num, struct regmap **regmap,
+					 int *reg, u8 *bit)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+
+	/* The bank0:16 and bank1:32 pins are located in PMU */
+	if ((bank->bank_num == 0) || (bank->bank_num == 1)) {
+		*regmap = info->regmap_pmu;
+		*reg = RK3399_PULL_PMU_OFFSET;
+
+		*reg += bank->bank_num * RK3188_PULL_BANK_STRIDE;
+
+		*reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4);
+		*bit = pin_num % RK3188_PULL_PINS_PER_REG;
+		*bit *= RK3188_PULL_BITS_PER_PIN;
+	} else {
+		*regmap = info->regmap_base;
+		*reg = RK3399_PULL_GRF_OFFSET;
+
+		/* correct the offset, as we're starting with the 3rd bank */
+		*reg -= 0x20;
+		*reg += bank->bank_num * RK3188_PULL_BANK_STRIDE;
+		*reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4);
+
+		*bit = (pin_num % RK3188_PULL_PINS_PER_REG);
+		*bit *= RK3188_PULL_BITS_PER_PIN;
+	}
+}
+
+static void rk3399_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
+					int pin_num, struct regmap **regmap,
+					int *reg, u8 *bit)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+	int drv_num = (pin_num / 8);
+
+	/*  The bank0:16 and bank1:32 pins are located in PMU */
+	if ((bank->bank_num == 0) || (bank->bank_num == 1))
+		*regmap = info->regmap_pmu;
+	else
+		*regmap = info->regmap_base;
+
+	*reg = bank->drv[drv_num].offset;
+	if ((bank->drv[drv_num].drv_type == DRV_TYPE_IO_1V8_3V0_AUTO) ||
+	    (bank->drv[drv_num].drv_type == DRV_TYPE_IO_3V3_ONLY))
+		*bit = (pin_num % 8) * 3;
+	else
+		*bit = (pin_num % 8) * 2;
+}
+
+static int rockchip_perpin_drv_list[DRV_TYPE_MAX][8] = {
+	{ 2, 4, 8, 12, -1, -1, -1, -1 },
+	{ 3, 6, 9, 12, -1, -1, -1, -1 },
+	{ 5, 10, 15, 20, -1, -1, -1, -1 },
+	{ 4, 6, 8, 10, 12, 14, 16, 18 },
+	{ 4, 7, 10, 13, 16, 19, 22, 26 }
+};
 
 static int rockchip_get_drive_perpin(struct rockchip_pin_bank *bank,
 				     int pin_num)
@@ -685,19 +818,74 @@ static int rockchip_get_drive_perpin(struct rockchip_pin_bank *bank,
 	struct rockchip_pin_ctrl *ctrl = info->ctrl;
 	struct regmap *regmap;
 	int reg, ret;
-	u32 data;
+	u32 data, temp, rmask_bits;
 	u8 bit;
+	int drv_type = bank->drv[pin_num / 8].drv_type;
 
 	ctrl->drv_calc_reg(bank, pin_num, &regmap, &reg, &bit);
 
+	switch (drv_type) {
+	case DRV_TYPE_IO_1V8_3V0_AUTO:
+	case DRV_TYPE_IO_3V3_ONLY:
+		rmask_bits = RK3399_DRV_3BITS_PER_PIN;
+		switch (bit) {
+		case 0 ... 12:
+			/* regular case, nothing to do */
+			break;
+		case 15:
+			/*
+			 * drive-strength offset is special, as it is
+			 * spread over 2 registers
+			 */
+			ret = regmap_read(regmap, reg, &data);
+			if (ret)
+				return ret;
+
+			ret = regmap_read(regmap, reg + 0x4, &temp);
+			if (ret)
+				return ret;
+
+			/*
+			 * the bit data[15] contains bit 0 of the value
+			 * while temp[1:0] contains bits 2 and 1
+			 */
+			data >>= 15;
+			temp &= 0x3;
+			temp <<= 1;
+			data |= temp;
+
+			return rockchip_perpin_drv_list[drv_type][data];
+		case 18 ... 21:
+			/* setting fully enclosed in the second register */
+			reg += 4;
+			bit -= 16;
+			break;
+		default:
+			dev_err(info->dev, "unsupported bit: %d for pinctrl drive type: %d\n",
+				bit, drv_type);
+			return -EINVAL;
+		}
+
+		break;
+	case DRV_TYPE_IO_DEFAULT:
+	case DRV_TYPE_IO_1V8_OR_3V0:
+	case DRV_TYPE_IO_1V8_ONLY:
+		rmask_bits = RK3288_DRV_BITS_PER_PIN;
+		break;
+	default:
+		dev_err(info->dev, "unsupported pinctrl drive type: %d\n",
+			drv_type);
+		return -EINVAL;
+	}
+
 	ret = regmap_read(regmap, reg, &data);
 	if (ret)
 		return ret;
 
 	data >>= bit;
-	data &= (1 << RK3288_DRV_BITS_PER_PIN) - 1;
+	data &= (1 << rmask_bits) - 1;
 
-	return rockchip_perpin_drv_list[data];
+	return rockchip_perpin_drv_list[drv_type][data];
 }
 
 static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank,
@@ -708,16 +896,23 @@ static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank,
 	struct regmap *regmap;
 	unsigned long flags;
 	int reg, ret, i;
-	u32 data, rmask;
+	u32 data, rmask, rmask_bits, temp;
 	u8 bit;
+	int drv_type = bank->drv[pin_num / 8].drv_type;
+
+	dev_dbg(info->dev, "setting drive of GPIO%d-%d to %d\n",
+		bank->bank_num, pin_num, strength);
 
 	ctrl->drv_calc_reg(bank, pin_num, &regmap, &reg, &bit);
 
 	ret = -EINVAL;
-	for (i = 0; i < ARRAY_SIZE(rockchip_perpin_drv_list); i++) {
-		if (rockchip_perpin_drv_list[i] == strength) {
+	for (i = 0; i < ARRAY_SIZE(rockchip_perpin_drv_list[drv_type]); i++) {
+		if (rockchip_perpin_drv_list[drv_type][i] == strength) {
 			ret = i;
 			break;
+		} else if (rockchip_perpin_drv_list[drv_type][i] < 0) {
+			ret = rockchip_perpin_drv_list[drv_type][i];
+			break;
 		}
 	}
 
@@ -729,8 +924,64 @@ static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank,
 
 	spin_lock_irqsave(&bank->slock, flags);
 
+	switch (drv_type) {
+	case DRV_TYPE_IO_1V8_3V0_AUTO:
+	case DRV_TYPE_IO_3V3_ONLY:
+		rmask_bits = RK3399_DRV_3BITS_PER_PIN;
+		switch (bit) {
+		case 0 ... 12:
+			/* regular case, nothing to do */
+			break;
+		case 15:
+			/*
+			 * drive-strength offset is special, as it is spread
+			 * over 2 registers, the bit data[15] contains bit 0
+			 * of the value while temp[1:0] contains bits 2 and 1
+			 */
+			data = (ret & 0x1) << 15;
+			temp = (ret >> 0x1) & 0x3;
+
+			rmask = BIT(15) | BIT(31);
+			data |= BIT(31);
+			ret = regmap_update_bits(regmap, reg, rmask, data);
+			if (ret) {
+				spin_unlock_irqrestore(&bank->slock, flags);
+				return ret;
+			}
+
+			rmask = 0x3 | (0x3 << 16);
+			temp |= (0x3 << 16);
+			reg += 0x4;
+			ret = regmap_update_bits(regmap, reg, rmask, temp);
+
+			spin_unlock_irqrestore(&bank->slock, flags);
+			return ret;
+		case 18 ... 21:
+			/* setting fully enclosed in the second register */
+			reg += 4;
+			bit -= 16;
+			break;
+		default:
+			spin_unlock_irqrestore(&bank->slock, flags);
+			dev_err(info->dev, "unsupported bit: %d for pinctrl drive type: %d\n",
+				bit, drv_type);
+			return -EINVAL;
+		}
+		break;
+	case DRV_TYPE_IO_DEFAULT:
+	case DRV_TYPE_IO_1V8_OR_3V0:
+	case DRV_TYPE_IO_1V8_ONLY:
+		rmask_bits = RK3288_DRV_BITS_PER_PIN;
+		break;
+	default:
+		spin_unlock_irqrestore(&bank->slock, flags);
+		dev_err(info->dev, "unsupported pinctrl drive type: %d\n",
+			drv_type);
+		return -EINVAL;
+	}
+
 	/* enable the write to the equivalent lower bits */
-	data = ((1 << RK3288_DRV_BITS_PER_PIN) - 1) << (bit + 16);
+	data = ((1 << rmask_bits) - 1) << (bit + 16);
 	rmask = data | (data >> 16);
 	data |= (ret << bit);
 
@@ -767,6 +1018,7 @@ static int rockchip_get_pull(struct rockchip_pin_bank *bank, int pin_num)
 	case RK3188:
 	case RK3288:
 	case RK3368:
+	case RK3399:
 		data >>= bit;
 		data &= (1 << RK3188_PULL_BITS_PER_PIN) - 1;
 
@@ -823,6 +1075,7 @@ static int rockchip_set_pull(struct rockchip_pin_bank *bank,
 	case RK3188:
 	case RK3288:
 	case RK3368:
+	case RK3399:
 		spin_lock_irqsave(&bank->slock, flags);
 
 		/* enable the write to the equivalent lower bits */
@@ -1003,6 +1256,7 @@ static bool rockchip_pinconf_pull_valid(struct rockchip_pin_ctrl *ctrl,
 	case RK3188:
 	case RK3288:
 	case RK3368:
+	case RK3399:
 		return (pull != PIN_CONFIG_BIAS_PULL_PIN_DEFAULT);
 	}
 
@@ -1860,7 +2114,7 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
 	struct device_node *np;
 	struct rockchip_pin_ctrl *ctrl;
 	struct rockchip_pin_bank *bank;
-	int grf_offs, pmu_offs, i, j;
+	int grf_offs, pmu_offs, drv_grf_offs, drv_pmu_offs, i, j;
 
 	match = of_match_node(rockchip_pinctrl_dt_match, node);
 	ctrl = (struct rockchip_pin_ctrl *)match->data;
@@ -1884,6 +2138,8 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
 
 	grf_offs = ctrl->grf_mux_offset;
 	pmu_offs = ctrl->pmu_mux_offset;
+	drv_pmu_offs = ctrl->pmu_drv_offset;
+	drv_grf_offs = ctrl->grf_drv_offset;
 	bank = ctrl->pin_banks;
 	for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
 		int bank_pins = 0;
@@ -1893,27 +2149,39 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
 		bank->pin_base = ctrl->nr_pins;
 		ctrl->nr_pins += bank->nr_pins;
 
-		/* calculate iomux offsets */
+		/* calculate iomux and drv offsets */
 		for (j = 0; j < 4; j++) {
 			struct rockchip_iomux *iom = &bank->iomux[j];
+			struct rockchip_drv *drv = &bank->drv[j];
 			int inc;
 
 			if (bank_pins >= bank->nr_pins)
 				break;
 
-			/* preset offset value, set new start value */
+			/* preset iomux offset value, set new start value */
 			if (iom->offset >= 0) {
 				if (iom->type & IOMUX_SOURCE_PMU)
 					pmu_offs = iom->offset;
 				else
 					grf_offs = iom->offset;
-			} else { /* set current offset */
+			} else { /* set current iomux offset */
 				iom->offset = (iom->type & IOMUX_SOURCE_PMU) ?
 							pmu_offs : grf_offs;
 			}
 
-			dev_dbg(d->dev, "bank %d, iomux %d has offset 0x%x\n",
-				 i, j, iom->offset);
+			/* preset drv offset value, set new start value */
+			if (drv->offset >= 0) {
+				if (iom->type & IOMUX_SOURCE_PMU)
+					drv_pmu_offs = drv->offset;
+				else
+					drv_grf_offs = drv->offset;
+			} else { /* set current drv offset */
+				drv->offset = (iom->type & IOMUX_SOURCE_PMU) ?
+						drv_pmu_offs : drv_grf_offs;
+			}
+
+			dev_dbg(d->dev, "bank %d, iomux %d has iom_offset 0x%x drv_offset 0x%x\n",
+				i, j, iom->offset, drv->offset);
 
 			/*
 			 * Increase offset according to iomux width.
@@ -1925,6 +2193,21 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
 			else
 				grf_offs += inc;
 
+			/*
+			 * Increase offset according to drv width.
+			 * 3bit drive-strenth'es are spread over two registers.
+			 */
+			if ((drv->drv_type == DRV_TYPE_IO_1V8_3V0_AUTO) ||
+			    (drv->drv_type == DRV_TYPE_IO_3V3_ONLY))
+				inc = 8;
+			else
+				inc = 4;
+
+			if (iom->type & IOMUX_SOURCE_PMU)
+				drv_pmu_offs += inc;
+			else
+				drv_grf_offs += inc;
+
 			bank_pins += 8;
 		}
 	}
@@ -2208,6 +2491,62 @@ static struct rockchip_pin_ctrl rk3368_pin_ctrl = {
 		.drv_calc_reg		= rk3368_calc_drv_reg_and_bit,
 };
 
+static struct rockchip_pin_bank rk3399_pin_banks[] = {
+	PIN_BANK_IOMUX_DRV_FLAGS_OFFSET(0, 32, "gpio0", IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					DRV_TYPE_IO_1V8_ONLY,
+					DRV_TYPE_IO_1V8_ONLY,
+					DRV_TYPE_IO_DEFAULT,
+					DRV_TYPE_IO_DEFAULT,
+					0x0,
+					0x8,
+					-1,
+					-1
+					),
+	PIN_BANK_IOMUX_DRV_FLAGS_OFFSET(1, 32, "gpio1", IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					IOMUX_SOURCE_PMU,
+					DRV_TYPE_IO_1V8_OR_3V0,
+					DRV_TYPE_IO_1V8_OR_3V0,
+					DRV_TYPE_IO_1V8_OR_3V0,
+					DRV_TYPE_IO_1V8_OR_3V0,
+					0x20,
+					0x28,
+					0x30,
+					0x38
+					),
+	PIN_BANK_DRV_FLAGS(2, 32, "gpio2", DRV_TYPE_IO_1V8_OR_3V0,
+			   DRV_TYPE_IO_1V8_OR_3V0,
+			   DRV_TYPE_IO_1V8_ONLY,
+			   DRV_TYPE_IO_1V8_ONLY
+			   ),
+	PIN_BANK_DRV_FLAGS(3, 32, "gpio3", DRV_TYPE_IO_3V3_ONLY,
+			   DRV_TYPE_IO_3V3_ONLY,
+			   DRV_TYPE_IO_3V3_ONLY,
+			   DRV_TYPE_IO_1V8_OR_3V0
+			   ),
+	PIN_BANK_DRV_FLAGS(4, 32, "gpio4", DRV_TYPE_IO_1V8_OR_3V0,
+			   DRV_TYPE_IO_1V8_3V0_AUTO,
+			   DRV_TYPE_IO_1V8_OR_3V0,
+			   DRV_TYPE_IO_1V8_OR_3V0
+			   ),
+};
+
+static struct rockchip_pin_ctrl rk3399_pin_ctrl = {
+		.pin_banks		= rk3399_pin_banks,
+		.nr_banks		= ARRAY_SIZE(rk3399_pin_banks),
+		.label			= "RK3399-GPIO",
+		.type			= RK3399,
+		.grf_mux_offset		= 0xe000,
+		.pmu_mux_offset		= 0x0,
+		.grf_drv_offset		= 0xe100,
+		.pmu_drv_offset		= 0x80,
+		.pull_calc_reg		= rk3399_calc_pull_reg_and_bit,
+		.drv_calc_reg		= rk3399_calc_drv_reg_and_bit,
+};
 
 static const struct of_device_id rockchip_pinctrl_dt_match[] = {
 	{ .compatible = "rockchip,rk2928-pinctrl",
@@ -2224,6 +2563,8 @@ static const struct of_device_id rockchip_pinctrl_dt_match[] = {
 		.data = (void *)&rk3288_pin_ctrl },
 	{ .compatible = "rockchip,rk3368-pinctrl",
 		.data = (void *)&rk3368_pin_ctrl },
+	{ .compatible = "rockchip,rk3399-pinctrl",
+		.data = (void *)&rk3399_pin_ctrl },
 	{},
 };
 MODULE_DEVICE_TABLE(of, rockchip_pinctrl_dt_match);
-- 
1.9.1




From kever.yang at rock-chips.com  Sun Jan 31 19:32:06 2016
From: kever.yang at rock-chips.com (Kever Yang)
Date: Mon, 01 Feb 2016 11:32:06 +0800
Subject: [PATCH v6 18/22] usb: dwc2: host: Schedule periodic right away
 if it's time
In-Reply-To: <CAD=FV=XUjAbSQhr9LtPG=5ycPbv6UBsA0rvfrPd25EndnjxRrw@mail.gmail.com>
References: <1454034013-24657-1-git-send-email-dianders@chromium.org>	<1454034013-24657-19-git-send-email-dianders@chromium.org>	<56ADD580.1070505@rock-chips.com>
 <CAD=FV=XUjAbSQhr9LtPG=5ycPbv6UBsA0rvfrPd25EndnjxRrw@mail.gmail.com>
Message-ID: <56AED1B6.7020409@rock-chips.com>

Doug,

On 02/01/2016 06:09 AM, Doug Anderson wrote:
> Kever,
>
> On Sun, Jan 31, 2016 at 1:36 AM, Kever Yang <kever.yang at rock-chips.com> wrote:
>> Doug,
>>
>>
>> On 01/29/2016 10:20 AM, Douglas Anderson wrote:
>>> In dwc2_hcd_qh_deactivate() we will put some things on the
>>> periodic_sched_ready list.  These things won't be taken off the ready
>>> list until the next SOF, which might be a little late.  Let's put them
>>> on right away.
>>>
>>> Signed-off-by: Douglas Anderson <dianders at chromium.org>
>>> Tested-by: Heiko Stuebner <heiko at sntech.de>
>>> Tested-by: Stefan Wahren <stefan.wahren at i2se.com>
>>> ---
>>> Changes in v6:
>>> - Add Heiko's Tested-by.
>>> - Add Stefan's Tested-by.
>>>
>>> Changes in v5: None
>>> Changes in v4:
>>> - Schedule periodic right away if it's time new for v4.
>>>
>>> Changes in v3: None
>>> Changes in v2: None
>>>
>>>    drivers/usb/dwc2/hcd_queue.c | 18 ++++++++++++++++--
>>>    1 file changed, 16 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
>>> index 9b3c435339ee..3abb34a5fc5b 100644
>>> --- a/drivers/usb/dwc2/hcd_queue.c
>>> +++ b/drivers/usb/dwc2/hcd_queue.c
>>> @@ -1080,12 +1080,26 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg
>>> *hsotg, struct dwc2_qh *qh,
>>>           * Note: we purposely use the frame_number from the "hsotg"
>>> structure
>>>           * since we know SOF interrupt will handle future frames.
>>>           */
>>> -       if (dwc2_frame_num_le(qh->next_active_frame, hsotg->frame_number))
>>> +       if (dwc2_frame_num_le(qh->next_active_frame, hsotg->frame_number))
>>> {
>>> +               enum dwc2_transaction_type tr_type;
>>> +
>>> +               /*
>>> +                * We're bypassing the SOF handler which is normally what
>>> puts
>>> +                * us on the ready list because we're in a hurry and need
>>> to
>>> +                * try to catch up.
>>> +                */
>>> +               dwc2_sch_vdbg(hsotg, "QH=%p IMM ready fn=%04x,
>>> nxt=%04x\n",
>>> +                             qh, frame_number, qh->next_active_frame);
>>>                  list_move_tail(&qh->qh_list_entry,
>>>                                 &hsotg->periodic_sched_ready);
>>> -       else
>>> +
>>> +               tr_type = dwc2_hcd_select_transactions(hsotg);
>> Do we need to add select_transactions call here? If we get into this
>> function in interrupt
>> and once we put the qh in ready queue, the qh can be handled in this frame
>> again by the
>> later function call of dwc_hcd_select_transactions, so what we need to to
>> here is put
>> it in ready list instead of inactive queue, and wait for the schedule.
> I'm not sure I understand.  Can you restate?
>
>
> I'll try to explain more in the meantime...
>
> Both before and after my change, this function would place something
> on the ready queue if the next_active_frame <= the frame number as of
> last SOF interrupt (aka hsotg->frame_number).  Otherwise it goes on
> the inactive queue.  Assuming that the previous change ("usb: dwc2:
> host: Manage frame nums better in scheduler") worked properly then
> next_active_frame shouldn't be less than (hsotg->frame_number - 1).
> Remember that next_active_frame is always 1 before the wire frame, so
> if "next_active_frame == hsotg->frame_number - 1" it means that we
> need to get the transfer on the wire _right away_.  If
> "next_active_frame == hsotg->frame_number" the transfer doesn't need
> to go on the wire right away, but since dwc2 can be prepped one frame
> in advance it doesn't hurt to give it to the hardware right away if
> there's space.
>
> As I understand it, if we stick something on the ready queue it won't
> generally get looked at until the next SOF interrupt.  That means
> we'll be too late if "next_active_frame == hsotg->frame_number - 1"
> and we'll possibly be too late (depending on interrupt latency) if
> "next_active_frame == hsotg->frame_number"
>
I understand this patch and agree with your point of schedule the
periodic right away instead of at least next frame.
My point is, there are only two call to dwc2_hcd_qh_deactivate(), from
dwc2_hcd_urb_dequeue() and dwc2_release_channel(), we don't need
to do the schedule for dequeue, and there is one
dwc2_hcd_select_transactions() call at the end of dwc2_release_channel(),
maybe we don't need another dwc2_hcd_select_transactions() here.

I think the duration from this point to the function call of 
dwc2_hcd_select_transactions()
in dwc2_release_channel() will be the main factor for us to decide if
we need to add a function call of  dwc2_hcd_select_transactions() here.

Thanks,
- Kever
> Note that before my series, there were more places than just the SOF
> interrupt that would update hsotg->frame_number (see "usb: dwc2: host:
> Manage frame nums better in scheduler" for fix).  Also before my
> series (specially "usb: dwc2: host: Manage frame nums better in
> scheduler") we used the actual current frame number when doing
> comparisons.  Also before my series (specifically "usb: dwc2: host:
> Properly set even/odd frame") we didn't really place things in the
> frame that they were scheduled in anyway.
>
>
> Also note that I believe that when dwc2_hcd_qh_deactivate() is called
> our spinlock is held which means that the SOF interrupt either ran
> before our function or won't run till after it.
>
>>> +               if (tr_type != DWC2_TRANSACTION_NONE)
>>> +                       dwc2_hcd_queue_transactions(hsotg, tr_type);
>>> +       } else {
>>>                  list_move_tail(&qh->qh_list_entry,
>>>                                 &hsotg->periodic_sched_inactive);
>>> +       }
>>>    }
>>>      /**
>>
>>




From dianders at chromium.org  Sun Jan 31 20:36:05 2016
From: dianders at chromium.org (Doug Anderson)
Date: Sun, 31 Jan 2016 20:36:05 -0800
Subject: [PATCH v6 18/22] usb: dwc2: host: Schedule periodic right away if
 it's time
In-Reply-To: <56AED1B6.7020409@rock-chips.com>
References: <1454034013-24657-1-git-send-email-dianders@chromium.org>
 <1454034013-24657-19-git-send-email-dianders@chromium.org>
 <56ADD580.1070505@rock-chips.com>
 <CAD=FV=XUjAbSQhr9LtPG=5ycPbv6UBsA0rvfrPd25EndnjxRrw@mail.gmail.com>
 <56AED1B6.7020409@rock-chips.com>
Message-ID: <CAD=FV=XJgmXm255=cmtqpVM0iJeWCzC5O8BtE2XY4y5Txvk+gg@mail.gmail.com>

Kever,

On Sun, Jan 31, 2016 at 7:32 PM, Kever Yang <kever.yang at rock-chips.com> wrote:
> Doug,
>
>
> On 02/01/2016 06:09 AM, Doug Anderson wrote:
>>
>> Kever,
>>
>> On Sun, Jan 31, 2016 at 1:36 AM, Kever Yang <kever.yang at rock-chips.com>
>> wrote:
>>>
>>> Doug,
>>>
>>>
>>> On 01/29/2016 10:20 AM, Douglas Anderson wrote:
>>>>
>>>> In dwc2_hcd_qh_deactivate() we will put some things on the
>>>> periodic_sched_ready list.  These things won't be taken off the ready
>>>> list until the next SOF, which might be a little late.  Let's put them
>>>> on right away.
>>>>
>>>> Signed-off-by: Douglas Anderson <dianders at chromium.org>
>>>> Tested-by: Heiko Stuebner <heiko at sntech.de>
>>>> Tested-by: Stefan Wahren <stefan.wahren at i2se.com>
>>>> ---
>>>> Changes in v6:
>>>> - Add Heiko's Tested-by.
>>>> - Add Stefan's Tested-by.
>>>>
>>>> Changes in v5: None
>>>> Changes in v4:
>>>> - Schedule periodic right away if it's time new for v4.
>>>>
>>>> Changes in v3: None
>>>> Changes in v2: None
>>>>
>>>>    drivers/usb/dwc2/hcd_queue.c | 18 ++++++++++++++++--
>>>>    1 file changed, 16 insertions(+), 2 deletions(-)
>>>>
>>>> diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
>>>> index 9b3c435339ee..3abb34a5fc5b 100644
>>>> --- a/drivers/usb/dwc2/hcd_queue.c
>>>> +++ b/drivers/usb/dwc2/hcd_queue.c
>>>> @@ -1080,12 +1080,26 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg
>>>> *hsotg, struct dwc2_qh *qh,
>>>>           * Note: we purposely use the frame_number from the "hsotg"
>>>> structure
>>>>           * since we know SOF interrupt will handle future frames.
>>>>           */
>>>> -       if (dwc2_frame_num_le(qh->next_active_frame,
>>>> hsotg->frame_number))
>>>> +       if (dwc2_frame_num_le(qh->next_active_frame,
>>>> hsotg->frame_number))
>>>> {
>>>> +               enum dwc2_transaction_type tr_type;
>>>> +
>>>> +               /*
>>>> +                * We're bypassing the SOF handler which is normally
>>>> what
>>>> puts
>>>> +                * us on the ready list because we're in a hurry and
>>>> need
>>>> to
>>>> +                * try to catch up.
>>>> +                */
>>>> +               dwc2_sch_vdbg(hsotg, "QH=%p IMM ready fn=%04x,
>>>> nxt=%04x\n",
>>>> +                             qh, frame_number, qh->next_active_frame);
>>>>                  list_move_tail(&qh->qh_list_entry,
>>>>                                 &hsotg->periodic_sched_ready);
>>>> -       else
>>>> +
>>>> +               tr_type = dwc2_hcd_select_transactions(hsotg);
>>>
>>> Do we need to add select_transactions call here? If we get into this
>>> function in interrupt
>>> and once we put the qh in ready queue, the qh can be handled in this
>>> frame
>>> again by the
>>> later function call of dwc_hcd_select_transactions, so what we need to to
>>> here is put
>>> it in ready list instead of inactive queue, and wait for the schedule.
>>
>> I'm not sure I understand.  Can you restate?
>>
>>
>> I'll try to explain more in the meantime...
>>
>> Both before and after my change, this function would place something
>> on the ready queue if the next_active_frame <= the frame number as of
>> last SOF interrupt (aka hsotg->frame_number).  Otherwise it goes on
>> the inactive queue.  Assuming that the previous change ("usb: dwc2:
>> host: Manage frame nums better in scheduler") worked properly then
>> next_active_frame shouldn't be less than (hsotg->frame_number - 1).
>> Remember that next_active_frame is always 1 before the wire frame, so
>> if "next_active_frame == hsotg->frame_number - 1" it means that we
>> need to get the transfer on the wire _right away_.  If
>> "next_active_frame == hsotg->frame_number" the transfer doesn't need
>> to go on the wire right away, but since dwc2 can be prepped one frame
>> in advance it doesn't hurt to give it to the hardware right away if
>> there's space.
>>
>> As I understand it, if we stick something on the ready queue it won't
>> generally get looked at until the next SOF interrupt.  That means
>> we'll be too late if "next_active_frame == hsotg->frame_number - 1"
>> and we'll possibly be too late (depending on interrupt latency) if
>> "next_active_frame == hsotg->frame_number"
>>
> I understand this patch and agree with your point of schedule the
> periodic right away instead of at least next frame.
> My point is, there are only two call to dwc2_hcd_qh_deactivate(), from
> dwc2_hcd_urb_dequeue() and dwc2_release_channel(), we don't need
> to do the schedule for dequeue, and there is one
> dwc2_hcd_select_transactions() call at the end of dwc2_release_channel(),
> maybe we don't need another dwc2_hcd_select_transactions() here.
>
> I think the duration from this point to the function call of
> dwc2_hcd_select_transactions()
> in dwc2_release_channel() will be the main factor for us to decide if
> we need to add a function call of  dwc2_hcd_select_transactions() here.

Oh, now I get what you're saying!

A) You've got dwc2_release_channel() -> dwc2_deactivate_qh() ->
dwc2_hcd_qh_deactivate()
...and always in that case we'll do a select / queue, so we don't need it there.

B) You've got dwc2_hcd_urb_dequeue() -> dwc2_hcd_qh_deactivate()

...but why don't we need it for dwc2_hcd_urb_dequeue()?  Yes, you're
not continuing a split so timing isn't quite as urgent, but you still
might have an INT or ISOC packet that's scheduled with an interval of
1.  We still might want to schedule right away if there are remaining
QTDs, right?

-Doug


From kishon at ti.com  Sun Jan 31 22:03:43 2016
From: kishon at ti.com (Kishon Vijay Abraham I)
Date: Mon, 1 Feb 2016 11:33:43 +0530
Subject: [PATCH v2 2/2] phy: add a driver for the Rockchip SoC internal
 eMMC PHY
In-Reply-To: <56A5D39B.8090008@rock-chips.com>
References: <1451956034-24421-1-git-send-email-shawn.lin@rock-chips.com>
 <1451956081-24501-1-git-send-email-shawn.lin@rock-chips.com>
 <56A5D39B.8090008@rock-chips.com>
Message-ID: <56AEF53F.3050206@ti.com>



On Monday 25 January 2016 01:19 PM, Shawn Lin wrote:
> Hello Kishon,
> 
> Any changce to apply this patch?  :)

back from vacation. Will review it this week. Sorry for the delay.

Thanks
Kishon

> 
> On 2016/1/5 9:08, Shawn Lin wrote:
>> This patch to add a generic PHY driver for ROCKCHIP eMMC PHY.
>> Access the PHY via registers provided by GRF (general register
>> files) module.
>>
>> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
>>
>> ---
>>
>> Changes in v2:
>> - remove phy from rockchip_emmc_phy
>> - remove state from rockchip_emmc_phy
>> - remove on/off from init/exit
>>
>>   drivers/phy/Kconfig             |   7 ++
>>   drivers/phy/Makefile            |   1 +
>>   drivers/phy/phy-rockchip-emmc.c | 241 ++++++++++++++++++++++++++++++++++++++++
>>   3 files changed, 249 insertions(+)
>>   create mode 100644 drivers/phy/phy-rockchip-emmc.c
>>
>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>> index 03cb3ea..47d16e3 100644
>> --- a/drivers/phy/Kconfig
>> +++ b/drivers/phy/Kconfig
>> @@ -320,6 +320,13 @@ config PHY_ROCKCHIP_USB
>>       help
>>         Enable this to support the Rockchip USB 2.0 PHY.
>>
>> +config PHY_ROCKCHIP_EMMC
>> +    tristate "Rockchip EMMC PHY Driver"
>> +    depends on ARCH_ROCKCHIP && OF
>> +    select GENERIC_PHY
>> +    help
>> +      Enable this to support the Rockchip EMMC PHY.
>> +
>>   config PHY_ST_SPEAR1310_MIPHY
>>       tristate "ST SPEAR1310-MIPHY driver"
>>       select GENERIC_PHY
>> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
>> index 075db1a..ed8f175 100644
>> --- a/drivers/phy/Makefile
>> +++ b/drivers/phy/Makefile
>> @@ -35,6 +35,7 @@ phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2)    +=
>> phy-s5pv210-usb2.o
>>   obj-$(CONFIG_PHY_EXYNOS5_USBDRD)    += phy-exynos5-usbdrd.o
>>   obj-$(CONFIG_PHY_QCOM_APQ8064_SATA)    += phy-qcom-apq8064-sata.o
>>   obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
>> +obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o
>>   obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA)    += phy-qcom-ipq806x-sata.o
>>   obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY)    += phy-spear1310-miphy.o
>>   obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)    += phy-spear1340-miphy.o
>> diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c
>> new file mode 100644
>> index 0000000..6a157c2
>> --- /dev/null
>> +++ b/drivers/phy/phy-rockchip-emmc.c
>> @@ -0,0 +1,241 @@
>> +/*
>> + * Rockchip emmc PHY driver
>> + *
>> + * Copyright (C) 2015 Shawn Lin <shawn.lin at rock-chips.com>
>> + * Copyright (C) 2015 ROCKCHIP, Inc.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/delay.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <linux/phy/phy.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/regmap.h>
>> +
>> +/*
>> + * The higher 16-bit of this register is used for write protection
>> + * only if BIT(x + 16) set to 1 the BIT(x) can be written.
>> + */
>> +#define HIWORD_UPDATE(val, mask, shift) \
>> +        ((val) << (shift) | (mask) << ((shift) + 16))
>> +
>> +/* Register definition */
>> +#define GRF_EMMCPHY_CON0    0x0
>> +#define GRF_EMMCPHY_CON1    0x4
>> +#define GRF_EMMCPHY_CON2    0x8
>> +#define GRF_EMMCPHY_CON3    0xc
>> +#define GRF_EMMCPHY_CON4    0x10
>> +#define GRF_EMMCPHY_CON5    0x14
>> +#define GRF_EMMCPHY_CON6    0x18
>> +#define GRF_EMMCPHY_STATUS    0x20
>> +
>> +#define PHYCTRL_PDB_MASK    0x1
>> +#define PHYCTRL_PDB_SHIFT    0x0
>> +#define PHYCTRL_PDB_PWR_ON    0x1
>> +#define PHYCTRL_PDB_PWR_OFF    0x0
>> +#define PHYCTRL_ENDLL_MASK    0x1
>> +#define PHYCTRL_ENDLL_SHIFT     0x1
>> +#define PHYCTRL_ENDLL_ENABLE    0x1
>> +#define PHYCTRL_ENDLL_DISABLE    0x0
>> +#define PHYCTRL_CALDONE_MASK    0x1
>> +#define PHYCTRL_CALDONE_SHIFT   0x6
>> +#define PHYCTRL_CALDONE_DONE    0x1
>> +#define PHYCTRL_CALDONE_GOING    0x0
>> +#define PHYCTRL_DLLRDY_MASK    0x1
>> +#define PHYCTRL_DLLRDY_SHIFT    0x5
>> +#define PHYCTRL_DLLRDY_DONE    0x1
>> +#define PHYCTRL_DLLRDY_GOING    0x0
>> +
>> +struct rockchip_emmc_phy {
>> +    unsigned int    reg_offset;
>> +    struct regmap    *reg_base;
>> +};
>> +
>> +static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy,
>> +                   bool on_off)
>> +{
>> +    unsigned int caldone;
>> +    unsigned int dllrdy;
>> +
>> +    /*
>> +     * Keep phyctrl_pdb and phyctrl_endll low to allow
>> +     * initialization of CALIO state M/C DFFs
>> +     */
>> +    regmap_write(rk_phy->reg_base,
>> +             rk_phy->reg_offset + GRF_EMMCPHY_CON6,
>> +             HIWORD_UPDATE(PHYCTRL_PDB_PWR_OFF,
>> +                   PHYCTRL_PDB_MASK,
>> +                   PHYCTRL_PDB_SHIFT));
>> +    regmap_write(rk_phy->reg_base,
>> +             rk_phy->reg_offset + GRF_EMMCPHY_CON6,
>> +             HIWORD_UPDATE(PHYCTRL_ENDLL_DISABLE,
>> +                   PHYCTRL_ENDLL_MASK,
>> +                   PHYCTRL_ENDLL_SHIFT));
>> +
>> +    /* Already finish power_off above */
>> +    if (on_off == PHYCTRL_PDB_PWR_OFF)
>> +        return 0;
>> +
>> +    /*
>> +     * According to the user manual, calpad calibration
>> +     * cycle takes more than 2us without the minimal recommended
>> +     * value, so we may need a little margin here
>> +     */
>> +    udelay(3);
>> +    regmap_write(rk_phy->reg_base,
>> +             rk_phy->reg_offset + GRF_EMMCPHY_CON6,
>> +             HIWORD_UPDATE(PHYCTRL_PDB_PWR_ON,
>> +                   PHYCTRL_PDB_MASK,
>> +                   PHYCTRL_PDB_SHIFT));
>> +
>> +    /*
>> +     * According to the user manual, it asks driver to
>> +     * wait 5us for calpad busy trimming
>> +     */
>> +    udelay(5);
>> +    regmap_read(rk_phy->reg_base,
>> +            rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
>> +            &caldone);
>> +    caldone = (caldone >> PHYCTRL_CALDONE_SHIFT) & PHYCTRL_CALDONE_MASK;
>> +    if (caldone != PHYCTRL_CALDONE_DONE) {
>> +        pr_err("rockchip_emmc_phy_power: caldone timeout.\n");
>> +        return -ETIMEDOUT;
>> +    }
>> +
>> +    /*
>> +     * Enable analog DLL circuits, and we need extra 10.2us
>> +     * for dll to be ready for work.
>> +     */
>> +    regmap_write(rk_phy->reg_base,
>> +             rk_phy->reg_offset + GRF_EMMCPHY_CON6,
>> +             HIWORD_UPDATE(PHYCTRL_ENDLL_ENABLE,
>> +                   PHYCTRL_ENDLL_MASK,
>> +                   PHYCTRL_ENDLL_SHIFT));
>> +    udelay(11);
>> +    regmap_read(rk_phy->reg_base,
>> +            rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
>> +            &dllrdy);
>> +    dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK;
>> +    if (dllrdy != PHYCTRL_DLLRDY_DONE) {
>> +        pr_err("rockchip_emmc_phy_power: dllrdy timeout.\n");
>> +        return -ETIMEDOUT;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static int rockchip_emmc_phy_power_off(struct phy *phy)
>> +{
>> +    struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
>> +    int ret = 0;
>> +
>> +    /* Power down emmc phy analog blocks */
>> +    ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_OFF);
>> +    if (ret)
>> +        return ret;
>> +
>> +    return 0;
>> +}
>> +
>> +static int rockchip_emmc_phy_power_on(struct phy *phy)
>> +{
>> +    struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
>> +    int ret = 0;
>> +
>> +    /* Power up emmc phy analog blocks */
>> +    ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_ON);
>> +    if (ret)
>> +        return ret;
>> +
>> +    return 0;
>> +}
>> +
>> +static int rockchip_emmc_phy_init(struct phy *phy)
>> +{
>> +    return 0;
>> +}
>> +
>> +static int rockchip_emmc_phy_exit(struct phy *phy)
>> +{
>> +    return 0;
>> +}
>> +
>> +static const struct phy_ops ops = {
>> +    .init        = rockchip_emmc_phy_init,
>> +    .exit        = rockchip_emmc_phy_exit,
>> +    .power_on    = rockchip_emmc_phy_power_on,
>> +    .power_off    = rockchip_emmc_phy_power_off,
>> +    .owner        = THIS_MODULE,
>> +};
>> +
>> +static int rockchip_emmc_phy_probe(struct platform_device *pdev)
>> +{
>> +    struct device *dev = &pdev->dev;
>> +    struct rockchip_emmc_phy *rk_phy;
>> +    struct phy *generic_phy;
>> +    struct phy_provider *phy_provider;
>> +    struct regmap *grf;
>> +    unsigned int reg_offset;
>> +
>> +    grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
>> +    if (IS_ERR(grf)) {
>> +        dev_err(dev, "Missing rockchip,grf property\n");
>> +        return PTR_ERR(grf);
>> +    }
>> +
>> +    rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL);
>> +    if (!rk_phy)
>> +        return -ENOMEM;
>> +
>> +    if (of_property_read_u32(dev->of_node, "reg", &reg_offset)) {
>> +        dev_err(dev, "missing reg property in node %s\n",
>> +            dev->of_node->name);
>> +        return -EINVAL;
>> +    }
>> +
>> +    rk_phy->reg_offset = reg_offset;
>> +    rk_phy->reg_base = grf;
>> +
>> +    generic_phy = devm_phy_create(dev, dev->of_node, &ops);
>> +    if (IS_ERR(generic_phy)) {
>> +        dev_err(dev, "failed to create PHY\n");
>> +        return PTR_ERR(generic_phy);
>> +    }
>> +
>> +    phy_set_drvdata(generic_phy, rk_phy);
>> +    phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
>> +
>> +    return PTR_ERR_OR_ZERO(phy_provider);
>> +}
>> +
>> +static const struct of_device_id rockchip_emmc_phy_dt_ids[] = {
>> +    { .compatible = "rockchip,rk3399-emmc-phy" },
>> +    {}
>> +};
>> +
>> +MODULE_DEVICE_TABLE(of, rockchip_emmc_phy_dt_ids);
>> +
>> +static struct platform_driver rockchip_emmc_driver = {
>> +    .probe        = rockchip_emmc_phy_probe,
>> +    .driver        = {
>> +        .name    = "rockchip-emmc-phy",
>> +        .of_match_table = rockchip_emmc_phy_dt_ids,
>> +    },
>> +};
>> +
>> +module_platform_driver(rockchip_emmc_driver);
>> +
>> +MODULE_AUTHOR("Shawn Lin <shawn.lin at rock-chips.com>");
>> +MODULE_DESCRIPTION("Rockchip EMMC PHY driver");
>> +MODULE_LICENSE("GPL v2");
>>
> 
> 


From caesar.upstream at gmail.com  Sun Jan 31 22:32:59 2016
From: caesar.upstream at gmail.com (Caesar Wang)
Date: Mon, 1 Feb 2016 14:32:59 +0800
Subject: [PATCH v4 9/9] ARM: dts: rockchip: support the spi for rk3036
In-Reply-To: <3295129.SttO6ack9z@phil>
References: <1453970618-4383-1-git-send-email-wxt@rock-chips.com>
 <1453970618-4383-10-git-send-email-wxt@rock-chips.com>
 <3295129.SttO6ack9z@phil>
Message-ID: <56AEFC1B.60407@gmail.com>

Heiko,

? 2016?01?31? 19:06, Heiko Stuebner ??:
> Hi Caesar,
>
> Am Donnerstag, 28. Januar 2016, 16:43:38 schrieb Caesar Wang:

[...]

>
>   	memory {
> @@ -485,6 +486,23 @@
>   		status = "disabled";
>   	};
>
> +	spi: spi at 20074000 {
> +		compatible = "rockchip,rockchip-spi";
> +		reg = <0x20074000 0x1000>;
> +		interrupts = <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>;
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&spi_txd &spi_rxd &spi_clk &spi_cs0 &spi_cs1>;
> Do we really want to enable both chip-selects by default?
>
> On the rk3288 Lin Huang wrote:
>      * It's assumed that most users of the SPI ports are using chip select
>        0.  Thus the default pinctrl for the ports enables chip select 0
>        (but not chip select 1 on ports that have it).  If a board wants to
>        use chip select 1 or wants a GPIO chip select the board should
>        override the pinctrl (just like boards can override UART pinctrl if
>        they have hardware flow control).
>
> Do we expect again mostly a use of cs0 or will in the major cases both chip-
> selects be needed?

Sound resonable.
In general, every cs and spi devices should be the one match one.

That should be resonable if we want to use chip select 1 or want a GPIO chip select the board should
override the pinctrl.

>> +		num-cs = <2>;
>> +		clocks =<&cru PCLK_SPI>, <&cru SCLK_SPI>;
>> +		clock-names = "apb-pclk","spi_pclk";
>> +		dmas = <&pdma 8>, <&pdma 9>;
>> +		#dma-cells = <2>;
> What do you need #dma-cells for? This is not a dma-controller :-)

Fixed.

>
>
>> +		dma-names = "tx", "rx";
>> +		status = "disabled";
>> +	};
>
> Also I'd suggest an ordering like:
>
> +	spi: spi at 20074000 {
> +		compatible = "rockchip,rockchip-spi";
> +		reg = <0x20074000 0x1000>;
> +		interrupts = <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>;
> +		clocks =<&cru PCLK_SPI>, <&cru SCLK_SPI>;
> +		clock-names = "apb-pclk","spi_pclk";
> +		dmas = <&pdma 8>, <&pdma 9>;
> +		dma-names = "tx", "rx";
> +		num-cs = <2>;
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&spi_txd &spi_rxd &spi_clk &spi_cs0 &spi_cs1>;
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		status = "disabled";
> +	};

Okay, I will remove the cs1/num-cs in here.

Thanks.

-
Caesar

>
> Heiko
>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip


-- 
Thanks,
Caesar



From caesar.upstream at gmail.com  Sun Jan 31 22:40:57 2016
From: caesar.upstream at gmail.com (Caesar Wang)
Date: Mon, 1 Feb 2016 14:40:57 +0800
Subject: [PATCH v4 1/9] ARM: dts: rockchip: add the leds control for kylin
 board
In-Reply-To: <2785565.JmZWWova7F@phil>
References: <1453970618-4383-1-git-send-email-wxt@rock-chips.com>
 <1453970618-4383-2-git-send-email-wxt@rock-chips.com>
 <2785565.JmZWWova7F@phil>
Message-ID: <56AEFDF9.2030905@gmail.com>



? 2016?01?31? 18:46, Heiko Stuebner ??:
> Am Donnerstag, 28. Januar 2016, 16:43:30 schrieb Caesar Wang:
>> As the kylin schematic drawing, add the needed work led for
>> kylin board.
>>
>> Run:
>> echo 0 > /sys/class/leds/kylin:red:led/brightness
>> echo 1 > /sys/class/leds/kylin:red:led/brightness
>>
>> The led can normal on/off on kylin board.
>>
>> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
> applied to my dts32-branch for 4.6

Okay, thanks.
I'm assuming this patch is applied to your local branch since I have not 
yet see on your branch[0].:-)

branch[0]: 
https://git.kernel.org/cgit/linux/kernel/git/mmind/linux-rockchip.git/log/?h=v4.6-armsoc/dts32



>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip


-- 
Thanks,
Caesar



From caesar.upstream at gmail.com  Sun Jan 31 22:56:54 2016
From: caesar.upstream at gmail.com (Caesar Wang)
Date: Mon, 1 Feb 2016 14:56:54 +0800
Subject: [PATCH v7 0/9] Fix broken DMAFLUSHP on Rockchips platform
In-Reply-To: <1453460812-8498-1-git-send-email-wxt@rock-chips.com>
References: <1453460812-8498-1-git-send-email-wxt@rock-chips.com>
Message-ID: <56AF01B6.6030204@gmail.com>

Hello Vinod,

Is there any chance apply the DMA patches?:-)


-
Caesar

? 2016?01?22? 19:06, Caesar Wang ??:
> This series patches are apply on linus master branch.
> Okay, this series patches verified on my github.
> (https://github.com/Caesar-github/rockchip/commits/for-dma-tests)
>
> Linux version 4.4.0+ (wxt at ubuntu)  (Ubuntu/Linaro 4.7.3-12ubuntu1) ) #14
> SMP Fri Jan 22 18:13:12 CST 2016
>
> The perious version 6 from Shawn.
> (http://www.spinics.net/lists/dmaengine/msg06606.html)
> I pick them up to go on upstream since Shawn hasn't much time to track them.
> ----
>
> The purpose of the DMAFLUSHP instruction:
> - Tell the peripheral to clear its status and control registers.
> - Send a message to the peripheral to resend its level status.
>
> There are 3 timings described in PL330 Technical Reference Manual:
> - Timing 1: Burst request, can work well without DMAFLUSHP.
> - Timing 2: Single and burst request, DMAC will ignore the single
>              transfer request. This timing happens if there are single
>              and burst request.
> - Timing 3: Single transfers for a burst request, DMAC should signals
>              datype to request the peripheral to flush the contents of
>              any control registers. This timing happens if there is
>              not enough MFIFO to places the burst data.
>
> A peripheral may signal a DMA request during the execution of
> DMAFLUSHP instruction, that cause DMA request being ignored by DMAC.
>
> But DMAC and all peripherals on RK3X SoCs DO NOT support DMAFLUSHP.
> It can't send a message to the peripheral to resend DMA request,
> and the peripheral can't acknowledge a flush request from DMAC.
> So all DMA requests should NOT be ignored by DMAC, and DMAC will not
> notify the peripheral to flush.
>
> To fix this problem, we need:
> - Do NOT execute DMAFLUSHP instruction.
> - Timing 2 and timing 3 should not happen.
>
> Because on RK3X SoCs, there are 6 or below  channels and 32 MFIFO depth
> for DMAC_BUS, and 8 channels and 64 MFIFO depth for DMAC_PERI, it is
> impossible to hit the timing 3 if burst length is equal or less than 4.
>
> Since the request type signal by the peripheral can only be set by
> software. We can set Rockchip Soc's GRF_PERIDMAC_CON0[2:1] to select single
> or burst request, if it is set b01,  all of the peripharals will signal a brust
> request. So the timing 2 will not happen, too.
>
> So DMAC on RK3X can support single or burst transfer, but can't support
> mixed transfer.
>
> Because burst transfer is more efficient than single transfer, this is
> confirmed by our ASIC team, who strongly suggest to use burst transfer.
> And this is confirmed by Addy's test on RK3288-Pink2 board, the speed of
> spi flash burst transfer will increase about two times than single transfer.
> Also, I have tested dw_mmc with pl330 on RK3188 platform to double confirm
> the result. That means burst transfer is reansonable.
>
> So we need a quirk not to execute DMAFLUSHP instruction and to use burst
> transfer.
>
> Note:
> - The Rockchip Soc default value of GRF_PERIDMAC_CON0[2:1] is b01. To
>    support brust transfer, these bits should not be changed in bootloader.
>
> Todo:
> remove the patches releated to i2s, that will send them at later.
> - https://patchwork.kernel.org/patch/7399581/
> - https://patchwork.kernel.org/patch/7399671/
>
>
> Changes in v7:
> - As vinod comments, fix the subsystem name.
> - modify the subject to be better match.
> - fix the subsystem.
> - fix the subject.
> - fix the subject.
>
> Changes in v6:
> - remove expose quirk and add dma max_burst caps for clients
> - remove expose quirks and add max_burst for dmaengine
> - remove quirks and get dma caps in order to limit burst
>
> Changes in v5:
> - add Mark's tag for spi changes
> - remove unnecessary whitespace change
> - use switch statement for i2s quirk
>
> Changes in v4:
> - remove spi & i2s dts changes and query quirk from dmaengine API
>    suggeseted by Mark.
> - fix typo
> - Add dmaengine_get_quirk hook and implement it for pl330
>
> Changes in v3:
> - add Sunny's tag
> - add more rockchip drivers' changes in this patchset
> - add Reviewed-by: Sonny Rao <sonnyrao at chromium.org>
> - add Reviewed-by: Sonny Rao <sonnyrao at chromium.org>
> - add Reviewed-by: Sonny Rao <sonnyrao at chromium.org>
>
> Changes in v2:
> - amend the author
> - reorder the patches suggested by Doug
> - add Reviewed-by: Doug Anderson <dianders at chromium.org> for
>    rk3288.dtsi patch and arm-pl330.txt patch
> - add Reviewed-by: Doug Anderson <dianders at chromium.org>
> - amend the author
> - fix Olof's mail address
> - amend the author
> - add Reviewed-by: Doug Anderson <dianders at chromium.org>
> - amend Olof's mail address
>
> Changes in v1:
> - rename broken-no-flushp to "arm,pl330-broken-no-flushp" suggested
>    by Krzysztof.
> - add From original author.
> - remove Sunny's tag
> - rename broken-no-flushp to "arm,pl330-broken-no-flushp" suggested
>    by Krzysztof.
> - rename broken-no-flushp to "arm,pl330-broken-no-flushp" suggested
>    by Krzysztof.
> - remove Sunny's tag
> - rename broken-no-flushp to "arm,pl330-broken-no-flushp" suggested
>    by Krzysztof.
>    - remove Sunny's tag
> - rename broken-no-flushp to "arm,pl330-broken-no-flushp" suggested
>    by Krzysztof.
>
> Addy Ke (3):
>    dmaengine: pl330: add quirk for broken no flushp
>    ARM: dts: Add arm, pl330-broken-no-flushp quirk for rk3288 platform
>    spi: rockchip: modify DMA max burst to 1
>
> Boojin Kim (1):
>    dmaengine: pl330: support burst mode for dev-to-mem and mem-to-dev
>      transmit
>
> Caesar Wang (1):
>    ARM: dts: add pl330-broken-no-flushp quirk for rk3036 SoCs
>
> Shawn Lin (4):
>    dt/bindings: arm-pl330: add description of arm, pl330-broken-no-flushp
>    ARM: dts: Add arm, pl330-broken-no-flushp quirk for rk3xxx platform
>    dmaengine: core: expose max burst capability to clients
>    dmaengine: pl330: add max burst for dmaengine
>
>   .../devicetree/bindings/dma/arm-pl330.txt          |   1 +
>   arch/arm/boot/dts/rk3036.dtsi                      |   1 +
>   arch/arm/boot/dts/rk3288.dtsi                      |   3 +
>   arch/arm/boot/dts/rk3xxx.dtsi                      |   3 +
>   drivers/dma/dmaengine.c                            |   1 +
>   drivers/dma/pl330.c                                | 106 +++++++++++++++------
>   drivers/spi/spi-rockchip.c                         |  12 ++-
>   include/linux/dmaengine.h                          |   4 +
>   8 files changed, 99 insertions(+), 32 deletions(-)
>


-- 
Thanks,
Caesar



From wxt at rock-chips.com  Sun Jan 31 23:30:07 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Mon,  1 Feb 2016 15:30:07 +0800
Subject: [PATCH v4.1 9/9] ARM: dts: rockchip: support the spi for rk3036
In-Reply-To: <1453970618-4383-1-git-send-email-wxt@rock-chips.com>
References: <1453970618-4383-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1454311807-5359-1-git-send-email-wxt@rock-chips.com>

This patch adds the needed spi node for rk3036 dts.

We have to use the 4 bus emmc to work if someone want to support
the spi devices, since the pins are re-used by emmc data[5-8] and spi.
In some caseswe need to support the spi devices, that will waste the
emmc performance.

Moment, the kylin/evb hasn't the spi devices to work, so maybe we need wait
the new required to enable in kylin/evb board.

Anyway, the spi should be needed land in rk3036 dts.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>

---

Changes in v4-->v4.1:
- Remove the unused, as Heiko comments on https://patchwork.kernel.org/patch/8147761/
- Modify the commit.

Changes in v4:
- Add this patch included in kylin series patches.

 arch/arm/boot/dts/rk3036.dtsi | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
index 532f232..e62e28b 100644
--- a/arch/arm/boot/dts/rk3036.dtsi
+++ b/arch/arm/boot/dts/rk3036.dtsi
@@ -60,6 +60,7 @@
 		serial0 = &uart0;
 		serial1 = &uart1;
 		serial2 = &uart2;
+		spi = &spi;
 	};
 
 	memory {
@@ -485,6 +486,21 @@
 		status = "disabled";
 	};
 
+	spi: spi at 20074000 {
+		compatible = "rockchip,rockchip-spi";
+		reg = <0x20074000 0x1000>;
+		interrupts = <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>;
+		clocks =<&cru PCLK_SPI>, <&cru SCLK_SPI>;
+		clock-names = "apb-pclk","spi_pclk";
+		dmas = <&pdma 8>, <&pdma 9>;
+		dma-names = "tx", "rx";
+		pinctrl-names = "default";
+		pinctrl-0 = <&spi_txd &spi_rxd &spi_clk &spi_cs0>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		status = "disabled";
+	};
+
 	pinctrl: pinctrl {
 		compatible = "rockchip,rk3036-pinctrl";
 		rockchip,grf = <&grf>;
@@ -723,5 +739,29 @@
 			};
 			/* no rts / cts for uart2 */
 		};
+
+		spi {
+			spi_txd:spi-txd {
+				rockchip,pins = <1 29 RK_FUNC_3 &pcfg_pull_default>;
+			};
+
+			spi_rxd:spi-rxd {
+				rockchip,pins = <1 28 RK_FUNC_3 &pcfg_pull_default>;
+			};
+
+			spi_clk:spi-clk {
+				rockchip,pins = <2 0 RK_FUNC_2 &pcfg_pull_default>;
+			};
+
+			spi_cs0:spi-cs0 {
+				rockchip,pins = <1 30 RK_FUNC_3 &pcfg_pull_default>;
+
+			};
+
+			spi_cs1:spi-cs1 {
+				rockchip,pins = <1 31 RK_FUNC_3 &pcfg_pull_default>;
+
+			};
+		};
 	};
 };
-- 
1.9.1



